2015年6月3日水曜日

1-2. Builder パターン

同じ構築プロセスで別の表現を作成できるように、複合したオブジェクトの構築を分離します。



Builder パターンでは、Product を構成するオブジェクト Part を生成する Builder を用いて、
Director がオブジェクトの構築を行います。
Builder パターンの目的は、オブジェクトの生成・構築を Builder, Director に分離すること
にあります。
これにより、Builder を差し替えるだけで別の内容の Product を得ることが可能に
なります。

複数のオブジェクトを構築する場合、
Product product = new Product();
    product.add(new Part1());
    product.add(new Part2());
Builder パターンでは、Builder 抽象クラスで個々の Part オブジェクトを生成する
インターフェースを用意し、ConcreteBuilder 派生クラスで実装します。
public interface Builder {
        public void buildPart1();
        public void buildPart2();
    }
    public class ConcreteBuilder1 implements Builder {
        private Product product;
        public ConcreteBuilder1(Product product) { this.product = product; }
        public void buildPart1() { this.product.add(new Part1()); }
        public void buildPart2() { this.product.add(new Part2()); }
        public Product getResult() { return this.product; }
    }
Director クラス内で、これらの Builder のインターフェースを介して実際に
オブジェクトを構築します。
public class Director {
        private Builder builder;
        public Director(Builder builder) { this.builder = builder; }
        public void construct() {
            this.builder.buildPart1();
            this.builder.buildPart2();
        }
    }
Client は、Builder, Director インターフェースを介してオブジェクトを
構築・取得します。
Builder builder = new ConcreateBuilder1();
    Director director = new Director(builder);
    director.construct();
    Product product = builder.getResult();
これにより、Builder を差し替えるだけで別の内容の Product を得ることが可能に
なります。
Builder builder = new ConcreateBuilder2(); // 差し替え
    Director director = new Director(builder);
    director.construct();
    Product product = builder.getResult();

サンプルプログラムでは、異なる構成部品からなる PC を XML, Json で構築する
Document Product の構築例を示します。
package design_pattern;

import java.io.PrintStream;
import java.util.LinkedList;
import java.util.List;

/**
 * Builder
 * GoF
 * 同じ構築プロセスで別の表現を作成できるように、複合したオブジェクトの構築を分離します。
 * 
 * Builder パターンでは、Product を構成するオブジェクト Part を生成する Builder を用いて、 
 * Director がオブジェクトの構築を行います。
 * Builder パターンの目的は、オブジェクトの生成・構築を Builder, Director に分離すること
 * にあります。
 * これにより、Builder を差し替えるだけで別の内容の Product を得ることが可能に
 * なります。
 * 
 * 複数のオブジェクトを構築する場合、
 * <pre>
 *     Product product = new Product();
 *     product.add(new Part1());
 *     product.add(new Part2());
 * </pre>
 * Builder パターンでは、Builder 抽象クラスで個々の Part オブジェクトを生成する
 * インターフェースを用意し、ConcreteBuilder 派生クラスで実装します。
 * <pre>
 *     public interface Builder {
 *         public void buildPart1();
 *         public void buildPart2();
 *     }
 *     public class ConcreteBuilder1 implements Builder {
 *         private Product product;
 *         public ConcreteBuilder1(Product product) { this.product = product; }
 *         public void buildPart1() { this.product.add(new Part1()); }
 *         public void buildPart2() { this.product.add(new Part2()); }
 *         public Product getResult() { return this.product; }
 *     }
 * </pre>
 * Director クラス内で、これらの Builder のインターフェースを介して実際に
 * オブジェクトを構築します。
 * <pre>
 *     public class Director {
 *         private Builder builder;
 *         public Director(Builder builder) { this.builder = builder; }
 *         public void construct() {
 *             this.builder.buildPart1();
 *             this.builder.buildPart2();
 *         }
 *     }
 * </pre>
 * Client は、Builder, Director インターフェースを介してオブジェクトを
 * 構築・取得します。
 * <pre>
 *     Builder builder = new ConcreateBuilder1();
 *     Director director = new Director(builder);
 *     director.construct();
 *     Product product = builder.getResult();
 * </pre>
 * これにより、Builder を差し替えるだけで別の内容の Product を得ることが可能に
 * なります。
 * <pre>
 *     Builder builder = new ConcreateBuilder2(); // 差し替え
 *     Director director = new Director(builder);
 *     director.construct();
 *     Product product = builder.getResult();
 * </pre>
 * 
 * サンプルプログラムでは、異なる構成部品からなる PC を XML, Json で構築する
 * Document Product の構築例を示します。
 * 
 * 出力結果:
// Builder 未使用
<PC>
  <CPU>
    <name>
      Core i7
    </name>
    <clock>
      3GHz
    </clock>
  </CPU>
  <Memory>
    <name>
      DDR3
    </name>
    <capacity>
      4GB
    </capacity>
  </Memory>
</PC>
// Builder 使用
<PC>
  <CPU>
    <name>
      Core i7
    </name>
    <clock>
      3GHz
    </clock>
  </CPU>
  <Memory>
    <name>
      DDR3
    </name>
    <capacity>
      4GB
    </capacity>
  </Memory>
</PC>
// Builder 未使用
{PC:
  {CPU:
    {name:
      Core i5
    }
    ,{clock:
      2GHz
    }
  }
  ,{Memory:
    {name:
      DDR3
    }
    ,{capacity:
      2GB
    }
  }
}
// Builder 使用
{PC:
  {CPU:
    {name:
      Core i5
    }
    ,{clock:
      2GHz
    }
  }
  ,{Memory:
    {name:
      DDR3
    }
    ,{capacity:
      2GB
    }
  }
}
 * 
 * @author 2015/05/22 matsushima
 */
public class BuilderSample {
	/**
	 * main
	 * 
	 * @param args
	 */
	public static void main(String[] args) {
		for (String type: args.length >= 1 ? args : new String[]{"xml", "json"}) {
			new BuilderSample().client(type);
		}
	}
	/**
	 * Client
	 */
	void client(String type) {
		/*
		 * 複数のオブジェクトを構築する場合、
		 */
		System.out.println("// Builder 未使用");
		if ("xml".equals(type)) {
			Node_AbstractPart e1, e2, e3;
			XmlDocument_ConcreteProduct doc1 = new XmlDocument_ConcreteProduct("XML");
			doc1.appendChild(e1 = new XmlElement_ConcretePart("PC"));
			e1.appendChild(e2 = new XmlElement_ConcretePart("CPU"));
			e2.appendChild(e3 = new XmlElement_ConcretePart("name"));
			e3.appendChild(new XmlText_ConcretePart("Core i7"));
			e2.appendChild(e3 = new XmlElement_ConcretePart("clock"));
			e3.appendChild(new XmlText_ConcretePart("3GHz"));
			e1.appendChild(e2 = new XmlElement_ConcretePart("Memory"));
			e2.appendChild(e3 = new XmlElement_ConcretePart("name"));
			e3.appendChild(new XmlText_ConcretePart("DDR3"));
			e2.appendChild(e3 = new XmlElement_ConcretePart("capacity"));
			e3.appendChild(new XmlText_ConcretePart("4GB"));
			doc1.printNode(System.out, "");
		} else if ("json".equals(type)) {
			Node_AbstractPart e1, e2, e3;
			JsonDocument_ConcreteProduct doc2 = new JsonDocument_ConcreteProduct("Json");
			doc2.appendChild(e1 = new JsonElement_ConcretePart("PC"));
			e1.appendChild(e2 = new JsonElement_ConcretePart("CPU"));
			e2.appendChild(e3 = new JsonElement_ConcretePart("name"));
			e3.appendChild(new JsonText_ConcretePart("Core i5"));
			e2.appendChild(e3 = new JsonElement_ConcretePart("clock"));
			e3.appendChild(new JsonText_ConcretePart("2GHz"));
			e1.appendChild(e2 = new JsonElement_ConcretePart("Memory"));
			e2.appendChild(e3 = new JsonElement_ConcretePart("name"));
			e3.appendChild(new JsonText_ConcretePart("DDR3"));
			e2.appendChild(e3 = new JsonElement_ConcretePart("capacity"));
			e3.appendChild(new JsonText_ConcretePart("2GB"));
			doc2.printNode(System.out, "");
		} else {
			throw new UnsupportedOperationException(type);
		}
		/*
		 * Builder パターンでは、Builder 抽象クラスで個々の Part オブジェクトを生成する
		 * インターフェースを用意し、ConcreteBuilder 派生クラスで実装します。
		 * Director クラス内で、これらの Builder のインターフェースを介して実際に
		 * オブジェクトを構築します。
		 * Client は、Builder, Director インターフェースを介してオブジェクトを
		 * 構築・取得します。
		 * これにより、Builder を差し替えるだけで別の内容の Product を得ることが可能に
		 * なります。
		 */
		System.out.println("// Builder 使用");
		Builder builder;
		if ("xml".equals(type)) {
			builder = new XmlCorei7PcConCreateBuilder("XML");
		} else if ("json".equals(type)) {
			builder = new JsonCorei5PcConCreateBuilder("Json");
		} else {
			throw new UnsupportedOperationException(type);
		}
		Director director = new Director(builder);
		director.construct();
		builder.getResult().printNode(System.out, "");
	}

	// Director ///////////////////////////////////////
	/** Node(Text, Element) を生成する Director。 */
	class Director {
		private Builder builder;
		public Director(Builder builder) {
			this.builder = builder;
		}
		public void construct() {
			this.builder.buildPc();
			this.builder.buildCpu();
			this.builder.buildMemory();
		}
	}

	// Builder ///////////////////////////////////////
	/** Builder インターフェース。 */
	public interface Builder {
		public void buildPc();
		public void buildCpu();
		public void buildMemory();
		public Node_AbstractPart getResult();
	}
	/** XML Node(Core i7 PC) を生成する Builder。 */
	public class XmlCorei7PcConCreateBuilder implements Builder {
		private Node_AbstractPart document;
		public XmlCorei7PcConCreateBuilder(String name) {
			this.document = new XmlDocument_ConcreteProduct(name);
		}
		public void buildPc() {
			this.document.appendChild(new XmlElement_ConcretePart("PC"));
		}
		public void buildCpu() {
			Node_AbstractPart e1, e2, e3;
			e1 = this.document.getChildNodes().get(0);
			e1.appendChild(e2 = new XmlElement_ConcretePart("CPU"));
			e2.appendChild(e3 = new XmlElement_ConcretePart("name"));
			e3.appendChild(new XmlText_ConcretePart("Core i7"));
			e2.appendChild(e3 = new XmlElement_ConcretePart("clock"));
			e3.appendChild(new XmlText_ConcretePart("3GHz"));
		}
		public void buildMemory() {
			Node_AbstractPart e1, e2, e3;
			e1 = this.document.getChildNodes().get(0);
			e1.appendChild(e2 = new XmlElement_ConcretePart("Memory"));
			e2.appendChild(e3 = new XmlElement_ConcretePart("name"));
			e3.appendChild(new XmlText_ConcretePart("DDR3"));
			e2.appendChild(e3 = new XmlElement_ConcretePart("capacity"));
			e3.appendChild(new XmlText_ConcretePart("4GB"));
		}
		public Node_AbstractPart getResult() {
			return this.document;
		}
	}
	/** Json Node(Core i5 PC) を生成する Builder。 */
	public class JsonCorei5PcConCreateBuilder implements Builder {
		private Node_AbstractPart document;
		public JsonCorei5PcConCreateBuilder(String name) {
			this.document = new JsonDocument_ConcreteProduct(name);
		}
		public void buildPc() {
			this.document.appendChild(new JsonElement_ConcretePart("PC"));
		}
		public void buildCpu() {
			Node_AbstractPart e1, e2, e3;
			e1 = this.document.getChildNodes().get(0);
			e1.appendChild(e2 = new JsonElement_ConcretePart("CPU"));
			e2.appendChild(e3 = new JsonElement_ConcretePart("name"));
			e3.appendChild(new JsonText_ConcretePart("Core i5"));
			e2.appendChild(e3 = new JsonElement_ConcretePart("clock"));
			e3.appendChild(new JsonText_ConcretePart("2GHz"));
		}
		public void buildMemory() {
			Node_AbstractPart e1, e2, e3;
			e1 = this.document.getChildNodes().get(0);
			e1.appendChild(e2 = new JsonElement_ConcretePart("Memory"));
			e2.appendChild(e3 = new JsonElement_ConcretePart("name"));
			e3.appendChild(new JsonText_ConcretePart("DDR3"));
			e2.appendChild(e3 = new JsonElement_ConcretePart("capacity"));
			e3.appendChild(new JsonText_ConcretePart("2GB"));
		}
		public Node_AbstractPart getResult() {
			return this.document;
		}
	}

	// Product ///////////////////////////////////////
	/** Node Part インターフェース。 */
	public interface Node_AbstractPart {
		public String getNodeName();
		public String getNodeValue();
		public Node_AbstractPart appendChild(Node_AbstractPart node);
		public List<Node_AbstractPart> getChildNodes();
		public void printNode(PrintStream writer, String indent);
	}
	/** Node Part 抽象クラス。 */
	public abstract class AbstractNode_AbstractPart implements Node_AbstractPart {
		private String name, value;
		private LinkedList<Node_AbstractPart> children = new LinkedList<>();
		protected AbstractNode_AbstractPart(String name, String value) {
			this.name = name;
			this.value = value;
		}
		public String getNodeName() { return this.name; }
		public String getNodeValue() { return this.value; }
		public Node_AbstractPart appendChild(Node_AbstractPart node) {
			this.children.add(node);
			return node;
		}
		public List<Node_AbstractPart> getChildNodes() {
			return this.children;
		}
	}
	/** XML Document Product。 */
	public class XmlDocument_ConcreteProduct extends AbstractNode_AbstractPart {
		protected XmlDocument_ConcreteProduct(String name) { super(name, null); }
		public void printNode(PrintStream writer, String indent) {
			this.getChildNodes().get(0).printNode(writer, "");
		}
	}
	/** XML Element Node Part。 */
	public class XmlElement_ConcretePart extends AbstractNode_AbstractPart {
		public XmlElement_ConcretePart(String name) { super(name, null); }
		public void printNode(PrintStream writer, String indent) {
			writer.println(indent + "<" + this.getNodeName() + ">");
			for (Node_AbstractPart node: this.getChildNodes()) {
				node.printNode(writer, indent + "  ");
			}
			writer.println(indent + "</" + this.getNodeName() + ">");
		}
	}
	/** XML Text Node Part。 */
	public class XmlText_ConcretePart extends AbstractNode_AbstractPart {
		public XmlText_ConcretePart(String value) { super(null, value); }
		public void printNode(PrintStream writer, String indent) {
			writer.println(indent + this.getNodeValue());
		}
	}
	/** Json Document Product。 */
	public class JsonDocument_ConcreteProduct extends AbstractNode_AbstractPart {
		protected JsonDocument_ConcreteProduct(String name) { super(name, null); }
		public void printNode(PrintStream writer, String indent) {
			this.getChildNodes().get(0).printNode(writer, "");
		}
	}
	/** Json Element Node Part。 */
	public class JsonElement_ConcretePart extends AbstractNode_AbstractPart {
		public JsonElement_ConcretePart(String name) { super(name, null); }
		public void printNode(PrintStream writer, String indent) {
			writer.println(indent + "{" + this.getNodeName() + ":");
			indent = indent.replace(",", "");
			for (Node_AbstractPart node: this.getChildNodes()) {
				node.printNode(writer,
						indent + (this.getChildNodes().get(0) == node ? "  " : "  ,"));
			}
			writer.println(indent + "}");
		}
	}
	/** Json Text Node Part。 */
	public class JsonText_ConcretePart extends AbstractNode_AbstractPart {
		public JsonText_ConcretePart(String value) { super(null, value); }
		public void printNode(PrintStream writer, String indent) {
			writer.println(indent + this.getNodeValue());
		}
	}
}

0 件のコメント:

コメントを投稿