Abstract Factory パターンの定義
具象クラスを指定することなく、一連の関連オブジェクトや依存オブジェクトを作成するためのインターフェースを提供する。
例えば
ある製品を作るのに、関東では以下の部品を組み合わせて使用する。
- ProsuctA1
- ProsuctB1
しかし関西では以下の部品を組み合わせて使用する。
- ProsuctA2
- ProsuctB2
この組み合わせは、地域毎で変わる可能性があるため、組み合わせを柔軟に設定できるようにしておきたい。
こんな時は Abstract Factory パターンの出番。
クラス構成
ファクトリ関連
パッケージ | クラス | 説明 |
---|---|---|
factory | Factory.java | ファクトリーinterface ProductAとProductBのインスタンス化メソッドを定義 |
factory.impl | FactoryImpl1.java | ファクトリーinterface の具象クラス。Productの組み合わせをカプセル化している。例えば関東版のファクトリー |
factory.imp | FactoryImpl2.java | ファクトリーinterface の具象クラス。Productの組み合わせをカプセル化している。例えば関西版のファクトリー |
public interface Factory { ProductA createProductA(); ProductB createProductB(); }
末尾が1のProductをインスタンス化
public class FactoryImpl1 implements Factory { @Override public ProductA createProductA() { return new ProductImplA1(); } @Override public ProductB createProductB() { return new ProductImplB1(); } }
末尾が2のProductをインスタンス化
public class FactoryImpl2 implements Factory { @Override public ProductA createProductA() { return new ProductImplA2(); } @Override public ProductB createProductB() { return new ProductImplB2(); } }
プロダクト関連
パッケージ | クラス | 説明 |
---|---|---|
product | ProductA.java | 部品用のinterface。部品A |
product | ProductB.java | 部品用のinterface。部品B |
product.Impl | ProductImplA1.java | 部品用のinterfaceの実装クラス。部品A1 |
product.Impl | ProductImplA2.java | 部品用のinterfaceの実装クラス。部品A2 |
product.Impl | ProductImplB1.java | 部品用のinterfaceの実装クラス。部品B1 |
product.Impl | ProductImplB2.java | 部品用のinterfaceの実装クラス。部品B2 |
public interface ProductA { String getName(); }
public interface ProductB { String getId(); }
public class ProductImplA1 implements ProductA { @Override public String getName() { return "ProsuctA1"; } }
public class ProductImplA2 implements ProductA { @Override public String getName() { return "ProsuctA2"; } }
public class ProductImplB1 implements ProductB { @Override public String getId() { return "B1"; } }
public class ProductImplB2 implements ProductB { @Override public String getId() { return "B2"; } }
その他
パッケージ | クラス | 説明 |
---|---|---|
- | Client.java | 部品を組み立てる主体となるクラス |
- | UnitTest.java | 動作確認用クラス |
外部から与えられるファクトリからインスタンスを取得し、処理を行う。
public class Client { private Factory factory; public Client(Factory factory) { this.factory = factory; } public void execute() { ProductA prosuctA = factory.createProductA(); ProductB prosuctB = factory.createProductB(); System.out.println(prosuctA.getName() + ":" + prosuctB.getId()); } }
以下はテストメソッド
@Test public void tear1() throws Exception { Factory factory = selectFactory(1); Client client = new Client(factory); client.execute(); } @Test public void tear2() throws Exception { Factory factory = selectFactory(2); Client client = new Client(factory); client.execute(); } private Factory selectFactory(int key) { switch (key) { case 1: return new FactoryImpl1(); case 2: return new FactoryImpl2(); default: throw new RuntimeException("no target"); } }
実行結果
ProsuctA1:B1 ProsuctA2:B2
それぞれProsuctのセットがファクトリで指定した通りに出力されている。Abstract Factoryにしておくと以下の恩恵が得られる。
- A1とB2の組み合わせを増やしたい場合、ファクトリを追加するのみで良い(UnitTestの修正は必要だが)
- A1をC1に変更したい場合、ProsuctC1を追加して、ファクトリのFactoryImpl1を変更するのみで済む
なんでいまさら
なんでいまさらこんな事を書いてるかというと、デザインパターンの説明って、だらだら文章書くよりシンプルなコード自体を見る方が分かりやすいのではないかと思ったから・