2015年6月6日土曜日

1-3. Factory Method パターン

オブジェクトを生成するインターフェースを定義するが、どのクラスをインスタンス化するかは
サブクラスが決定します。



Creator 抽象クラスで Product の生成を行う factory method インターフェースを用意し、
ConcreteCreator 派生クラスで生成を実装します。
これにより、ConcreteFactory を差し替えるだけで別の Product を得ることができます。

一般的に関連するインスタンス群を生成するには、直接クラス名を指定して new します。
ConcreteProduct product = new ConcreteProduct();
Factory Method パターンでは、Creator 抽象クラスでこれらを行うインターフェースを
用意し、ConcreteCreator 派生クラスで実装します。
public abstract class Creator {
        protected abstract AbstractProduct factoryMethod();
        public void anOperation() {
            AbstractProduct product = this.factoryMethod();
            doSomething(product);
        }
    }
    public class ConcreteCreator extends Creator {
        protected AbstractProduct factoryMethod() {
            return new ConcreteProduct();
        }
    }
上記の例では Creator#anOperation(Template Method パターンの template method) 内で
ConcreteFactory#factoryMethod(factory method) で生成された Product に対して操作を
行っています。
そのほかにも、factory method を public にして、外部から呼び出す方法もあります。

サンプルプログラムは、入力されたテキストを解析し、XML と Json 形式で出力する例です。
出力結果:
<PC><CPU><name>Core i7</name><clock>3GHz</clock></CPU><Memory><name>DDR3</name><capacity>4GB</capacity></Memory></PC>
    {PC:{CPU:{name:Core i7},{clock:3GHz}},{Memory:{name:DDR3},{capacity:4GB}}}
package design_pattern;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintStream;
import java.io.Reader;
import java.io.StringReader;
import java.util.LinkedList;
import java.util.List;

/**
 * Factory Method
 * GoF, Code Complete
 * オブジェクトを生成するインターフェースを定義するが、どのクラスをインスタンス化するかは
 * サブクラスが決定します。
 * 
 * Creator 抽象クラスで Product の生成を行う factory method インターフェースを用意し、
 * ConcreteCreator 派生クラスで生成を実装します。
 * これにより、ConcreteFactory を差し替えるだけで別の Product を得ることができます。
 * 
 * 一般的に関連するインスタンス群を生成するには、直接クラス名を指定して new します。
 * <pre>
 *     ConcreteProduct product = new ConcreteProduct();
 * </pre>
 * Factory Method パターンでは、Creator 抽象クラスでこれらを行うインターフェースを
 * 用意し、ConcreteCreator 派生クラスで実装します。
 * <pre>
 *     public abstract class Creator {
 *         protected abstract AbstractProduct factoryMethod();
 *         public void anOperation() {
 *             AbstractProduct product = this.factoryMethod();
 *             doSomething(product);
 *         }
 *     }
 *     public class ConcreteCreator extends Creator {
 *         protected AbstractProduct factoryMethod() {
 *             return new ConcreteProduct();
 *         }
 *     }
 * </pre>
 * 上記の例では Creator#anOperation(Template Method パターンの template method) 内で
 * ConcreteFactory#factoryMethod(factory method) で生成された Product に対して操作を
 * 行っています。
 * そのほかにも、factory method を public にして、外部から呼び出す方法もあります。
 * 
 * サンプルプログラムは、入力されたテキストを解析し、XML と Json 形式で出力する例です。
 * 出力結果:
 * <pre>
 *     <PC><CPU><name>Core i7</name><clock>3GHz</clock></CPU><Memory><name>DDR3</name><capacity>4GB</capacity></Memory></PC>
 *     {PC:{CPU:{name:Core i7},{clock:3GHz}},{Memory:{name:DDR3},{capacity:4GB}}}
 * </pre>
 * 
 * @author 2015/05/23 matsushima
 */
public class FactoryMethodSample {
	/**
	 * main
	 * 
	 * @param args
	 */
	public static void main(String[] args) {
		for (String type: args.length >= 1 ? args : new String[]{"xml", "json"}) {
			new FactoryMethodSample().client(type);
		}
	}
	/**
	 * Client
	 */
	void client(String type) {
		String text =
				"PC:\n"
				+ " CPU:\n"
				+ "  name:\n"
				+ "   Core i7\n"
				+ "  clock:\n"
				+ "   3GHz\n"
				+ " Memory:\n"
				+ "  name:\n"
				+ "   DDR3\n"
				+ "  capacity:\n"
				+ "   4GB\n"
				;
		try {
			Creator creator;
			switch (type.toLowerCase()) {
			case "xml": creator = new XmlDocument_ConcreteCreator(); break;
			case "json": creator = new JsonDocument_ConcreteCreator(); break;
			default: throw new UnsupportedOperationException(type);
			}
			creator.parse(new StringReader(text));
			creator.printNode(System.out);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	// Creator ///////////////////////////////////////
	/** Creator 抽象クラス。 */
	public abstract class Creator extends AbstractNode_AbstractProduct {
		protected Creator(String name, String value) { super(name, value); }
		public void parse(Reader in) throws IOException {
			LinkedList<Node_AbstractProduct> parentStack = new LinkedList<>();
			parentStack.push(this);
			BufferedReader reader = new BufferedReader(in);
			int indent = -1;
			String line;
			while (null != (line = reader.readLine())) {
				String nodeText = line.trim();
				// 親を探す
				Node_AbstractProduct parent;
				for (int i = 0; i < indent - (line.length() - nodeText.length()) + 1; ++ i) {
					parent = parentStack.pop();
				}
				parent = parentStack.peek();
				// 子供を追加
				Node_AbstractProduct node = parent.appendChild(nodeText.endsWith(":")
						? this.createElement(nodeText.substring(0, nodeText.length() - 1))
						: this.createTextNode(nodeText));
				parentStack.push(node);
				indent = line.length() - nodeText.length();
			}
		}
		public void printNode(PrintStream writer) {
			this.getChildNodes().get(0).printNode(writer);
			writer.println();
		}
		protected abstract Node_AbstractProduct createElement(String name);
		protected abstract Node_AbstractProduct createTextNode(String value);
	}
	/** XML Node を生成する Document Creator。 */
	public class XmlDocument_ConcreteCreator extends Creator {
		public XmlDocument_ConcreteCreator() { super("XML", null); }
		protected Node_AbstractProduct createElement(String name) {
			return new XmlElement_ConcreteProduct(name);
		}
		protected Node_AbstractProduct createTextNode(String value) {
			return new XmlText_ConcreteProduct(value);
		}
	}
	/** Json Node を生成する Document Creator。 */
	public class JsonDocument_ConcreteCreator extends Creator {
		public JsonDocument_ConcreteCreator() { super("Json", null); }
		protected Node_AbstractProduct createElement(String name) {
			return new JsonElement_ConcreteProduct(name);
		}
		protected Node_AbstractProduct createTextNode(String value) {
			return new JsonText_ConcreteProduct(value);
		}
	}

	// Product ///////////////////////////////////////
	/** Node Product インターフェース。 */
	public interface Node_AbstractProduct {
		public String getNodeName();
		public String getNodeValue();
		public Node_AbstractProduct appendChild(Node_AbstractProduct node);
		public List<Node_AbstractProduct> getChildNodes();
		public void printNode(PrintStream writer);
	}
	/** Node Product 抽象クラス。 */
	public abstract class AbstractNode_AbstractProduct implements Node_AbstractProduct {
		private String name, value;
		private LinkedList<Node_AbstractProduct> children = new LinkedList<>();
		protected AbstractNode_AbstractProduct(String name, String value) {
			this.name = name;
			this.value = value;
		}
		public String getNodeName() { return this.name; }
		public String getNodeValue() { return this.value; }
		public Node_AbstractProduct appendChild(Node_AbstractProduct node) {
			this.children.add(node);
			return node;
		}
		public List<Node_AbstractProduct> getChildNodes() {
			return this.children;
		}
	}
	/** XML Element Node Product。 */
	public class XmlElement_ConcreteProduct extends AbstractNode_AbstractProduct {
		public XmlElement_ConcreteProduct(String name) { super(name, null); }
		public void printNode(PrintStream writer) {
			writer.print("<" + this.getNodeName() + ">");
			for (Node_AbstractProduct node: this.getChildNodes()) {
				node.printNode(writer);
			}
			writer.print("</" + this.getNodeName() + ">");
		}
	}
	/** XML Text Node Product。 */
	public class XmlText_ConcreteProduct extends AbstractNode_AbstractProduct {
		public XmlText_ConcreteProduct(String value) { super(null, value); }
		public void printNode(PrintStream writer) {
			writer.print(this.getNodeValue());
		}
	}
	/** Json Element Node Product。 */
	public class JsonElement_ConcreteProduct extends AbstractNode_AbstractProduct {
		public JsonElement_ConcreteProduct(String name) { super(name, null); }
		public void printNode(PrintStream writer) {
			writer.print("{" + this.getNodeName() + ":");
			String comma = "";
			for (Node_AbstractProduct node: this.getChildNodes()) {
				writer.print(comma);
				comma = ",";
				node.printNode(writer);
			}
			writer.print("}");
		}
	}
	/** Json Text Node Product。 */
	public class JsonText_ConcreteProduct extends AbstractNode_AbstractProduct {
		public JsonText_ConcreteProduct(String value) { super(null, value); }
		public void printNode(PrintStream writer) {
			writer.print(this.getNodeValue());
		}
	}
}

0 件のコメント:

コメントを投稿