8、Builder 建造者模式 组装复杂的实例 创造型模式

1、什么是Builder模式

定义:

将一个复杂对象的构建与表示相分离,使得同样的构建过程可以创建不同的表示。大白话就是,你不需要知道这个类的内部是什么样的,只用把想使用的参数传进去就可以了,达到了解耦的目的。

使用场景:

(1) 相同的方法,不同的执行顺序,产生不同的事件结果时。 (2) 多个部件或零件,都可以装配到一个对象中。但是产生的运行结果又不相同时。 (3) 产品类非常复杂,或者产品类中的调用顺序不同产生了不同的作用,这个时候使用建造者模式非常合适。 (4) 当初始化一个对象特别复杂,如参数多,且很多参数都具有默认值时。

2、示例

类的一览表:

image-20200720162210397

示例类图:

image-20200720162156254

定义Builder接口

 package cn.design.create.builder;
 
 /**
  * @author lin
  * @version 1.0
  * @date 2020-07-20 14:15
  * @Description TODO
  */
 public interface Builder {
 
     void makeTitle(String title);
 
     void makeString(String str);
 
     void makeItems(String[] item);
 
     void close();
 }
 

定义HtmlBuilder类

 package cn.design.create.builder;
 
 import java.io.FileWriter;
 import java.io.IOException;
 import java.io.PrintWriter;
 
 /**
  * @author lin
  * @version 1.0
  * @date 2020-07-20 15:58
  * @Description TODO
  */
 public class HtmlBuilder implements Builder {
     //文件名
     private String filename;
     //用于编写文件的PrintWriter
     private PrintWriter writer;
 
     @Override
     public void makeTitle(String title) {
         // HTML文件的标题
         //将标题作为文件名
         filename = title + ".html";
         try {
             // 生成PrintWriter
             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) {
         // HTML文件中的字符串
         //用<p>标签输出
         writer.println("<h4>" + str + "</h4>");
    }
 
     @Override
     public void makeItems(String[] items) {
         // HTML文件中的条目
         writer.println("<ul>");
         //用<ul>和<li>输出
         for (int i = 0; i < items.length; i++) {
             writer.println("<li>" + items[i] + "</1i>");
        }
         writer.println("</ul>");
    }
 
     @Override
     public void close() {
         //完成文档
         writer.println("</body></html>");
         //关闭标签
         writer.close();
         //关闭文件
    }
 
     public String getResult() {
         //编写完成的文档
         //返回文件名
         return filename;
    }
 }
 

定义TextBuilder类

 package cn.design.create.builder;
 
 /**
  * @author lin
  * @version 1.0
  * @date 2020-07-20 15:53
  * @Description TODO
  */
 public class TextBuilder implements Builder {
     private StringBuffer buffer = new StringBuffer();
 
     //又档内容保存在该子段中
     @Override
     public void makeTitle(String title) {
         //纯文本的标题
         buffer.append("========================= "); //装饰线
         //为标题添加「」
         buffer.append("「" + title + "」 ");
    }
 
     @Override
     public void makeString(String str) {
         //纯文本的字符串.
         //为字符串添加■
         buffer.append(" ■" + str + " ");
    }
 
     @Override
     public void makeItems(String[] items) {
         //纯文本的条目
         for (int i = 0; i < items.length; i++) {
             //为条目添加.
             buffer.append(" ● " + items[i] + " ");
        }
    }
 
     @Override
     public void close() {
         //完成文档
         // 装饰线
         buffer.append("======================== ");
    }
 
     public String getResult() {
         //完成的文档
         //将StringBuffer变换为String
         return buffer.toString();
    }
 }

定义Director类

 package cn.design.create.builder;
 
 /**
  * @author lin
  * @version 1.0
  * @date 2020-07-20 14:18
  * @Description TODO
  */
 public class Director {
     private Builder builder;
 
     public Director(Builder builder) {
         //因为接收的参数是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();
    }
 }

定义测试BuilderMain类

 package cn.design.create.builder;
 
 /**
  * @author lin
  * @version 1.0
  * @date 2020-07-20 13:53
  * @Description TODO
  */
 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.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);
        }
    }
 
     public static void usage() {
         System.out.println("Usage: java Main plain 编写纯文本文档");
         System.out.println("Usage: java Main html 编写HTML文档");
    }
 
 }
 

运行结果:

plain:

 =========================
 「Greeting」
  ■从早上至下午
  ● 早上好。
  ● 下午好。
  ■晚上
  ● 晚上好。
  ● 晚安。
  ● 再见。
 ========================

html:

image-20200720162537693

3、Builder模式中的角色

类图:

image-20200720162648739

角色说明:

◆ Builder (建造者)

Builder角色负责定义用于生成实例的接口( API )。Builder 角色中准备了用于生成实例的方法。在示例程序中,由Builder类扮演此角色。

◆ ConcreteBuilder (具体的建造者)

ConcreteBuilder角色是负责实现Builder角色的接口的类(API)。这里定义了在生成实例时实际被调用的方法。此外,在ConcreteBuilder角色中还定义了获取最终生成结果的方法。在示例程序中,由TextBuilder类和HTMLBui lder类扮演此角色。

◆ Director (监工)

Director角色负责使用Builder角色的接口( API)来生成实例。它并不依赖于ConcreteBuilder角色。为了确保不论ConcreteBuilder角色是如何被定义的,Director 角色都能正常工作,它只调用在Builder角色中被定义的方法。在示例程序中,由Director类扮演此角色。

◆Client(使用者)

该角色使用了Builder 模式中,Builder 模式并不包含Client角色。在示例程序中,由Main类扮演此角色。

4、相关的设计模式对比

◆Template Method模式

在Builder模式中,Director 角色控制Builder角色。 在Template Method模式中,父类控制子类。

◆Composite模式

有些情况下Builder模式生成的实例构成了Composite模式。

◆Abstract Factory模式

Builder模式和Abstract Factory模式都用于生成复杂的实例。

◆Facade模式

在Builder模式中,Director 角色通过组合Builder角色中的复杂方法向外部提供可以简单生成实例的接口( API)(相当于示例程序中的construct方法)。 Facade模式中的Facade角色则是通过组合内部模块向外部提供可以简单调用的接口( API)。

5、小结

为了灵活构造复杂对象,该对象会有多个成员变量,在外部调用的时候,不需要或者不方便一次性创建出所有的成员变量,在这种情况下,使用多个构造方法去构建对象,很难维护,这时候Builder设计模式解决这个问题,进行buid()方法中创建对象,并且将builder传入,该builder中,维护了传入对象的成员变量。

优点:

我可以不必知道你的内部构造是怎样的,我可以直接使用Builder建造自己需要的客户端;代码清晰,易维护,易扩展;将构造和表示分离,降低耦合 缺点:

代码也可能不清晰,不易维护(怎么说:比如你的客户端实现了很多接口,当你每当修改接口的时候,每次都要对应修改你的客户端);使用不恰当消耗内存

6、Main方法如何为args传参

 参数举例: 123 456 abc def 
 
 1. 编译器idea的使用方式:
    在 main 的 启动配置中 ,在程序参数中 填入自己需要的值
     
 2. 黑窗口的使用方式:
    不使用包路径, 大家都会. javac XXX.java 执行 java XXX abc def 123
    使用包路径,则要注意, javac -d . XXX.java 执行 java cn.**.XXX 123 456

第一种参考如下:

image-20200720163541134

第二种参考如下:

 javac -d . cn.fagejiang.Test.java
 java cn.fagejiang.Test plain

 

公众号发哥讲

这是一个稍偏基础和偏技术的公众号,甚至其中包括一些可能阅读量很低的包含代码的技术文,不知道你是不是喜欢,期待你的关注。

代码分享

https://gitee.com/naimaohome

微信公众号 点击关于我,加入QQ群,即可获取到代码以及高级进阶视频和电子书!!

img

如果你觉得文章还不错,就请点击右上角选择发送给朋友或者转发到朋友圈~

● 扫码关注我们

据说看到好文章不推荐的人,服务器容易宕机!

本文版权归 发哥讲博客园 共有,原创文章,未经允许不得转载,否则保留追究法律责任的权利。

 

 

原文地址:https://www.cnblogs.com/naimao/p/13353493.html