创建模式

建造者模式:是将一个复杂的对象的构建与它的表示(实体)分离,使得同样的构建过程可以创建不同的表示。
 
写在前面:
  builder 模式可以分为两种实现,一种是用静态内部类实现,不需要 director 类;另一种是需要 director 类。网上的介绍往往都是带有 director 的,但实际使用时,完全可以省去 director,把构造逻辑放在 静态内部类的 build 方法中,这样把创建对象的逻辑放在一个类中,更易于代码阅读。而且,第一种方式的实现往往会结合链式调用的写法,提高代码的可读性。
 
1. 初识建造者模式
     顾名思义,builder的意思是建造者或建筑工人。建造者模式的好处就是保证了建造流程不会发生变化,流程既不会增加,也不会遗漏或者产生流程次序的错误,这是非常重要的。
举个例子:盖楼要遵循一个严格的流程,不然就会出现楼歪歪事件,而因为每个步骤具体的实现有所不同,所以盖出的楼房也不同。
     几个角色:
          1. Builder: 抽象建造者,给出一个抽象接口,以规范产品对象的各个组成成分的建造。这个接口规定要实现复杂对象的哪些部分的创建,并不涉及具体的对象部件的创建。
          2. ConcreteBuilder: 具体建造者,实现Builder接口,针对不同的商业逻辑,具体化复杂对象的各部分的创建。 在建造过程完成后,提供产品的实例。
          3. Director: 导演,调用具体建造者来创建复杂对象的各个部分,在指导者中不涉及具体产品的信息,只负责保证对象各部分完整创建或按某种顺序创建。与导演类直接交互的是建造者类。一般来说,导演类被用来封装程序中易变的部分。
          4. Product: 产品,要创建的复杂对象,也就是说创建对象的过程比较复杂,一般会有比较多的代码量。
    结构图:
         
2. 使用场合和好处
     使用建造模式的场合:
          1.创建一些复杂的对象时,这些对象的内部组成构件间的建造顺序是稳定的,但是对象的内部组成构件面临着复杂的变化。
          2.要创建的复杂对象的算法,独立于该对象的组成部分,也独立于组成部分的装配方法时。
     使用建造者模式的好处:
          1.封装很好,使用建造者模式可以有效封装变化,在使用建造者模式的场景中,一般产品类和建造者类是比较稳定的,因此,将主要业务逻辑封装在导演类中对整体而言可以取得比较好的稳定性。
          2.具体的建造者类之间是相互独立的,对系统的扩展非常有利。
          3.由于具体的建造者是独立的,因此可以对建造过程逐步细化,而不对其他的模块产生任何影响。
3. 与工厂模式相比
          我们可以看到,建造者模式与工厂模式是极为相似的,总体上,建造者模式仅仅只比工厂模式多了一个“导演类”的角色。在建造者模式的类图中,假如把这个导演类看做是最终调用的客户端,那么图中剩余的部分就可以看作是一个简单的工厂模式了。
          与工厂模式相比,建造者模式一般用来创建更为复杂的对象,因为对象的创建过程更为复杂,因此将对象的创建过程独立出来组成一个新的类——导演类。也就是说,工厂模式是将对象的全部创建过程封装在工厂类中,由工厂类向客户端提供最终的产品;而建造者模式中,建造者类一般只提供产品类中各个组件的建造,而将具体建造过程交付给导演类。由导演类负责将各个组件按照特定的规则组建为产品,然后将组建好的产品交付给客户端。
          建造者模式与工厂模式类似,适用的场景也很相似。一般来说,如果产品的建造很复杂,那么请用工厂模式;如果产品的建造更复杂,那么请用建造者模式。
 
4. 例子
  1. class Product {  
  2.     private String name;  
  3.     private String type;  
  4.     public void showProduct(){  
  5.         System.out.println("名称:"+name);  
  6.         System.out.println("型号:"+type);  
  7.     }  
  8.     public void setName(String name) {  
  9.         this.name = name;  
  10.     }  
  11.     public void setType(String type) {  
  12.         this.type = type;  
  13.     }  
  14. }  
  15.   
  16. abstract class Builder {  
  17.     public abstract void setPart(String arg1, String arg2);  
  18.     public abstract Product getProduct();  
  19. }  
  20. class ConcreteBuilder extends Builder {  
  21.     private Product product = new Product();  
  22.       
  23.     public Product getProduct() {  
  24.         return product;  
  25.     }  
  26.   
  27.     public void setPart(String arg1, String arg2) {  
  28.         product.setName(arg1);  
  29.         product.setType(arg2);  
  30.     }  
  31. }  
  32.   
  33. public class Director {  
  34.     private Builder builder = new ConcreteBuilder();  
  35.     public Product getAProduct(){  
  36.         builder.setPart("宝马汽车","X7");  
  37.         return builder.getProduct();  
  38.     }  
  39.     public Product getBProduct(){  
  40.         builder.setPart("奥迪汽车","Q5");  
  41.         return builder.getProduct();  
  42.     }  
  43. }  
  44. public class Client {  
  45.     public static void main(String[] args){  
  46.         Director director = new Director();  
  47.         Product product1 = director.getAProduct();  
  48.         product1.showProduct();  
  49.   
  50.         Product product2 = director.getBProduct();  
  51.         product2.showProduct();  
  52.     }  
  53. }  
 
这段代码在Director中设计了两个getProduct的方法,可以设计成一个getProduct(Builder builder),这样只要创建director对象时给出一个具体的builder,就能够得对应的product。下一个例子比较标准,具体构造者类中成员是产品,且实现getResult方法,director实现Construct方法
 
// Builder pattern -- Structural example  
using System;
using System.Collections;
// "Director"
class Director
{
  // Methods
  public void Construct( Builder builder )
  {
    builder.BuildPartA();
    builder.BuildPartB();
  }
}
 
在构造函数里设计build过程,getResult可以放在ConcreteBuilder里
// "Builder"
abstract class Builder
{
  // Methods
  abstract public void BuildPartA();
  abstract public void BuildPartB();
  abstract public Product GetResult();
}
// "ConcreteBuilder1"
class ConcreteBuilder1 : Builder
{
  // Fields
  private Product product; 
  // Methods
  override public void BuildPartA()
  {
    product = new Product();
    product.Add( "PartA" );
  }
  override public void BuildPartB()
  {
    product.Add( "PartB" );
  }
  override public Product GetResult()
  {
    return product;
  }
}
// "ConcreteBuilder2"
class ConcreteBuilder2 : Builder
{
  // Fields
  private Product product;
  // Methods
  override public void BuildPartA()
  {
    product = new Product();
    product.Add( "PartX" );
  }
  override public void BuildPartB()
  {
    product.Add( "PartY" );
  }
  override public Product GetResult()
  {
    return product;
  }
}
// "Product"
class Product
{
  // Fields
  ArrayList parts = new ArrayList();
 
  // Methods
  public void Add( string part )
  {
    parts.Add( part );
  }
  public void Show()
  {
    Console.WriteLine( " Product Parts -------" );
    foreach( string part in parts )
      Console.WriteLine( part );
  }
}
/// <summary>
/// Client test
/// </summary>
public class Client
{
  public static void Main( string[] args )
  {
    // Create director and builders
    Director director = new Director( );
    Builder b1 = new ConcreteBuilder1();
    Builder b2 = new ConcreteBuilder2();
    // Construct two products
    director.Construct( b1 );
    Product p1 = b1.GetResult();
    p1.Show();
    director.Construct( b2 );
    Product p2 = b2.GetResult();
    p2.Show();
  }
}
 
假设要组装一辆自行车,并且自行车就是车轮和车架组成,自行车有永久牌和凤凰牌
Builder对应于组装自行车所使用的车轮和车架
ConcreteBuiler1对应于永久牌车轮和车架,同时可以返回一辆永久牌自行车,ConcreteBuiler2对应于凤凰牌车轮和车架,同时可以返回一辆凤凰牌自行车
Product对应于自行车
Director表示组装过程
此时我们再来理解下面这句话:“在软件系统中,有时候面临着“一个复杂对象”的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法确相对稳定。”
自行车就是“一个复杂对象”,它有车轮和车架组成,但不管车轮和车架这两个部件怎么变化,生产一辆自行车的过程是不会变的,即组装过程是不会变的。
 
这里可以看到,一个Builder对象就对应了一个Product,这是不是它的局限性?
 
对 Builder进行抽象,Builder接口只是定义了构建各个部件需要的方法,而具体的构建顺序是由 Director 来决定的,一个 Director 如果传入了一个 Builder,这个 Builder 里自带了一个产品对象,而最后会返回 build 过程后的产品对象。和工厂方法不一样,工厂方法是在调用 create 时才创建并返回对象,而建造模式在Builder 确定时,产品对象就已经存在了,如果想要再要新的产品对象,需要新的 Builder 对象。
 
Builder 这种模式会不会有线程安全的问题?
 
抛开上面这些,Builder 设计模式可能会有两种理解。
 
第一种,就是上面所述的,一个产品有多个部分组成,builder 来实现具体的几个部分,然后这就是一个完整的产品,抽象的 Builder 接口用于规范(指定)几个要创建部件的方法,具体的 Builder 实现,就能创建具体的部件,除了 Builder 以外,还需要一个 Director,这个 Director 持有一个 Builder 对象,而 Builder 对象持有一个 商品对象。这种方式,可以看到 Builder 和 Product 是一一对应的,当然,如果想制造一系列某些属性固定的 Product, 可以在 Builder 处做文章。但是一个 Builder 只能生产 一个 Product,这是不太好的。
 
另一种,链式创建,没有 Director了, Builder 存在于Product内,是一个静态内部类,用于保存参数,在 build 时,new Product 并返回,而 new Product 调用构造函数是以 Builder 为参数的,这样,可以用一个Builder 不停的生产 Product。此外,new Builder 时,可以指定一些属性不被修改,而提供一些方法可以动态修改属性,这样能够控制哪些属性不被修改,功能强大。在创建复杂对象时,采用这种方法再合适不过。
 
 
注意:
 
Builder 本身不是线程安全的,最好在创建完对象后做必要的参数检查
 
这篇讲的比较明了
 
 
Builder 是用于构建复杂对象的一种 创建类型的设计模式,这种模式的意义在于简单灵活的创建,体现在两个方面,一方面,可以指定固定的创建流程,能创建不同的产品,(这就是原始而又经典的建造者模式);另一方面,可以简化创建流程和代码,控制可变与不可变,甚至可以 类似工厂形式的创建,比工厂灵活,代码可读性好。这两种是完全两种不同的实现,功能也不同,方法也不同,但是因为创建过程被拆分到不同的方法里,因而都会有线程安全的问题。
 
一个好的 Builder 实现,是保存配置的,而不是在 set 时赋给 Product 对象,把对象完整的构建过程都放在 build 过程里,无论是依赖于 Director,还是只用 build,这样做的好处就是延迟对象的创建,也易于构建不同的对象。
 
此外,builder 模式通常与 享元模式 结合使用,这样也会避免 builder 本身线程不安全的问题,因为只有一个线程可以获取到这个 builder,当然享元模式也可以与任何其他的创建模式结合使用。
原文地址:https://www.cnblogs.com/43726581Gavin/p/9043923.html