• 创建型设计模式 之 建造者模式


    一、介绍

    生成器模式是什么呢?

    《研磨设计模式中》给了这样的定义:将一个复杂对象的构建与它的表现分离,使得同样的构建过程可以创建不同的表示

    生成器模式的本质——分离整体构建算法和部件构造

    其实,生成器模式的目的,便是为了构建复杂的产品,将构建算法和构造实现分离出来,以便系统可以更好的优化、扩展。

    理解生成器模式主要是理解生成器2个部件,一个是生成器(Builder),一个是指导者(Director)。生成器指的是整个复杂对象的构建过程、构建算法。而指导者指的是对生成器所生成的部件对象的构造和装配。

    二、结构

    1、Product——需要最终构建的复杂对象。

    2、Director——指导者。用来使用生成器接口,并定义一个整体的构建算法,定义产品构建中不变的那些部分,而其他部分交给Builder来实现。

    3、Builder——生成器接口。定义创建一个Product所需的各个部件的操作。

    4、ConcreteBuilder——具体的生成器实现。实现Product各个组件的创建,并且负责组装各个部件。同时还可以获得整个产品。

    研磨设计模式里面有个很形象的图,这里拿来用一下:

    三、我的实现

    下面我们用一个例子来解释生成器模式。

    1、问题:假设我们要生产水果罐头,大体上有这么3个环节——材料准备、加工、包装。这3个环节都很复杂,需要复杂的设计和管理技术。我们希望能尽可能的将这3个环节的构建和这3个环节的组合从复杂的类层次中独立出来,以便系统升级、优化!生成器模式可以很好的解决这个问题。

    首先,3个环节可以抽象成这样3个简单的类,如下:

    //预处理
    public class Pretreatment {
    
        //这里只列出一个成员变量,实际还会有很多
        private Fruit fruit;
    
        public void setFruit(Fruit fruit) {
            this.fruit = fruit;
        }
        
        public Fruit getFruit() {
            return fruit;
        }
    }
    

      

    //制作过程
    public class Proceed {
        // 这里只列出一个成员变量,实际还会有很多
        private Cook cook;
    
        public Cook getCook() {
            return cook;
        }
    
        public void setCook(Cook cook) {
            this.cook = cook;
        }
    
    }
    

      

    //包装
    public class Pack {
    
        // 这里只列出一个成员变量,实际还会有很多
        private Can can;
    
        public Can getCan() {
            return can;
        }
    
        public void setCan(Can can) {
            this.can = can;
        }
    }
    

      

    2、为了构建整个过程,需要一个生成器,为了方便扩展,所以实现成接口,如下:

    package builder.toBuild;
    
    //生成器接口
    public interface Builder {
        
        //把前面3个类作为形参传进来
        public void pretreatment(Pretreatment pre);
    
        public void proceed(Proceed pro);
    
        public void pack(Pack pack);
    }
    

      

    3、生成器接口的实现类,就是我们抽象出来的整个过程、构建逻辑的核心了。如下:

    //生成器的实现类,封装了复杂对象的具体生成逻辑。
    public class BuilderImpl1 implements Builder {
    
        public void pretreatment(Pretreatment pre) {
            System.out.println("现在是预处理部!");
            System.out.println("精心选用上等"+pre.getFruit().getName()+",剥皮,洗干净!");
        }
    
        public void proceed(Proceed proceed) {
            System.out.println("现在是加工部!");
            System.out.println("加工过程中,我们采用""+proceed.getCook().getCookName()+""的先进工艺");
        }
    
        public void pack(Pack pack) {
            System.out.println("现在是在包装部");
            System.out.println("我们采用了"+pack.getCan().getName()+"进行了精心包装!");
        }
    }
    

      

    4、但是我们还需要一个指导者,为生成器注入原材料,并将生成器生成的对象进行拼接组装,为了方便扩展,当然也是先处理为一个接口,如下:

    //指导者接口
    public interface Director {
        //注入生成器对象
        public void setBuilder(Builder builder);
        //指导者进行将生成器生成的部分进行拼接
        public void proceed(Pretreatment pre,Proceed proceed,Pack pack);
    }
    

      

    5、指导者实现类如下:

    public class DirectorImpl1 implements Director{
    
        private Builder builder;
        //注入生成器对象
        public void setBuilder(Builder builder) {
            // TODO Auto-generated method stub
            this.builder = builder;
        }
    
        //将生成器部件按简单的顺序进行组装
        public void proceed(Pretreatment pre,Proceed proceed,Pack pack){
            System.out.println("开始!");
            builder.pretreatment(pre);
            builder.proceed(proceed);
            builder.pack(pack);
            System.out.println("结束!");
        }    
    }
    

      

    6、客户端实现如下:

    public class Client {
    
        public static void main(String[] args) {
            // 构建生成器
            Builder builder = new BuilderImpl1();
            // 通过生成器构建组织者
            DirectorImpl1 director = new DirectorImpl1();
            director.setBuilder(builder);
    
            // 简单组件
            Pretreatment pretreatment = new Pretreatment();
            pretreatment.setFruit(new Apple("红富士"));
            // 简单组件
            Proceed proceed = new Proceed();
            proceed.setCook(new Boil());
            // 简单组件
            Pack pack = new Pack();
            Can can = new Can();
            can.setName("铁制罐头");
            pack.setCan(can);
            // 组织者将简单组件注入到生成器中进行真正的组装,并将返回结果拼装。
            director.proceed(pretreatment, proceed, pack);
        }
    }
    

      

    最后结果:

    开始!
    现在是预处理部!
    精心选用上等红富士,剥皮,洗干净!
    现在是加工部!
    加工过程中,我们采用"祖传黑火焖煮"的先进工艺
    现在是在包装部
    我们采用了铁制罐头进行了精心包装!
    结束!

    为了避免繁琐,演示中许多简单的JavaBean被省略了,为了看的方便,类的包名、import等都被我删去了。

    Builder 模式实例应用

    1. 在 okHttp 中 Request属性

    Request 有6个属性,按照套路 构造方法持有一个 Builder ,在构造中将 builder 制造的组件赋值给 Request 完成构建,提供 newBuilder 用于重新获得 Builder 返厂重建

    final HttpUrl url;
    final String method;
    final Headers headers;
    final RequestBody body;
    final Object tag;
    
    private volatile CacheControl cacheControl; // Lazily initialized.
    
    Request(Builder builder) {
       this.url = builder.url;
       this.method = builder.method;
       this.headers = builder.headers.build();
       this.body = builder.body;
       this.tag = builder.tag != null ? builder.tag : this;
    }
    
    public Builder newBuilder() {
       return new Builder(this);
    }
    

    Builder 有两个构造,第一个空构造中初始化两个默认值。第二个构造持有 Request 用于重新构建 Builder 返厂重建。

    public Builder() {
          this.method = "GET";
          this.headers = new Headers.Builder();
    }
    
    Builder(Request request) {
          this.url = request.url;
          this.method = request.method;
          this.body = request.body;
          this.tag = request.tag;
          this.headers = request.headers.newBuilder();
    }
    

    剩下的就是一些属性初始化的方法,返回值为 Builder 方便链式调用。这里就列出一个方法,详细的请查看源码,最后调用 build() 方法 初始化 Request 传入 Builder 完成构建。 

    public Builder url(HttpUrl url) {
         if (url == null) throw new NullPointerException("url == null");
         this.url = url;
         return this;
    }
    ...此处省略部分方法
    public Request build() {
         if (url == null) throw new IllegalStateException("url == null");
         return new Request(this);
    }
    

    调用

    Request request = new Request.Builder()  
                    .url("http://www.youtube.com")  
                    .addHeader("header","header")  
                    .put("RequestBody")  
                    .build(); 
    

      

    全部源码

    public final class Request {  
      final HttpUrl url;  
      final String method;  
      final Headers headers;  
      final RequestBody body;  
      final Object tag;  
      
      Request(Builder builder) {  
        this.url = builder.url;  
        this.method = builder.method;  
        this.headers = builder.headers.build();  
        this.body = builder.body;  
        this.tag = builder.tag != null ? builder.tag : this;  
      }  
      
      public HttpUrl url() {  
        return url;  
      }  
      
      public String method() {  
        return method;  
      }  
      
      public Headers headers() {  
        return headers;  
      }  
      
      public String header(String name) {  
        return headers.get(name);  
      }  
      
      public List<String> headers(String name) {  
        return headers.values(name);  
      }  
      
      public RequestBody body() {  
        return body;  
      }  
      
      public Object tag() {  
        return tag;  
      }  
      
      public Builder newBuilder() {  
        return new Builder(this);  
      }  
      
        
      public CacheControl cacheControl() {  
        CacheControl result = cacheControl;  
        return result != null ? result : (cacheControl = CacheControl.parse(headers));  
      }  
      
      public boolean isHttps() {  
        return url.isHttps();  
      }  
      
      
      public static class Builder {  
        HttpUrl url;  
        String method;  
        Headers.Builder headers;  
        RequestBody body;  
        Object tag;  
      
        public Builder() {  
          this.method = "GET";  
          this.headers = new Headers.Builder();  
        }  
      
        Builder(Request request) {  
          this.url = request.url;  
          this.method = request.method;  
          this.body = request.body;  
          this.tag = request.tag;  
          this.headers = request.headers.newBuilder();  
        }  
      
        public Builder url(HttpUrl url) {  
          if (url == null) throw new NullPointerException("url == null");  
          this.url = url;  
          return this;  
        }  
      
      
        public Builder url(String url) {  
          if (url == null) throw new NullPointerException("url == null");  
      
          // Silently replace web socket URLs with HTTP URLs.  
          if (url.regionMatches(true, 0, "ws:", 0, 3)) {  
            url = "http:" + url.substring(3);  
          } else if (url.regionMatches(true, 0, "wss:", 0, 4)) {  
            url = "https:" + url.substring(4);  
          }  
      
          HttpUrl parsed = HttpUrl.parse(url);  
          if (parsed == null) throw new IllegalArgumentException("unexpected url: " + url);  
          return url(parsed);  
        }  
      
        public Builder url(URL url) {  
          if (url == null) throw new NullPointerException("url == null");  
          HttpUrl parsed = HttpUrl.get(url);  
          if (parsed == null) throw new IllegalArgumentException("unexpected url: " + url);  
          return url(parsed);  
        }  
      
        public Builder header(String name, String value) {  
          headers.set(name, value);  
          return this;  
        }  
      
        public Builder addHeader(String name, String value) {  
          headers.add(name, value);  
          return this;  
        }  
      
        public Builder removeHeader(String name) {  
          headers.removeAll(name);  
          return this;  
        }  
      
        public Builder headers(Headers headers) {  
          this.headers = headers.newBuilder();  
          return this;  
        }  
      
        public Builder cacheControl(CacheControl cacheControl) {  
          String value = cacheControl.toString();  
          if (value.isEmpty()) return removeHeader("Cache-Control");  
          return header("Cache-Control", value);  
        }  
      
        public Builder get() {  
          return method("GET", null);  
        }  
      
        public Builder head() {  
          return method("HEAD", null);  
        }  
      
        public Builder post(RequestBody body) {  
          return method("POST", body);  
        }  
      
        public Builder delete(RequestBody body) {  
          return method("DELETE", body);  
        }  
      
        public Builder delete() {  
          return delete(Util.EMPTY_REQUEST);  
        }  
      
        public Builder put(RequestBody body) {  
          return method("PUT", body);  
        }  
      
        public Builder patch(RequestBody body) {  
          return method("PATCH", body);  
        }  
      
        public Builder method(String method, RequestBody body) {  
          if (method == null) throw new NullPointerException("method == null");  
          if (method.length() == 0) throw new IllegalArgumentException("method.length() == 0");  
          if (body != null && !HttpMethod.permitsRequestBody(method)) {  
            throw new IllegalArgumentException("method " + method + " must not have a request body.");  
          }  
          if (body == null && HttpMethod.requiresRequestBody(method)) {  
            throw new IllegalArgumentException("method " + method + " must have a request body.");  
          }  
          this.method = method;  
          this.body = body;  
          return this;  
        }  
      
        public Builder tag(Object tag) {  
          this.tag = tag;  
          return this;  
        }  
      
      
        /*  
         * Request 对象创建器,想得到一个Request 对象必须使用build 方法,  
         * 在方法中增加对Builder参数的验证,并以异常的形式告诉给开发人员。  
         */           
        public Request build() {  
        /** 
         * 比如下面判断如果 url 是null的话就会抛出异常 
         */  
          if (url == null) throw new IllegalStateException("url == null");  
          return new Request(this);  
        }  
      }  
    } 
    View Code

     

  • 相关阅读:
    JAVA地址通过百度地图API转化为经纬度
    JS验证手机号
    JAVA获取六位随机数
    JS判断是否为“YYYYMMDD”式的日期
    JAVA 通过url下载图片保存到本地
    JAVA汉字转拼音
    JS判断是否是苹果系统(ios)和安卓系统(Android)客户端
    JAVA获取请求链接中所有参数(GET请求)
    如何高效学习 Kubernetes 知识图谱?
    当云原生遇到混合云:如何实现“求变”与“求稳”的平衡
  • 原文地址:https://www.cnblogs.com/JiangWJ/p/10801439.html
Copyright © 2020-2023  润新知