CFMLでデザインパターン実装(1.AbstractFactory)

最近、Coldfusion8の評価をしようとあれこれ調べておりますが、CFでどこまでGoFデザインパターンが使えるのか気になりました。Javaと違って動的型付けっぽいので適用させる意味があまり無いパターンもあるのは間違いないと思うのですが、AbstractFactoryを実装してみます。

まず、基本となる機能をJavaで実装したもの。

Java-1:呼び元(TestCaseをそのままmain扱い)

public void test() {
	try {
		String returnValue = null;
		returnValue = this.getSaid(AbstractFactory.HELLO_WORLD);
		System.out.println(returnValue);
		returnValue = this.getSaid(AbstractFactory.GOOD_BY_WORLD);
		System.out.println(returnValue);
	} catch (Exception e) {
		e.printStackTrace();
		fail();
	}
}
private String getSaid(String key) {
	AbstractFactory factory = AbstractFactory.getFactory(key);;
	HelloWorld hello = factory.createHelloWorld();
	return hello.say();
}

Java-2:Abstract Factory

public abstract class AbstractFactory {
	public static final String HELLO_WORLD = "hello";
	public static final String GOOD_BY_WORLD = "good-by";
	public static AbstractFactory getFactory(String key) {
		AbstractFactory factory = null;
		if (key.equals(HELLO_WORLD)) {
			factory = new HelloWorldFactory(); 
		} else if (key.equals(GOOD_BY_WORLD)) {
			factory = new GoodByWorldFactory();
		}
		return factory;
	}
	abstract public HelloWorld createHelloWorld();

Java-3:Concrete Factory

public class HelloWorldFactory extends AbstractFactory {
	@Override
	public HelloWorld createHelloWorld() {
		return new HelloWorld();
	}
}

Java-4:Factoryまで使って生成したいHelloWorld。

public class HelloWorld {
	private String word;

	public HelloWorld() {
		this.word = "Hello, World.";
	}

	public String say() {
		return this.getWord();
	}

	public String getWord() {
		return word;
	}

	public void setWord(String word) {
		this.word = word;
	}
}

Java-5:上記の(1)を実行すれば、以下がコンソールに出力される。

Hello, World.
Good-By, World.

※いちおうGoodByWorldというクラスも作っていた為、余分な出力がありました。
(考えずに貼り付けた私が一番悪いです)

このJavaコードをまんまCFMLに展開。

cf-1:呼び元(ブラウザで見るしかないのでCFMで実装)

<html>
	<head>
		<title>Gof design pattern@CFML</title>
	</head>
	<body>
		<cfoutput>
			<cftry>
				<hr/>
				<h5>Hello, world.</h5>
				<!---// Get factory instance. //--->
				<cfinvoke method="getFactory"
							component="cftest.gof.abstract_factory.AbstractFactory"
								returnvariable="Variables.ret">
					<cfinvokeargument name="key" value="hello"/>
				</cfinvoke>
				<!---// Get HelloWorld. //--->
				<cfset Variables.obj=Variables.ret.createHelloWorld()/>
				<cfinvoke method="say" component="#Variables.obj#" returnvariable="Variables.returnValue"/>
				Return value of getter:[#Variables.returnValue#])<br/>
				Direct access:[#Variables.obj.word#]
				<hr/>
				<!---// Get factory instance. //--->
				<cfinvoke method="getFactory"
							component="cftest.gof.abstract_factory.AbstractFactory"
								returnvariable="Variables.ret">
					<cfinvokeargument name="key" value="good-by"/>
				</cfinvoke>
				<!---// Get HelloWorld. //--->
				<cfset Variables.obj=Variables.ret.createHelloWorld()/>
				<cfinvoke method="say" component="#Variables.obj#" returnvariable="Variables.returnValue"/>
				Return value of getter:[#Variables.returnValue#])<br/>
				Direct access:[#Variables.obj.word#]
				<cfcatch>
					<cfdump label="#cfcatch.Message#" var="#cfcatch#"/>
				</cfcatch>
			</cftry>
		</cfoutput>
	</body>
</html>

cf-2:Abstract Factory

<cfcomponent output="false">
	<cffunction name="getFactory" access="public" returntype="cftest.gof.abstract_factory.AbstractFactory">
		<cfargument name="key" type="string" required="true"/>
		<cfset var factory=""/>
		<cfif Arguments.key is "hello">
			<cfobject name="factory" component="cftest.gof.abstract_factory.HelloWorldFactory"/>
		<cfelseif Arguments.key is "good-by">
			<cfobject name="factory" component="cftest.gof.abstract_factory.GoodByWorldFactory"/>
		</cfif>
		<cfreturn factory/>
	</cffunction>
	<cffunction name="createHelloWorld" access="public" returntype="cftest.gof.common.HelloWorld">
		<!---// Interface defined. //--->
		<cfreturn/>
	</cffunction>
</cfcomponent>

cf-3:Concrete Factory

<cfcomponent output="false" extends="cftest.gof.abstract_factory.AbstractFactory">
	<!---// @Override //--->
	<cffunction name="createHelloWorld" access="public" returntype="cftest.gof.common.HelloWorld">
		<!---// Interface defined. //--->
		<cfset var obj=""/>
		<cfobject name="obj" component="cftest.gof.common.HelloWorld"/>
		<cfinvoke method="HelloWorld" component="#obj#"/>
		<cfreturn obj/>
	</cffunction>
</cfcomponent>

cf-4:Factoryまで使って生成し(ry

<cfcomponent output="false">
	<!---// コンストラクタ風 //--->
	<cffunction name="HelloWorld" access="public" returntype="void">
		<cfinvoke method="setWord">
			<cfinvokeargument name="word" value="Hello, World."/>
		</cfinvoke>
	</cffunction>

	<cffunction name="say" access="public" output="false" returntype="string">
		<cfreturn This.getWord()/>
	</cffunction>

	<cffunction name="setWord" access="public" output="false" returntype="void">
		<cfargument name="word" type="string" required="true"/>
		<cfset This.word=Arguments.word/>
	</cffunction>

	<cffunction name="getWord" access="public" output="false" returntype="string">
		<cfreturn This.word/>
	</cffunction>
</cfcomponent>

cf-5:上記の(1)を実行すれば、以下がブラウザに出力される。

Hello, world.
Return value of getter:[Hello, World.])
Direct access:[Hello, World.]

                  • -

Return value of getter:[Good-by, World.])
Direct access:[Good-by, World.]