一、基础知识:先前学习建造者模式的时候,总是以这个UML图作为学习基础资料
然后总是要记住四个角色
- 产品类:一般是一个较为复杂的对象,也就是说创建对象的过程比较复杂,一般会有比较多的代码量。在本类图中,产品类是一个具体的类,而非抽象类。实际编程中,产品类可以是由一个抽象类与它的不同实现组成,也可以是由多个抽象类与他们的实现组成。
- 抽象建造者:引入抽象建造者的目的,是为了将建造的具体过程交与它的子类来实现。这样更容易扩展。一般至少会有两个抽象方法,一个用来建造产品,一个是用来返回产品。
- 建造者:实现抽象类的所有未实现的方法,具体来说一般是两项任务:组建产品;返回组建好的产品。
- 导演类:负责调用适当的建造者来组建产品,导演类一般不与产品类发生依赖关系,与导演类直接交互的是建造者类。一般来说,导演类被用来封装程序中易变的部分。住四然后总是要记住四个角色
我们可以看到,建造者模式相对工厂方法模式,更加注重产品建造过程,个人认为这才是Builder的核心思想。
二、近日看了一些Builder的例子,再一次觉得设计模式很多时候是一种思想,而不是照搬UML图
例子1.
1 public class User { 2 private final String firstName; // required 3 private final String lastName; // required 4 private final int age; // optional 5 private final String phone; // optional 6 private final String address; // optional 7 8 private User(UserBuilder builder) { 9 this.firstName = builder.firstName; 10 this.lastName = builder.lastName; 11 this.age = builder.age; 12 this.phone = builder.phone; 13 this.address = builder.address; 14 } 15 16 public String getFirstName() { 17 return firstName; 18 } 19 20 public String getLastName() { 21 return lastName; 22 } 23 24 public int getAge() { 25 return age; 26 } 27 28 public String getPhone() { 29 return phone; 30 } 31 32 public String getAddress() { 33 return address; 34 } 35 36 public static class UserBuilder { 37 private final String firstName; 38 private final String lastName; 39 private int age; 40 private String phone; 41 private String address; 42 43 public UserBuilder(String firstName, String lastName) { 44 this.firstName = firstName; 45 this.lastName = lastName; 46 } 47 48 public UserBuilder age(int age) { 49 this.age = age; 50 return this; 51 } 52 53 public UserBuilder phone(String phone) { 54 this.phone = phone; 55 return this; 56 } 57 58 public UserBuilder address(String address) { 59 this.address = address; 60 return this; 61 } 62 63 public User build() { 64 return new User(this); 65 } 66 67 } 68 }
当我们试图创建一个user对象的时候
1 public User getUser() { 2 return new 3 User.UserBuilder('Jhon', 'Doe') 4 .age(30) 5 .phone('1234567') 6 .address('Fake address 1234') 7 .build(); 8 }
优点如下:
- User构造方法是私有的,这意味着该类不能在客户端代码里直接实例化。
- User所有属性都是final类型的,在构造方法里面被赋值。只提供了getter方法。
- builder类使用流式接口风格,让客户端代码阅读起来更容易(见getUser())。
- builder类构造方法只接收必须属性,为了确保这些属性在构造方法里赋值,只有这些属性被定义成final类型。
另外还有一个例子:
AlertDialog
1 public class AlertDialog extends Dialog implements DialogInterface { 2 3 ... 4 5 protected AlertDialog(Context context, int theme) { 6 this(context, theme, true); 7 } 8 9 AlertDialog(Context context, int theme, boolean createThemeContextWrapper) { 10 super(context, resolveDialogTheme(context, theme), createThemeContextWrapper); 11 12 mWindow.alwaysReadCloseOnTouchAttr(); 13 mAlert = new AlertController(getContext(), this, getWindow()); 14 } 15 16 protected AlertDialog(Context context, boolean cancelable, OnCancelListener cancelListener) { 17 super(context, resolveDialogTheme(context, 0)); 18 mWindow.alwaysReadCloseOnTouchAttr(); 19 setCancelable(cancelable); 20 setOnCancelListener(cancelListener); 21 mAlert = new AlertController(context, this, getWindow()); 22 } 23 24 public static class Builder { 25 private final AlertController.AlertParams P; 26 private int mTheme; 27 28 29 public Builder(Context context) { 30 this(context, resolveDialogTheme(context, 0)); 31 } 32 33 34 public Builder(Context context, int theme) { 35 P = new AlertController.AlertParams(new ContextThemeWrapper( 36 context, resolveDialogTheme(context, theme))); 37 mTheme = theme; 38 } 39 40 41 public Builder setMessage(CharSequence message) { 42 P.mMessage = message; 43 return this; 44 } 45 46 public AlertDialog create() { 47 final AlertDialog dialog = new AlertDialog(P.mContext, mTheme, false); 48 P.apply(dialog.mAlert); 49 dialog.setCancelable(P.mCancelable); 50 if (P.mCancelable) { 51 dialog.setCanceledOnTouchOutside(true); 52 } 53 dialog.setOnCancelListener(P.mOnCancelListener); 54 dialog.setOnDismissListener(P.mOnDismissListener); 55 if (P.mOnKeyListener != null) { 56 dialog.setOnKeyListener(P.mOnKeyListener); 57 } 58 return dialog; 59 } 60 ... 61 } 62 }
我们可以看到:AlertDialog的Build是一个静态内部类。AlertDialog设置的属性会暂时保存在Build类的成员变量P(AlertController.AlertParams)中。同时,我们注意到我们设置的属性(如setMessage()),它都回返回本身的AlertBuild对象,这样我们就可以不停地调用它设置的方法(流式接口风格)。
如果我们想获得这个AlertDialog。我们就需要调用建造者的create()方法,在create()方法里面它就会构造出一个Dialog实例,并且将我们刚才设置的属性全部赋给AlertDialog,最后返回AlertDialog的实例
三、总结
从上面的UserBuilder和AlertDialog的例子,我们可以做一些总结。Builder设计模式的适用点:
1.产品的属性较多
2.产品本身不可变——Build后,不建议修改产品。
3.产品属性中有部分非必须的属性。
以上三点恰好符合“注重产品创建过程”的Builder核心思想。
参考资料:
http://www.importnew.com/11506.html 建造者模式实践
http://my.oschina.net/weiCloudS/blog/392872?fromerr=AIX2qbD2 在Android中探秘建造者模式