みなさんは「工場」と言われたときに何の工場を想像するでしょうか?
車に、缶詰、飲み物などいろいろな工場があります。
今回紹介するFactoryMethodパターンは、「オブジェクトを生成する方法」に一工夫を加えるデザインパターンです。
と、言ってもなかなかうまく伝わりませんね。
実際に行っている操作に比べて言葉にすると難しいように感じられますが、
FactoryMethodパターンを使うことで「パッケージに依存しないプログラミング」が可能となります。
Contents
FactoryMethodパターンの実装
言葉で概要を説明するよりも先に実装を確認してもらった方が分かりやすいかもしれません。
ここでのサンプルプログラムは「車」オブジェクトを作る工場を考えてみましょう。
Factoryクラス(抽象クラス)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
package com.xoxopigs.framework; // 工場のテンプレート(Templateパターン) public abstract class Factory { public final Product create(String owner) { Product p = createProduct(owner); registerProduct(p); return p; } // !!! FactoryMethod !!! protected abstract Product createProduct(String owner); protected abstract void registerProduct(Product product); } |
FactoryクラスではTemplateパターンを使って作成しています。
createメソッドでは、製品を作り(createProduct)、その製品を登録し(registerProduct)し、出荷(return)します。
製造方法や、登録の方法などはFactoryクラスのサブクラスに委任しています。
Productクラス(抽象クラス)
1 2 3 4 5 6 |
package com.xoxopigs.framework; // 製品はどんな形であれ使用する(use)することができる。 public abstract class Product { public abstract void use(); } |
Factoryクラスで作成される製品はどんな形であれ使用することができるはずです。
今回の車であれば使用 = 運転すると考えてもいいでしょうか。
CarFactoryクラス
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
package com.xoxopigs.car; import java.util.*; import com.xoxopigs.framework.*; // 車製造工場 public class CarFactory extends Factory { private List owners = new ArrayList(); // !!! FactoryMethod !!! @Override protected Product createProduct(String owner) { return new Car(owner); } @Override protected void registerProduct(Product product) { owners.add(((Car)product).getOwner()); } public List getOwners() { return owners; } } |
Factoryクラス(工場のテンプレート)を継承し、車の製造”工場”を作りました。
createProductではCarオブジェクトを作成し、registerProductでは車を製造したオーナー( = メーカー)を登録します。
CarFactoryクラスはパッケージが異なることに注意してください。
Carクラス
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
package com.xoxopigs.car; import com.xoxopigs.framework.Product; public class Car extends Product { private String owner; Car(String owner) { System.out.println(owner + "の車を製造"); this.owner = owner; } @Override public void use() { System.out.println(owner + "の車を走らせる"); } public String getOwner() { return owner; } } |
Product(製品)クラスを継承した、車クラスです。
製造された際に、「”メーカー”の車を作成」とコンソールに表示し、実際に使用する(use)する場合に、「”メーカー”の車を走らせる」と表示するだけの簡単なコードです。
Mainクラス
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
package com.xoxopigs; import com.xoxopigs.framework.*; import com.xoxopigs.car.CarFactory; public class Main { public static void main(String[] args) { Factory factory = new CarFactory(); Product product1 = factory.create("honda"); Product product2 = factory.create("suzuki"); Product product3 = factory.create("daihatsu"); product1.use(); product2.use(); product3.use(); } } |
FactoryMethodパターンを確認するためだけのサンプルプログラムです。
FactoryMethodパターンの確認
一度動かしてみていただければ、まぁそうなるだろうな。程度の感想かもしれません。
ここで重要なのはFactoryクラスはどんな工場なのか全くわからないことです。
(com.xoxopigs.carパッケージのインポートもされない。)
車であろうが、野菜であろうが、缶ジュースであろうが、Factoryクラスは関知していないのです。
これはFactoryMethodパターンを使ってオブジェクトの生成に一工夫し、抽象メソッドとしたことに由来します。
まとめ
うーん、これはあまりうまく伝えられていない気がします。
一度サンプルコードを実行すると、「あー、なるほど。そういうことか。」とイメージはつかめると思うので、
実際に打ち込んで動作を確認して、改造してみてください。
今回のサンプルコードではFactoryMethodを抽象メソッドとしましたが、その他にも
- 実装されていない場合にエラーが出るように定義する。
- デフォルトの実装を定義し、オーバーライドで対応する。
といった方法も考えられます。
またうまく伝えられるように色々試してみて何か補足があればまた記事にしますね!