デザインパターンFactoryMethodについて勉強した

Factory Methodパターンとは

factoryは工場のことです。
インスタンスを次々作り出す工場のイメージでしょうか。

以前にTemplate Methodパターンについて説明しましたが、それのコンストラクタ版といったところです。
chiopino.hatenablog.com
メインプログラムではインスタンスを直接呼び出さないので、型に柔軟なプログラムを書くことができます。

パターンの例

新入社員に社員証を発行し、出勤、退勤を管理するシステムを作ります。
例ではフレームワークとなる2つの抽象クラス、それを実装した2クラス、テスト用の1クラスを作ります。

Productインタフェース

public interface Product {

	void on();

	void off();
}

社員証の元となるインタフェースです。
二つのメソッドが宣言されていますが、これらは「何らかの機能を実装する」ためのメソッドです。
社員証なら「出勤」「退勤」を実装できますし、
pcなら「電源オン」「電源オフ」を実装できます。

このon,offメソッドはFactoryMethodパターンからすると必要のないものです。
極端に言ってしまえば製品を意味するProductインタフェースさえあれば、メソッドは何もなくてもFactoryMethodを名乗れます。
ただし実用性があるかは別の話です。


Factoryクラス

public abstract class Factory {

	public abstract Product createProduct(String name) ;
}

createProductメソッドはProduct(製品)を生産するための抽象メソッドです。


実装例

これら2つのクラス、インタフェースを実装して、実際の社員証と管理システムを作っていきます。


EmployeelIDcardクラス

public class EmployeelIDcard implements Product {

	private String name ;

	public EmployeelIDcard(String name) {
		this.name = name;
		System.out.println(name + "が入社しました");
	}

	public void on() {
		System.out.println(name + "が出勤しました");
	}

	public void off() {
		System.out.println(name + "が退勤しました");
	}
}

コンストラクタでは持ち主の名前を社員証に登録しています。
登録が完了したら新入社員の名前と、入社したことをアナウンスします。

onメソッドはProductインタフェースで宣言したメソッドです。
ここでは出勤という意味を持たせて実装しました。
本来であればAdapterパターンでメソッド名をarrival(出社)のように変えたいところですが、FactoryMethodパターンの説明には不要であるためそのままonメソッドとします。

offメソッドはonメソッドと同様の実装となっています。


EmployeelIDSystemクラス

public class EmployeelIDSystem extends Factory {

	@Override
	public Product createProduct(String name) {
		return new EmployeelIDcard(name);
	}
}

社員管理システムです。
管理システムとは言ってもここでは社員証の発行しかしていません。

createProductメソッドはFactoryクラスで宣言されていた抽象メソッドでした。
EmployeelIDcardクラスのインスタンスを生成し、それを返すだけのメソッドです。


テスト用クラス

public class Main {

	public Main() {
		Factory factory = new EmployeelIDSystem();
		Product a_san = factory.createProduct("安藤");
		Product b_san = factory.createProduct("馬場");
		Product c_san = factory.createProduct("千代田");
		Product d_san = factory.createProduct("大和");

		System.out.println("----------------");
		a_san.on();
		b_san.on();
		c_san.on();
		d_san.on();
		System.out.println("----------------");
		a_san.off();
		b_san.off();
		c_san.off();
		d_san.off();
	}

	public static void main(String[] args) {
		new Main();
	}
}

4人の新入社員にそれぞれの社員証を発行しています。
createProductメソッドは、渡された名前でProductクラスのスーパークラスインスタンスを生成して返すものでした。
変数a_san,b_san,c_san,d_sanにはそれぞれの社員証が保持されました。

それぞれの社員証をon,offメソッドを呼び出しています。
onメソッドは出勤、offメソッドは退勤でした。

実行してみます。

安藤が入社しました
馬場が入社しました
千代田が入社しました
大和が入社しました
----------------
安藤が出勤しました
馬場が出勤しました
千代田が出勤しました
大和が出勤しました
----------------
安藤が退勤しました
馬場が退勤しました
千代田が退勤しました
大和が退勤しました

使用する利点

createProductの実装で、社員証を全てコレクションに追加することで全社員を管理することができたりします。
コンストラクタ呼び出しとセットにしたい処理がある場合に力を発揮するパターンです。

また、createProductで呼び出しているコンストラクタをGuestIDcardなどに差し替えることで、簡単に来客用IDカード用のシステムに変更したりもできます。

まとめ

FactoryクラスやProductインタフェースがFactoryMethodパターンの本体であり、EmployeelIDcardクラスやEmployeelIDcardSystemクラスはあくまで実装例に過ぎません。

FactoryクラスやProductインタフェースだけ見ると、社員証システムのプログラムには到底見えません。
なぜなら社員証システム以外でも十分使用できるように設計しているからです。
テレビやスマホ、車などでも実装次第です。

デザインパターンはプログラムの部品化、そのやり方であることを忘れないようにしましょう。