Builder (建造者)模式

    xiaoxiao2024-12-15  61

    7.1 Builder 模式

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

    7.2 示例程序

      作用:使用 Builder 模式编写 “文档” 的程序,具有以下结构。

    含有一个标题含有几个字符串含有条目项目   Builder 类中定义了决定文档接口的方法,然后 Director 类使用该方法编写一个具体的文档。   Builder 是抽象类,它并没有进行任何实际的处理,仅仅声明了抽象方法。Builder 类的子类决定了用来编写文档的具体处理。   在示例程序中,我们定义了以下 Builder 类的子类TextBuilder 类:使用纯文本编写文档。HTMLBuilder 类:使用 HTML 编写文档。

    示例程序类图
    || Builder 类

      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 类

      Director 类使用 Builder 类中声明的方法来编写文档。   Director 类通过构造方法存储 Buidler 的子类对象。其中 construct 方法是编写文档的方法。其调用的方法都是 Builder 中声明的方法。

    /** * 使用 Builder 类中声明的方法来编写文档 * */ 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(); } }
    || TextBuilder 类

      TextBuilder 类是 Builder 类的子类,其功能是使用纯文本编写文档,并以 String 返回结果。

    /** * 使用纯文本编写文档,并以 String 返回结果 */ public class TextBuilder extends Builder{ private StringBuilder builder = new StringBuilder(); @Override public void makeTitle(String title) { builder.append("===========================\n"); builder.append("『").append(title).append("』\n"); } @Override public void makeString(String str) { builder.append('■').append(str).append("\n"); } @Override public void makeItems(String[] items) { Arrays.asList(items).forEach(e -> builder.append(" .").append(e).append("\n")); } @Override public void close() { builder.append("===========================\n"); } public String getResult() { return builder.toString(); } }
    || HTMLBuilder 类

      HTMLBuilder 类是 Builder 类的子类,它的功能是使用 HTML 编写文档,返回是 HTML 文件的名字。

    /** * 使用 HTML 编写文档 */ public class HTMLBuilder extends Builder { private String fileName; private PrintWriter writer; @Override public void makeTitle(String title) { fileName = title + ".html"; initPrintWriter(fileName); writer.println("<html><head><title>" + title + "</title></head><body>"); writer.println("<h1>" + title + "</h1>"); } @Override public void makeString(String str) { writer.println("<p>" + str + "</p>"); } @Override public void makeItems(String[] items) { writer.println("<ul>"); Arrays.asList(items).forEach(e -> writer.println("<li>" + e + "</li>")); writer.println("</ul>"); } @Override public void close() { writer.println("</body></html>"); writer.close(); } public String getResult() { return fileName; } private void initPrintWriter(String fileName) { try { writer = new PrintWriter(new FileWriter(fileName)); } catch (IOException e) { e.printStackTrace(); System.out.println("创建文件失败"); } } }
    || Main 类

      Main 类是 Builder 模式的测试程序。当我们在命令行中指定参数为 plain 时,会将 TextBuilder 类的实例作为参数传递至 Director 类的构造方法中;而若是在命令行中指定参数为 HTML 的时候,会将 HTMLBuilder 类的实例作为参数传递至 Director 类的构造方法中。   注意,Director 并不关心实际编写的文档到底是 TextBuilder 还是 HTMLBuilder。

    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); } } private static void usage() { System.out.println("Usage: java Main plain 编写纯文本文档"); System.out.println("Usage: java Main html 编写 HTML 文档"); } }

    结果:   1.文本形式

    =========================== 『Greeting』 ■ 从早上至下午 . 早上好 . 下午好 ■ 晚上 . 晚上好 . 晚安。 . 再见。 ===========================

      2.HTML形式

    <html><head><title>Greeting</title></head><body> <h1>Greeting</h1> <p> 从早上至下午</p> <ul> <li> 早上好</li> <li> 下午好</li> </ul> <p> 晚上</p> <ul> <li> 晚上好</li> <li> 晚安。</li> <li> 再见。</li> </ul> </body></html>

    7.3 Builder 模式中的登场角色

      Builder 模式中有以下登场角色。

    Builer (建造者)   负责定义用于生成实例的接口(API)。Builder 角色中准备了用户生成实例的方法。ConcreteBuilder (具体的建造者)   负责实现 Builder 角色的接口的类(API)。定义了在生成实例的实际中被调用的方法Director (监工)   Director 角色负责使用 Builder 角色的接口(API)来生成实例。并不依赖于 ConcreteBuilder 角色。它只调用在 Builder 角色中被定义的方法。Client(使用者)   使用了 Builder 模式(Builder 模式并不包含该角色)。 Builder 模式类图

    Builder 模式时序图

    7.4 拓展思路的要点

    || 谁知道什么

      在面向对象的编程中,“谁知道什么” 是非常重要的。我们在编程时,需要注意到哪个类可以使用哪个方法以及使用这个方法到底好不好。   Main 类并不知道调用 Builder 类,它只调用了 Direct 类的 construcet 方法。   另一方面,Director 类知道 Builder 类,它调用 Builder 来的方法来编写文档。但是它并不知道 “真正” 使用的是哪个类。   “只有不知道子类才能替换”,正是因为可以替换,组件才具有高价值。   Director 决定了 Builer 角色中方法的调用顺序,而在 Template Method 模式中,父类决定了子类方法的调用顺序。

    最新回复(0)