Builder (建造者)模式

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 模式类图

Builder 模式时序图

Builder 模式时序图
7.4 拓展思路的要点
|| 谁知道什么

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值