Builderパターン(インスタンスを作る)
複雑なインスタンスを組み立てる
---目的---
作成するプログラムがとても大きくて複雑なものを作る時に
まずシンプルな全体像を作成して、そこから細かい所を作っていきたい。
(具体例)
動物園を例にしてみます。
登場するキャラクター
①動物園作りの仕事をしている私、②キリン、③ゾウ、④ライオン
(1)新しく動物園を作る事になった
(2)どんな動物が入るかわからないけど3種類の動物を想定して
動物が入るゲージを作る、入場ゲートを作る、入場料を決める①
⇒大雑把に入る動物は決まってないけれど動物園が完成
(3)動物園にやってくる動物が決まる(キリン、ゾウ、ライオン)
それぞれの動物によって必要なエサ、ゲージの形状が決まる
(4)入る動物が決まり、ゲージの形状など具体的に細かい所まで作り込んだ
動物園が完成する
と言うイメージでいます。
---実施---
- Builder = 文章を構成するためのメソッドを定めた抽象クラス
- Director = 1つの文章を作るクラス
- TextBuilder = プレーンテキスト(普通の文字列)を使って文章を作るクラス
- HTMLBuilderHTML = ファイルを使って文章を作るクラスMain動作テスト用のクラス
public abstract class Builder {
public abstract void makeTitle(String title);
public abstract void makeString(String str);
public abstract void makeItems(String[] items);
public abstract void close();
}
makeTitle(String title)
タイトル構築メソッド
makeString(String str)
文字列構築メソッド
makeItems(String[] items)
箇条書き構築メソッド
close()
文章を完成させるメソッド
public class Director {
private Builder builder;
public Director(Builder buider){
this.builder = buider;
}
public void constract(){
builder.makeTitle("Greeting");
builder.makeString("朝から昼にかけて");
builder.makeItems(new String[] {
"おはようございます",
"こんにちは"
});
builder.makeString("夜に");
builder.makeItems(new String[] {
"こんばんは",
"おやすみなさい",
"さようなら"
});
builder.close();
}
}
constract()
文書を作るメソッド。このメソッドを呼ぶと、文書が構築されます。
public class TextBuilder extends Builder{
private StringBuffer buffer = new StringBuffer();
public void makeTitle(String title){
buffer.append("====================\n");
buffer.append("『"+title+"』");
buffer.append("\n");
}
public void makeString(String str){
buffer.append("■"+str+"\n");
buffer.append("\n");
}
public void makeItems(String[] items){
for(int i = 0; i <items.length; i++){
buffer.append(" *" + items[i] + "\n");
}
buffer.append("\n");
}
public void close(){
buffer.append("====================\n");
}
public String getResult(){
return buffer.toString();
}
}
Builderクラスの抽象メソッドを実装している
public class HTMLBuilder extends Builder {
private String filename;
private PrintWriter writer;
public void makeTitle(String title) {
filename = title + ".html";
try{
writer = new PrintWriter(new FileWriter(filename));
}catch(IOException e){
e.printStackTrace();
}
writer.println("<html><head><title>" + title +"</title></head><body>");
//タイトルを出力
writer.println("<h1>" + title + "</h1>");
}
public void makeString(String str) {
writer.println("<p>" + str +"</p>");
}
public void makeItems(String[] items) {
writer.println("<ul>");
for(int i = 0; i < items.length; i++){
writer.println("<li>" + items[i] + "</li>");
}
writer.println("</ul>");
}
public void close() {
writer.println("</body></html>");
writer.close();
}
public String getResult(){
return filename;
}
}
Builderクラスの抽象メソッドを実装している
public class Main {
public static void main(String[] args){
if(args.length !=1){
usage();
System.exit(0);
}
if(args[0].equals("plain")){
TextBuilder textbuilder = new TextBuilder();
Director director = new Director(textbuilder);
director.constract();
String result = textbuilder.getResult();
System.out.println(result);
}else if(args[0].equals("html")){
HTMLBuilder htmlbuilder = new HTMLBuilder();
Director director = new Director(htmlbuilder);
director.constract();
String filename = htmlbuilder.getResult();
System.out.println(filename + "が作成されまし。");
}else{
usage();
}System.exit(0);
}
public static void usage(){
System.out.println("Usage: java Main plain プレーンテキストで文書作成");
System.out.println("Usage: java Main html htmlで文書作成");
}
}
---結果---
mainメソッドを実行する際に引数の値が、
plainの場合はテキストで出力される。
htmlの場合はHTML形式で出力される。
---まとめ---
- 誰が何を知っているか
- どのクラスがどのメソッドを使えるか(使ってよいか)に注意してプログラミングをする必要がある
- 例ではMainクラスはBuilderクラスのメソッドを知らない
- MainクラスはDirectorクラスのconstractメソッドだけを呼び出す。
- DirectorクラスはBuilderクラスのメソッドを利用してロジックを作成しているが、 実際に利用しているクラスが何なのかは知らない。
- Directorクラスが自分の利用しているBuilderクラスのサブクラスを知らないのは良い。 =「知らないからこそ入れ替えができる」
「交換可能性」について常に意識する
~上記例での流れ~
(1)Builder(抽象クラス)に抽象メソッドを作成
(2)Directorのコンストラクタの引数にBuilder(実装クラス)を入れてインスタンス生成
(3)Builder(抽象クラス)を実装したクラスを作成
(4)Directorクラスのメソッド内でBuilder(実装クラス)のインスタンスのメソッドを使用
(5)mainメソッド内でBuilder(実装クラス)のインスタンスを作成し、Directorのコンストラクタの引数に入れてDirectorインスタンス作成