设计模式——Builder模式

1. 引言

通常,在构造复杂结构的物体时,很难一气呵成,我们需要首先构造组成这个物体的各个部分,然后分阶段将他们组装起来,Builer模式也是这个原理。

2. 示例程序

我们使用Builder模式编写“文档”,他具有如下结构:

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

抽象类Builder定义了决定文档结构的方法,Director类使用这些方法编写具体的文档,Builder类的子类决定了编写文档的具体方法。

  • TextBuilder类:使用普通字符串编写文档
  • HTMLBuilder类:使用HTML编写文档

2.1 类的一览表

名字说明
Builder定义文档结构的方法的抽象类
Director编写一个文档的类
TextBuilder使用纯文本编写文档
HTMLBuilder使用HTML编写文档
Main测试程序行为的类

2.2 示例程序类图

uml

2.3 Builder类

/**
 * 声明编写文档的抽象类,close是完成文档编写的方法
 * @author littlemotor
 * @since 18.9.13
 */
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.4 Director类

使用Builder类中声明的方法来编写文档,Director类的构造函数的参数是抽象类Builder的实现类,construct方法是编写文档的方法。

/**
 * 调用Builder实现类中的方法,完成文档的编写
 * @author littlemotor
 * @since 18.8.13
 */
public class Director {
  private Builder builder;
  
  public Director(Builder builder) {
    this.builder = builder;
  }
  
  //构造文档的方法
  public void constract() {
    builder.makeTitle("Greeting");
    builder.makeString("从早上到下午");
    builder.makeItems(new String[] {
        "早上好",
        "下午好"
    });
    builder.makeString("晚上");
    builder.makeItems(new String[] {
        "晚上好",
        "晚安"
    });
    builder.close();
  }
}

2.5 HTMLBuilder类

Builder子类,使用HTML编写文档

import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * Builder子类,输出HTML格式文档
 * @author littlemotor
 * @version 18.8.13
 */
public class HTMLBuilder extends Builder{
  private String filename;
  private PrintWriter writer;
  
  @Override
  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>");
  }

  @Override
  public void makeString(String str) {
    writer.println("<p>" + str + "</p>" );
  }
  
  @Override
  public void makeItems(String[] items) {
    for(int i = 0; i < items.length; i++) {
      writer.println("<li>" + items[i] + "</li>");
    }
    writer.println("</ul>");
  }

  @Override
  public void close() {
    writer.println("</body></html>");
    writer.close();
  }
  
  public String getResult() {
    return filename;
  }
}

2.6 TextBuilder类

使用纯文本编写文档并返回结果

/**
 * Builer实现类,使用纯文本编写文档
 * @author littlemotor
 * @version 18.8.13
 */
public class TextBuilder extends Builder{
  private StringBuffer buffer = new StringBuffer();
  
  @Override
  public void makeTitle(String title) {
    buffer.append("====================");
    buffer.append("「" + title + "」");
    buffer.append("\n");
  }

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

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

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

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

2.7 BuilderMain类

测试类,使用下面命令编写相应格式的文档:

java BuilderMain plain:编写纯文本文档
java BuilderMain html:编写HTML格式文档

代码如下:

public class BuilderMain {
  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.constract();
      String result = textbuilder.getResult();
      System.out.println(result);
    }
    else if(args[0].equals("html")) {
      HTMLBuilder htmlbuilder = new HTMLBuilder();
      Director director = new Director(htmlbuilder);
      director.constract();
      String filename = htmlbuilder.getResult();
      System.out.println(filename + "文档编写完成。");
      
    } else {
      usage();
      System.exit(0);
    }
  }
  
  public static void usage() {
    System.out.println("Usage: java BuilderMain plain     编写纯文本文档");
    System.out.println("Usage: java BuilderMain html      编写HTML文档");
  }
}

3. Builder模式中的角色

3.1 Builder(建造者)

Builder角色定义生成实例的接口(API),准备了用于生成实例的方法。

3.2 ConcreteBuilder(具体的建造者)

实现Builder角色的接口的类,定义了实际被调用的方法,并且还定义了最终生成结果的方法。

3.3 Director(监工)

Director角色负责使用Builder角色的接口(API)生成示例。

4. 小结

让组件具有可替换性才会具有更高的价值,组装的具体过程被隐藏在Director角色中。

另一种比较常用的builder模式是在类中增加一个相同类名的Builer子类,然后子类中每次构建一个自己的参数,并返回本身,这样分步构建,如下所示:

public class Page {
    private String data;
    private int pageSize;
    private int pageNumber;

    public static Page.PageBuilder builder() {
        return new Page.PageBuilder();
    }

    /**
      getter/setter方法
     */

    public BasePage() {
    }

    public BasePage(String data, int pageSize, int pageNumber) {
        /**
        有参初始化
         */
    }

    public static class PageBuilder {
        private String data;
        private int pageSize;
        private int pageNumber;

        PageBuilder() {
        }

        public Page.PageBuilder data(T data) {
            this.data = data;
            return this;
        }

        public Page.PageBuilder pageSize(int pageSize) {
            this.pageSize = pageSize;
            return this;
        }

        public Page.PageBuilder pageNumber(int pageNumber) {
            this.pageNumber = pageNumber;
            return this;
        }

        public Page build() {
            return new Page(this.data,this.pageSize, this.pageNumber);
        }

        public String toString() {
            return "Page.PageBuilder(data=" + this.data + ", pageSize=" + this.pageSize + ", pageNumber=" + this.pageNumber + ")";
        }
    }
}

构建过程可以为如下方式实现

Page.PageBuiler pageBuiler = Page.builer();
Page page = pageBuiler.date(...).pageSize(...).pageNumber(...).build();
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值