かとじゅんの技術日誌

技術の話をするところ

今さらデザインパターン FactoryMethodパターン編

みなさま、スターをいただきありがとうございます!
次は、FactoryMethodパターンです。以外とシンプルなのでサクッと説明します。

まず、ConverterFactoryを以下のようにinterfaceに書き換えます。

public interface ConverterFactory {
	public Converter create();
}

さらに以下のような抽象クラスを準備します。createメソッドは引数を持たなくなり、肝心のgetComponentの処理もなくなりました。代わりにabstractなcreateConverterメソッドが定義されています。

import org.seasar.framework.container.S2Container;

public abstract class AbstractConverterFactory implements ConverterFactory {

	protected S2Container s2Container;

	public void setS2Container(S2Container container) {
		s2Container = container;
	}

	public Converter create(){
		return this.createConverter();
	}

	protected absract Converter createConverter();

}

AbstractConverterFactoryの派生クラスとして、MySQLConverterFactoryとMySQLConverterFactoryを作成します。加えて、それぞれのFactoryをdiconファイル上に定義してください。
各クラスでcreateConverterを実装します。ここではインスタンス管理がprototype前提のコンポーネントDIコンテナから生成・取得します。(多くのFactoryMethdoのサンプルではcreateに当たる部分が生成したオブジェクトを操作するanOperationメソッドとして紹介されていますが、Factoryは生成したオブジェクトを呼び出し側に返却するのが目的なので今回は上記のようにcreateConverterメソッドで生成したConverterをそのまま返します)

public class MySQLConverterFactory extends AbstractConverterFactory {

	@Override
	protected Converter createConverter(){
		return (Converter) this.s2Container.getComponent("mySQLConverter");
	}

}
public class MySQLConverterFactory extends AbstractConverterFactory {

	@Override
	protected Converter createConverter(){
		return (Converter) this.s2Container.getComponent("postgreSQLConverter");
	}

}

前回のFactoryパターンのBusinessLogicでは、ひとつのBusinessLogicで複数のコンバート処理をサポートするようなI/F仕様になっていました。
ですので、上記のFactoryを二種類使いわけないといけません。なので、以下のような構成になってしまいます。なにやら、前回のものよりシンプルさにかけますねwwこのような場合はSimpleFactoryのままのほうがシンプルでよいですね。

public class BusinessLogic {
	
	@Binding("mySQLConverterFactory")
	public ConverterFactory mySQLConverterFactory;

	@Binding("postgreSQLConverterFactory")
	public ConverterFactory postgreSQLConverterFactory;

	public void doBusiness(String conveterMode, Table table) {
		Converter converter = null;
		if ("MYSQL".equals(mode)) {
			converter = mySQLConverterFactory.create();
		} else if ("POSTGRESQL".equals(mode)) {
			converter = postgreSQLConverterFactory.create();
		}
		String sql = converter.convert(table);
		System.out.println(sql);
	}
}

もともとの仕様どおり、BusinessLogicは各DBMSごとに定義されるのであれば、同時にConverterをDIする必要もありませんし、doBusiness内でif文を使って分岐する必要もないと思います。
でも、よくよく考えると、この場合だと生成されるConverterは、ConvereterFactoryが決まれば必然的に決まります。要するにdoBusinessにはMySQL/PostgreSQLの切り替えを要求する引数が存在しないため、もともとの仕様どおりConvereterFactoryを使わず、ConverterをそのままDIしたほうがシンプルですねwww

public class BusinessLogic {
	
	public ConverterFactory converterFactory;

	public void doBusiness(Table table) {
		Converter converter = converterFactory.create();
		String sql = converter.convert(table);
		System.out.println(sql);
	}
}

まとめ

DIコンテナをベースにすると、FactoryMethodの使いどころに困りそうですねw
大抵の場合はSimpleFactoryで事足りる気がしています。以上、ご参考までに。ていうか、参考にならなかったかも。。。