通过组装生成复杂的实例-Builder模式

128 阅读2分钟

1、引入

build意为建造,builder模式即建造模式。在建造复杂的物品时,可以先建造物体各部分,再把他们组装起来,这样可以提高建造效率。建造者模式便是如此。

2、实例

在例子中,我们要基于相同的结构搭建两个不同的文档。

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

2.2、Builder类的子类实现

实现一:文本文档

public class TextBuilder extends Builder{
    private StringBuffer buffer=new StringBuffer();
    @Override
    public void makeTitle(String title) {
        buffer.append("=============================\n");
        buffer.append("["+title+"]\n");
        buffer.append("\n");
    }

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

    @Override
    public void makeItems(String[] items) {
        for (String s : items) {
            buffer.append("  *"+s+"\n");
        }
        buffer.append("\n");
    }

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

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

实现二:HTML文档

public class HTMLBuilder extends Builder{
    private String filename;
    private PrintWriter printWriter;
    @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 (String item : items) {
            printWriter.println("<li>"+item+"</li>");
        }
        printWriter.println("</ul>");
    }

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

    public String getResult() {
        return filename;
    }
}

2.3、指导建造者Director

在Director类中,规范了如何将各零件拼接

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

2.4、测试

分别生成两种文档:

public class Main {
    public static void main(String[] args) {
        //普通文本
        TextBuilder textBuilder = new TextBuilder();
        Director director = new Director(textBuilder);
        director.construct();
        String result0 = textBuilder.getResult();
        System.out.println("普通文本:"+result0);
        //html
        HTMLBuilder htmlBuilder = new HTMLBuilder();
        director=new Director(htmlBuilder);
        director.construct();
        String result1 = htmlBuilder.getResult();
        System.out.println("html:"+result1);
    }
}

运行结果:

image.png

image.png

3、tips

  • 了解template模式的同学会觉得两者很像:都是将零件拼接成复杂的成品。而两者不同在于:

    • template模式是在父类中控制零件的拼接,因此子类无论如何都是生成类模板的产品,体现了“模板”的特性。
    • Buider模式是通过Director类来控制零件的拼接,可以通过不同的Director来builder不同的产品,体现了“建造”。
  • 谁知道什么,限制类的“认知”可以提高组件的可替换性:

    • Main类没有调用Builder类中的具体函数,他只需要关注调用Director即可,这样要更换Builder类时只需要更改一处
    • Director类知道要调用Builder类,但是不知道调用那个具体实现。但是它也没必要知道要用哪个具体实现,知道有Builder类中定义的方法即可,在实际问题中会为它传入实现了Builder方法的子类。正是这种无知,才给予了Builder参数的可替换性