Android 内功心法(1.10)——android常用设计模式之建造者(Builder)模式

目的:
如果你的对象相对复杂,那么就应该考虑对象的构建和它的表现相分离。目的是为了同样的构建过程可以创建不同的表示。

在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源码中用到的设计模式就是最终形态。它也只是在适合的场景用到了适合的设计模式。

它代表的是最合适的场景+最合适的代码方式;并非最好的设计模式的具体体现。

认清真相很重要,做对很重要。

你可能感兴趣的:(java,设计模式,android,builder,创建者模式)