Java设计模式-构建者Builder模式

bohp2357 6年前
   <p>构建者Builder模式是Java中十分常见的一种设计模式,先看一下GOF设计模式一书中给它的定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。</p>    <p>构造者Builder模式类图如下:</p>    <p><img src="https://simg.open-open.com/show/d6982e4ed0445908966d09f88c55290a.jpg"></p>    <p>一般而言,Builder模式有四个组成部分:</p>    <p>Builder为创建一个Product对象的各个部件指定抽象接口。</p>    <p>ConcreteBuilder实现Builder的接口以构造和装配该产品的各个部件、定义并明确它所创建的表示、提供一个检索产品的接口。</p>    <p>Director构造一个使用Builder接口的对象。</p>    <p>Product表示被构造的复杂对象。ConcreteBuilder创建该产品的内部表示并定义它的装配过程。包含定义组成部件的类,包括将这些部件装配成最终产品的接口。</p>    <h3>Builder模式示例</h3>    <p>下面通过一个简单的示例介绍Builder模式。</p>    <p>首先创建一个Product产品类。</p>    <pre>  <code class="language-java">public class Product {      private String header;   private String body;   private String footer;      public void setHeader(String header) {   this.header = header;   }      public void setBody(String body) {   this.body = body;   }      public void setFooter(String footer) {   this.footer = footer;   }      public String toString() {   return "Product [header=" + header + ", body=" + body + ", footer="   + footer + "]";   }  }  </code></pre>    <p>其次创建Builder接口。</p>    <pre>  <code class="language-java">public interface Builder {      void buildHeader();      void buildBody();      void buildFooter();      Product build();     }  </code></pre>    <p>然后创建一个ConcreteBuilder 类,即实现Builder接口。</p>    <pre>  <code class="language-java">public class ConcreteBuilderA implements Builder {      private Product product;      public ConcreteBuilderA() {   product = new Product();   }      public void buildHeader() {   product.setHeader("Header_A");   }      public void buildBody() {   product.setBody("Body_A");   }      public void buildFooter() {   product.setFooter("Footer_A");   }      public Product build() {   return product;   }     }  </code></pre>    <p>最后创建一个Director类。</p>    <pre>  <code class="language-java">public class Director {      private Builder builder;      public Director(Builder builder) {   this.builder = builder;   }      public void buildProduct() {   this.builder.buildHeader();   this.builder.buildBody();   this.builder.buildFooter();   }      public Product build() {   return this.builder.getProduct();   }     }  </code></pre>    <p>接下来测试一下Builder模式示例。</p>    <pre>  <code class="language-java">Builder builderA = new ConcreteBuilderA();     Director director = new Director(builderA);  director.buildProduct();  Product product = director.build();  System.out.println(product.toString());  //Product [header=Header_A, body=Body_A, footer=Footer_A]  </code></pre>    <p>这样我们也可以通过继承Builder接口,接下来创建ProductB、ProductC等等,甚至有一天可以创建一个ProductX,上述代码调用顺序以及逻辑几乎不需要做大的变动,只需要再扩展一个ConcreteBuilder即可。</p>    <h3>Builder模式特点</h3>    <p>它使你可以改变一个产品的内部表示。Builder对象提供给Director一个构造产品的抽象接口。该接口使得生成器可以隐藏这个产品的表示和内部结构。它同时也隐藏了该产品是如何装配的。因为产品是通过抽象接口构造的,你在改变该产品的内部表示时所要做的只是定义一个新的生成器。</p>    <p>它将构造代码和表示代码分开。Builder模式通过封装一个复杂对象的创建和表示方式提高了对象的模块性。客户不需要知道定义产品内部结构的类的所有信息;这些类是不出现在Builder接口中的。每个ConcreteBuilder包含了创建和装配一个特定产品的所有代码。这些代码只需要写一次;然后不同的Director可以复用它以在相同部件集合的基础上构作不同的Product。</p>    <p>它可对构造过程进行更精细的控制。Builder模式与一下子就生成产品的创建型模式不同,它是在Director的控制下一步一步构造产品的。仅当该产品完成时Director才从生成器中取回它。因此Builder接口相比其他创建型模式能更好的反映产品的构造过程。这使可以更精细的控制构建过程,从而能更精细的控制所得产品的内部结构。</p>    <p>通常有一个抽象的Builder类为Director可能要求创建的每一个构件定义一个操作。这些操作缺省情况下什么都不做。一个ConcreteBuilder类对它有兴趣创建的构件重定义这些操作。</p>    <h3>Builder模式的变体</h3>    <p>如果ConcreteBuilder只需要一个,只是为了个性化设置Product中的每个属性,那么Director也同样失去了它的意义了。既然ConcreteBuilder只有一个实现者,这时候我们的Builder设置为接口就有些冗余了,我们可将Builder设置为Product的一个内部类。为了更安全的考虑,Product中每个属性外界都必须通过Builder设置才可以,这种情况就需要将Product属性设置为final属性,然后通过私有构造方法设置。z最后一切从简,将创建Product的方法buildProduct()也剔除了,让Builder中的属性设置方法都返回一个Builder类型,直接return它自己即可,在创建方法build中通过链式调用直接返回一个Product对象。</p>    <p>下面是示例源代码:</p>    <pre>  <code class="language-java">public class Product {      private final String header;   private final String body;   private final String footer;      private Product(Builder builder) {   this.header = builder.header;   this.body = builder.body;   this.footer = builder.footer;   }      public static class Builder {   private String header;   private String body;   private String footer;      public Builder setHeader(String header) {   this.header = header;   return this;   }      public Builder setBody(String body) {   this.body = body;   return this;   }      public Builder setFooter(String footer) {   this.footer = footer;   return this;   }      public Product build() {   return new Product(this);   }      }      public String toString() {   return "Product [header=" + header + ", body=" + body + ", footer="   + footer + "]";   }  }  </code></pre>    <p>在使用的时候也相当方面,如下所示:</p>    <pre>  <code class="language-java">Product product = new Product.Builder()   .setHeader("Header")   .setBody("Body")   .setFooter("Footer")   .build();     System.out.println(product.toString());  //Product [header=Header, body=Body, footer=Footer]  </code></pre>    <p>这种写法在Android开发中很常见,例如AlertDialog的创建方式如下:</p>    <pre>  <code class="language-java">AlertDialog.Builder builder=new AlertDialog.Builder(context);  builder.setIcon(R.drawable.icon);  builder.setTitle("Title");  builder.setMessage("Message");  ...  builder.create().show();  </code></pre>    <p>还有一些比较常用的框架都是采用的这种变体Builder模式,如okhttp中创建Request,以及retrofit框架中创建一个Retrofit对象。</p>    <pre>  <code class="language-java">Request request = new Request.Builder()          .url("url")          .build();  Retrofit retrofit = new Retrofit.Builder()                  .baseUrl("url")                  .addConverterFactory(GsonConverterFactory.create())                  .build();  </code></pre>    <p>还有比较常用的加载图片的框架如Universal-Image-Loader,这里代码就不再贴出来了。</p>    <p>这种Builder模式,也有一个缺点就是一旦调用build()方法构造一个实体之后,就不能再更改其它属性了,因为build()方法返回的是Product,这本身是一种保护机制,也是Builder模式的特性。但是在实际开发中必须考虑一种情况,就是即使已经返回了Product还是需要重新设置一个新的属性值,有些开发者可能说了,再重新Builder一次即可,这种方式也未尝不可以。可是考虑到代码的复用性上面,前面一个链式调用几十个属性值返回了一个Product,而现在需要更改的仅仅是一个或者极少几个属性就可以了获取所需要的新Product了,作为软件开发人员这时候不免不会产生疑问,还有其它更好地方式吗?那么,答案是肯定的,从使用的框架中也可以一探究竟。</p>    <p>在OkHttp中就大量使用了这种方式,如Request类中:</p>    <pre>  <code class="language-java">public Builder newBuilder() {   return new Builder(this);  }  Builder(Request request) {    this.url = request.url;    this.method = request.method;    this.body = request.body;    this.tag = request.tag;    this.headers = request.headers.newBuilder();  }  </code></pre>    <p>通过这种方式我们又可以拿到Buidler对象了,而且以前设置的属性也都被重新设置进了当前返回的Builder对象中了,这样就又可以重新设置部分属性了。</p>    <p>实际上在Android Studio中已经有人提供了插件模板进行Builder创建了。在插件中搜索builder,然后选中InnerBuilder插件,下载安装即可。</p>    <p><img src="https://simg.open-open.com/show/033c1e63a0b0f207d5e4c50cf3503f09.png"></p>    <p>Builder设计模式因其简洁且封装性高在Android开发当中使用频率非常的高。Factory工厂模式与Builder模式相似,因为它也可以创建复杂对象。主要的区别是Builder模式着重于一步步构造一个复杂对象。而Factory着重于多个系列的产品对象(简单的或是复杂的)。Builder在最后的一步返回产品,而对于Factory工厂来说,产品是立即返回的。</p>    <p> </p>    <p>来自:http://www.sunnyang.com/754.html</p>    <p> </p>