Builder模式在Android中的应用

盘点Builder模式在Android中的应用。

Builder Pattern Review

Builder模式用来将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

值得注意的是,这个构建的过程是统一的、固定不变的,变化的部分放到生成器部分了,只要配置不同的生成器,那么同样的构建过程,就能构建出不同的产品来。

Pattern架构

  • Builder:生成器接口,定义创建一个Product对象所需要的各个部件的操作。
  • ConcreteBuilder:具体的生成器实现,实现各个部件的创建,并负责组装Product对象的各个部件,同时还提供一个让用户获取组装完成后的产品对象的方法。
  • Director:指导者,也被称导向者,主要用来使用Builder接口,以一个统一的过程来构建所需要的Product对象。
  • Product:产品,表示被生成器构建的复杂对象,包含多个部件。

生成器模式的优点

  • 松散耦合: 生成器模式可以用同一个构建算法构建出表现上完全不同的产品,实现产品构建和产品表现上的分离。生成器模式正是把产品构建的过程独立出来,使它和具体产品的表现分松散耦合,从而使得构建算法可以复用,而具体产品表现也可以很灵活地、方便地扩展和切换。

  • 可以很容易的改变产品的内部表示: 在生成器模式中,由于Builder对象只是提供接口给Director使用,那么具体部件创建和装配方式是被Builder接口隐藏了的,Director并不知道这些具体的实现细节。这样一来,要想改变产品的内部表示,只需要切换Builder接口的具体实现即可,不用管Director,因此变得很容易。

  • 更好的复用性: 生成器模式很好的实现构建算法和具体产品实现的分离。这样一来,使得构建产品的算法可以复用。同样的道理,具体产品的实现也可以复用,同一个产品的实现,可以配合不同的构建算法使用。

该在什么场景下用Builder?

当构造一个对象需要很多参数的时候,并且参数的个数或者类型不固定的时候,Builder是一个很好的选择。例如考虑下面这个例子:

http://stackoverflow.com/questions/328496/when-would-you-use-the-builder-pattern

对于这样的类,应该用哪种构造器或者静态方法来编写呢?通常的方法是采用重叠构造器模式(telescoping constructor)。在这种模式下,你提供第一个挚友必要参数的构造器,第二个构造函数加一个可选参数,第二个加两个。。。以此类推。最后一个构造函数包含所有的可选参数。

Pizza(int size) { ... }        
Pizza(int size, boolean cheese) { ... }    
Pizza(int size, boolean cheese, boolean pepperoni) { ... }    
Pizza(int size, boolean cheese, boolean pepperoni, boolean bacon) { ... }

当参数比较多的时候,这种方法会显得很乱,而且一不小心弄反两个参数的顺序,而这两个参数巧合类型相同,那么产生的错误很难发现。一种可以替代的方法是采用JavaBean模式。调用一个无参数的构造来创建对象,然后调用setter函数来设置必要的参数:

Pizza pizza = new Pizza(12);
pizza.setCheese(true);
pizza.setPepperoni(true);
pizza.setBacon(true);

但是这种方法也有问题:由于构造过程分在了几个调用中,在构造过程中JavaBean可能处于不一致的状态,类无法仅仅通过检验构造器参数的有效性来保证一致性。

幸运的是,我们有第三种方法 -- Builder:

public class Pizza {
  private int size;
  private boolean cheese;
   private boolean pepperoni;
   private boolean bacon;

    public static class Builder {
       //required
      private final int size;

      //optional
       private boolean cheese = false;
      private boolean pepperoni = false;
         private boolean bacon = false;

      public Builder(int size) {
         this.size = size;
         }

      public Builder cheese(boolean value) {
            cheese = value;
            return this;
      }

      public Builder pepperoni(boolean value) {
            pepperoni = value;
            return this;
      }

      public Builder bacon(boolean value) {
            bacon = value;
            return this;
      }

         public Pizza build() {
          return new Pizza(this);
         }
  }

  private Pizza(Builder builder) {
      size = builder.size;
      cheese = builder.cheese;
         pepperoni = builder.pepperoni;
      bacon = builder.bacon;
     }
}

Note that Pizza is immutable and that parameter values are all in a single location. Because the Builder’s setter methods return the Builder object they are able to be chained.

现在Pizza变得immutable,因为所有的参数都存放在了一个单独的地方builder之中,并且Builder所有的setter方法都返回一个Builder对象,这样我们就可以进行链式调用了,如下:

Pizza pizza = new Pizza.Builder(12)
                   .cheese(true)
                   .pepperoni(true)
                   .bacon(true)
                   .build();

这样的代码变的易写易读,而且容易理解。而且这种模式非常灵活,可以很简单的应对参数的增减。

Builder模式在Android中的应用

翻看Android系统的源码,会发现Builder模式出现了很多次。比较典型的一个就是我们常用的AlertDialog.Builder, 其用法如下:

AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(
context);

// set title
alertDialogBuilder.setTitle("Your Title");

// set dialog message
alertDialogBuilder
    .setMessage("Click yes to exit!")
    .setCancelable(false)
    .setPositiveButton("Yes",new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog,int id) {
            // if this button is clicked, close
            // current activity
            MainActivity.this.finish();
        }
      })
    .setNegativeButton("No",new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog,int id) {
            // if this button is clicked, just close
            // the dialog box and do nothing
            dialog.cancel();
        }
    });

    // create alert dialog
    AlertDialog alertDialog = alertDialogBuilder.create();

    // show it
    alertDialog.show();
 }
});

进一步查看AlertDialog的源码,会发现其实现方式和我们上面用Builder模式实现的Pizza类完全一致。

另外,Android源码中,类似的Builder类还有很多,比如:

Uri.Builder; 
Notification.Builder;
ContentProviderOperation.Builder;

等等,有兴趣的话可以去翻看源码。