mohuneko’s blog

かんばる駆け出しエンジニアのブログです

【Java】Builderパターン【デザインパターン】

駆け出しエンジニアがデザインパターンをもくもく勉強します

 こんな本で勉強しています🌟

目次

Builderパターンについて

Builderパターンとは

  • 全体を構成している、各部分部分の処理を積上げていくためのパターンです

サンプルプログラム

  • Builderパターンを使って、タイトル、文字列、箇条項目を含む文章を作成する例を取り上げます
  • 各クラスの役割は以下のようになっています
名前 役割
Builder 文章を構成するメソッドを定めた抽象クラス
表現形式を決定する
Director 一つの文章を作るクラス
作成過程を決定する
TextBuilder プレーンテキストで文章を作るクラス
HTMLBuilder HTMLファイルでで文章を作るクラス

f:id:mohuNeko:20201228221742p:plain

Builderクラス

  • 文章を作るメソッドを宣言する抽象クラスです
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();
}

Directorクラス

  • Builderクラスで宣言されているメソッドで文章を作ります
  • Builderクラスは抽象クラスなので、インスタンスを生成できません
  • Directorクラスのコンストラクタに渡されるのは、Builderクラスのサブクラスのインスタンスです
  • 与えられたインスタンスの種類によって、Direntorで作るインスタンスの形式が決まります
public class Director {
    private Builder builder;
    public Director(Builder builder) {      // 与えられたBuilderのサブクラスのインスタンスを、builderフィールドに保持
        this.builder = builder;             
    }
    public void construct() {  // 文書構築メソッド
        builder.makeTitle("Greeting");  // タイトル
        builder.makeString("朝から昼にかけて");  // 文字列
        builder.makeItems(new String[]{  // 箇条書き
            "おはようございます。",
            "こんにちは。",
        });
        builder.makeString("夜に");  // 別の文字列
        builder.makeItems(new String[]{  // 別の箇条書き
            "こんばんは。",
            "おやすみなさい。",
            "さようなら。",
        });
        builder.close();  // 文書を完成させる
    }
}

TextBuilderクラス

  • Builderクラスのサブクラスです
public class TextBuilder extends Builder {
    private StringBuffer buffer = new StringBuffer();        
    public void makeTitle(String title) {                       
        buffer.append("==============================\n");          
        buffer.append("『" + title + "』\n");                      
        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();  // StringBufferをStringに変換
    }
}

HTMLBuilderクラス

  • Builderクラスのサブクラスです
  • 文章を構築し、HTMLファイル名を返します
import java.io.*;

public class HTMLBuilder extends Builder {
    private String filename;         // 作成するファイル名
    private PrintWriter writer;     // ファイルに書き込むPrintWriter
    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;                                            
    }
}

Mainクラスで動作確認

  • コマンドラインの引数に応じて、plain/HTML文章を作成します
  • コマンドライン
    • plainを指定した場合、TextBuilderクラスのインスタンスをDirectorクラスのコンストラクタに渡します
    • htmlを指定した場合、HTMLBuilderクラスのインスタンスをDirectorクラスのコンストラクタに渡します
    • TextBuilder、HTMLBuilderはBuilderのサブクラスです
  • Directorは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.construct();
            String result = textbuilder.getResult();
            System.out.println(result);

        } else if (args[0].equals("html")) {
            HTMLBuilder htmlbuilder = new HTMLBuilder();
            Director director = new Director(htmlbuilder);
            director.construct();
            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ファイルで文書作成");
    }
}

Builderパターンのメリット

  • MainはDirectorクラスのconstructメソッドのみを呼び出し、
  • DirectorクラスはConcreteBuilderクラスに依存せず、Builderクラスのメソッドのみを利用します
  • →ConcreteBuilderクラスは必要に応じて変更することができます
    • Builderの抽象メソッドにどのような動作が期待されているのかを知っておくのが重要です

今日のポイント

  • 全体を構成している、各部分部分の処理を積上げていくためのパターンです
  • DirectorクラスはConcreteBuilderクラスに依存しないので、必要に応じて変更することができます
    • 例えば、同じ文書を要求に合わせて、異なる表現形式で出力することができます

 本日もお疲れ様です😊