目的:
如果你的对象相对复杂,那么就应该考虑对象的构建和它的表现相分离。目的是为了同样的构建过程可以创建不同的表示。
在android中我们创建dialog的时候就是建造者模式的最好体现。
AlertDialog alertDialog = new AlertDialog.Builder(getContext()).setView(new EditText(getContext())).create();
我们先来看看android源码中实现builder模式是如何实现的,这里我借鉴朋友的博客java设计模式——-Builder模式里面的一个构建复杂数据的例子来说明:
public class NutritionFacts {
private int servingSize = 0; // 食用份量
private int servings; // 份数
private int calories; // 卡路里
private int sodium; // 钠
private int fat; // 脂肪
private int carbohydrate; // 碳水化合物
public static class Builder {
private int servingSize = 0; // 食用份量
private int servings; // 份数
private int calories; // 卡路里
private int sodium; // 钠
private int fat; // 脂肪
private int carbohydrate; // 碳水化合物
public Builder(int servingSize, int servings) {
this.servingSize = servingSize;
this.servings = servings;
}
public Builder calories(int calories) {
this.calories = calories;
return this;
}
public Builder sodium(int sodium) {
this.servingSize = sodium;
return this;
}
public Builder fat(int fat) {
this.fat = fat;
return this;
}
public Builder carbohydrate(int carbohydrate) {
this.carbohydrate = carbohydrate;
return this;
}
public NutritionFacts show() {
return new NutritionFacts(this);
}
}
private NutritionFacts(Builder builder) {
servings = builder.servings;
servingSize = builder.servingSize;
calories = builder.calories;
carbohydrate = builder.carbohydrate;
fat = builder.fat;
sodium = builder.sodium;
}
}
这就是类似android源码中dialog的builder模式,我们用这种方式去创建一个复杂的数据结构是非常简单和方便的。
调用方法:
NutritionFacts nutritionFacts = new NutritionFacts
.Builder(20, 8)
.calories(20)
.carbohydrate(50)
.fat(60)
.show();
这个数据结构是不是很想android中new一个AlertDialog的写法?
我们去翻看AlertDialog源码你就会发现,其实是一模一样的写法。
这样得到的nutritionFacts实例你可以实现部分数据,也可以实现全部数据。
but!
如果你觉得这就是builder模式了,那你就太小看它的魅力了。
以上代码只是一个简单的示范,我要说的是builder模式的实现原理和工作方式。
在我看来以上这种builder模式是最简单的但并非最好的。
以上代码适合一个房子的建造,比如AlertDialog。如果你在建立一个AlertDialogA或者AlertDialogB。建立一个类似的房子的时候是不是需要重新写一个AlertDialogA或者AlertDialogB的代码呢?
所以,以上代码是有问题的。
第一,强耦合;
第二,必须声明静态;
第三,不符合开闭原则。
首相我们要搞清楚一个builder模式需要什么东西。
1,一个需要被建造出来的对象。比如房子。(dialog)
2,一个建造房子的工人。(builder)
3,指挥工程师如何建房子的工程师。比如指挥者。(director)
4,一个对象的建造,哪些是必须的,哪些是非必须的。就像一栋房子的房梁,墙体,房顶,这是必须的。但是贴瓷砖、建围墙、装修豪华这些是非必须的。
只要一个builder模式完成了这四个部分,那么你再造一栋房子,不用重复了,只需要在原来做过的这栋房子上稍加改造就行了。
具体看代码实现吧。
/** * 抽象建造者类,包含一个建造者必须建造的具体产品,还有一些制造产品的零件,零件不是必须的。 */
public abstract class Builder {
public abstract void buildPartA();//零件a
public abstract void buildPartB();//零件b
public abstract void buildPartC();//零件c
public abstract Product getPart();//得到由建造者建造的具体产品
}
/** * 建造者具体实现类 */
public class ConcreteBuilderA extends Builder {
private Product product = new Product();
@Override
public void buildPartA() {
product.addPart("这个产品必须的零件a");
Log.i("test", "制造零件a");
}
@Override
public void buildPartB() {
product.addPart("这个产品必须的零件b");
Log.i("test", "制造零件b");
}
@Override
public void buildPartC() {
product.addPart("这个产品必须的零件b");
Log.i("test", "制造零件c");
}
//返回一个制造出来的具体产品
@Override
public Product getPart() {
return product;
}
}
/** * 指挥者类 * 指挥者决定让建造者必须建造哪些零件 * 相当于指挥者对建造者说,房梁,墙体,房顶,必须建造 */
public class Director {
public void cunstruct(Builder builder) {
builder.buildPartA();//创建零件a
builder.buildPartB();//创建零件B
//builder.buildPartC();//创建零件C,这栋房子不需要零件c
}
}
/** * 产品类,由多个零件组成; * 一个产品可以添加很多的属性,这些属性不是必须的 * 就相当于一个基本完成的房子包含好多操作:刷墙,贴瓷砖,装修、建立围墙 * 这些操作不是必须的 */
public class Product {
List<String> parts = new ArrayList<>();
public void addPart(String part) {
parts.add(part);
}
public void getPartTile(String title) {//得到由建造者建造的名称
Log.i("test", "设置产品标题");
}
public void getPartMessage(String message) {//得到由建造者建造的简介
Log.i("test", "设置产品介绍");
}
public Button getPartButton(Button button) {//得到由建造者建造的操作按钮
return button;
}
//将这个产品类展示出来,房子完工
public void show() {
for (String part : parts) {
Log.i("test", part);
}
}
}
到这里。已经完工了。
1,抽象建造者控制必须建造的方法。(老板。这里的一切我说了算)
2,具体建造者(工人)执行建造。
3,指挥者(工程师)决定建造者建造哪些方法。
4,产品类。所有的老板,工人,工程师都是围绕着如何建立这个产品而生的。
我们来看看使用方法:
Director director = new Director();//实例化一个指挥者
Builder builder = new ConcreteBuilderA();//实例化一个建造者
director.cunstruct(builder);//指挥者告诉建造者你必须要建造哪几个类,必须创建的类是抽象建造者中包含的抽象方法,而指挥者告诉建造者,这部分的抽象方法必须实现,因为这是必不可少的。
Product product = builder.getPart();//具体要建造的产品,它其中有很多方法,这些方法可以全部实现,也可以部分实现
product.addPart("非必需零件a");
product.addPart("非必需零件b");
product.getPartMessage("这是一个dialog");
product.getPartTile("dialog的标题");
product.getPartButton(new Button(context));
product.show();
这种builder比较复杂,但是它的确符合设计模式的六大原则。
如果你用这中方式创建一个AlertDialog。那么你同样可以用这中方式创建一个AlertDialogA或者更多类似的产品。
我们只需要在抽象建造者类中控制所有必须方法。
然后再创建一个工程师,称之为建造AlertDialogA的工程师,它告诉BuilderA(AlertDialogA的工人)建造AlertDialogA的必须方法。
再创建一个工人,称之为AlertDialogA的工人。完成工程师交代的任务。
最后,拿出ProductA产品的蓝图。
test,开工!
代码没有重复,没有修改原来建造的AlertDialog,相反拓展了AlertDialogA的创建方式。
这才是一个合格的或者说最好的设计模式实现方法。
这里要说明的一点是,并非android源码中用到的设计模式就是最终形态。它也只是在适合的场景用到了适合的设计模式。
它代表的是最合适的场景+最合适的代码方式;并非最好的设计模式的具体体现。
认清真相很重要,做对很重要。