建造者模式

769 阅读2分钟

背景

建造者(Builder)模式,在建造大楼时,需要先打牢地基,搭建框架,然后自下而上地一层一层盖起来。通常,在建造这种具有复杂结构的物体时,很难一气呵成。我们需要首先建造组成这个物体的各个部分,然后分阶段将他们组装起来

用于组装具有复杂结构的实例的Builder模式

登场角色

Builder 建造者

Builder 角色负责定义用于生成实例的接口(API)

ConcreteBuilder 具体的建造者

ConcreteBuilder 角色负责实现 Builder 角色的接口的类

Direct 监工

Direct角色负责使用Builder角色的接口生成实例,他并不依赖于 ConcreteBuilder角色,它只调用 Builder 角色中被定义的方法, 即实例代码中的 construct 方法

Cilent 使用者

该角色使用Bulider模式,Builder 模式并不包含Client角色

类图

示例代码

使用建造者模式编写一个 “文档”程序。这里编写出的文档具有以下结构:

  • 含有一个标题
  • 含有几个字符串
  • 含有条目项目

Builder类中定义了决定文档结构的方法,然后 Director 类使用该方法编写一个具体的文档

Builder的子类:

  • TextBuilder 类:使用纯文本编写文档
  • HTMLBuilder 类:使用HTML编写文档

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();
}

TextBuilder 实现类:使用纯文本编写文档

public class TextBuilder extends Builder {

    private StringBuilder stringBuilder = new StringBuilder();

    @Override
    public void makeTitle(String title) {
        stringBuilder.append("===================\n");
        stringBuilder.append("[").append(title).append("]");
        stringBuilder.append("\n");
    }

    @Override
    public void makeString(String str) {
        stringBuilder.append(" ").append(str).append("\n");
        stringBuilder.append("\n");
    }

    @Override
    public void makeItems(String[] items) {
        for (int i = 0; i < items.length; i++) {
            stringBuilder.append(" >").append(items[i]).append("\n");
        }
    }

    @Override
    public void close() {
        stringBuilder.append("===================\n");
    }

    public String getResult() {
        return stringBuilder.toString();
    }
}

HTMLBuilder实现类:使用 HTML 编写文档

public class HTMLBuilder extends Builder {

    private PrintWriter printWriter;

    private String fileName;

    @Override
    public void makeTitle(String title) {
        fileName = title + ".html";
        try {
            printWriter = new PrintWriter(fileName);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

        printWriter.println("<html><head><title>" + title + "</title></head><body>");
        // 输出标题
        printWriter.println("<h1>" + title + "</h1>");
    }

    @Override
    public void makeString(String str) {
        printWriter.println("<p>" + str + "</p>");
    }

    @Override
    public void makeItems(String[] items) {
        printWriter.println("<ul>");

        for (int i = 0; i < items.length; i++) {
            printWriter.println("<li>" + items[i] + "</li>");
        }

        printWriter.println("</ul>");
    }

    @Override
    public void close() {
        printWriter.println("</body></html>");
        printWriter.close();
    }

    public String getResult() {
        return fileName;
    }
}

Director类:使用Bulider类中声明的方法来编写文档

public class Director {
    private Builder builder;

    public Director(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();
    }
}

Main类

public class Main {
    public static void main(String[] args) {
        if (args.length != 1) {
            System.exit(0);
        }
        
        // 创建建造器
        if (args[0].equals("html")) {
            HTMLBuilder htmlBuilder = new HTMLBuilder();
            Director director = new Director(htmlBuilder);
            director.construct();
            System.out.println(htmlBuilder.getResult());
        }
        
        if (args[0].equals("plain")) {
            TextBuilder textBuilder = new TextBuilder();
            Director director = new Director(textBuilder);
            director.construct();
            System.out.println(textBuilder.getResult());
        }
    }
}