no-image

Dartでファクトリパターン/シングルトンパターンを実装する

ファクトリパターン(Factory Pattern)シングルトンパターン(Singleton Pattern)はオブジェクト指向プログラミングにおいて欠かせないデザインパターンです。Dartにおいても、もちろんこのようなデザインパターンは伝統的な方法で実装することができます。

main() {
  var instance1 = MyClassFactory.createInstance("foo");
  var instance2 = MyClassFactory.createInstance("foo");

  print(identical(instance1, instance2)); // true
}

// ファクトリクラスで生成したいクラス。
class MyClass {
  final String name;
  MyClass(this.name);
}

// ファクトリクラス。オブジェクトの生成を担当する。
// 一度生成したものはキャッシュする。
class MyClassFactory {
  static Map<String, MyClass> _cache = new Map<String, MyClass>();

  static MyClass createInstance(String name) {
    if (!_cache.containsKey(name)) _cache[name] = new MyClass(name);
    return _cache[name];
  }
}

これでも期待した動作を得ることはできます。しかし、Dartではファクトリパターンのための、よりよい仕組みが用意されています。この記事では、その仕組みを使ったファクトリパターン/シングルトンパターンの実装方法について説明します。

factoryコンストラクタとファクトリパターン

Dartには通常のコンストラクタ(ジェネレーティブコンストラクタ)の他にも、factoryコンストラクタというものがあります。ジェネレーティブコンストラクタはそのクラスのインスタンスを自動的に生成しますが、factoryコンストラクタはインスタンスを生成しません。その代わりに、手動でインスタンスを生成して、明示的にreturnする必要があります。その名の通り、ファクトリパターンに向いたコンストラクタになっているのです。

factoryコンストラクタを使用するには、コンストラクタの前にfactory修飾子を付けるだけです。これでそのコンストラクタはfactoryコンストラクタになります。

先程の例を、factoryコンストラクタで書き直したものを見てみましょう。

main() {
  // factoryコンストラクタは通常のコンストラクタのように
  // newキーワードで呼び出すことができる。
  var instance1 = new MyClass("foo");
  var instance2 = new MyClass("foo");

  print(identical(instance1, instance2)); // true
}

// factoryコンストラクタを持つクラス。
class MyClass {
  static Map<String, MyClass> _cache = new Map<String, MyClass>();
  final String name;

  // インスタンスを生成するfactoryコンストラクタ。
  // インスタンスをキャッシュする。
  factory MyClass(String name) {
    if (!_cache.containsKey(name)) _cache[name] = new MyClass._internal(name);
    return _cache[name]; // 明示的にインスタンスをreturnする。
  }

  // factoryコンストラクタからインスタンスを生成するための、
  // プライベートなコンストラクタ。
  MyClass._internal(this.name);
}

factoryコンストラクタを使って書き換えることができました。factoryコンストラクタはジェネレーティブコンストラクタと同じくnewキーワードを使って呼び出せるので、より自然な記述をすることができます。また、生成専用のファクトリクラスを作る必要がなくなりました。クラスそのものがファクトリの機能を持つことができるのです。

factoryコンストラクタとシングルトンパターン

factoryコンストラクタは、ファクトリパターンだけではなくシングルトンパターンについても有用です。

main() {
  var instance1 = new Singleton();
  var instance2 = new Singleton();

  print(identical(instance1, instance2)); // true
}

// シングルトンクラス。
class Singleton {
  static Singleton _instance; // インスタンスのキャッシュ

  // 初めて呼び出されたときはインスタンスを生成してキャッシュし、
  // それ以降はキャッシュを返すfactoryコンストラクタ。
  factory Singleton() {
    if (_instance == null) _instance = new Singleton._internal();
    return _instance;
  }

  // 内部から呼び出してインスタンスを作るための
  // プライベートなコンストラクタ。
  Singleton._internal();
}

ファクトリパターンと同様にしてシングルトンパターンを実装することができました。