Android设计模式与最佳实践
上QQ阅读APP看书,第一时间看更新

2.5 应用建造者模式

建造者设计模式是最有用的创建型模式之一,因为它将较小的对象构建成较大的对象。这正是我们想要做的——从原料列表中构建出一个三明治对象。建造者模式还有一个更大的优势,就是以后可以很容易地增加可选功能。和以前一样,先要创建一个称为Ingredient的接口,并用它来表示Bread和Filling。这一次需要将热值表示为整数类型,因为我们需要计算成品三明治的总热量。

打开一个Android Studio项目或启动一个新项目,然后按照以下步骤创建一个基本的三明治建造者模式。

(1)创建一个名为Ingredient.java的新接口,用如下代码实现:

    public interface Ingredient {
        String name();
        int calories();
    }

(2)创建一个名为Bread的抽象类:

    public abstract class Bread implements Ingredient {
        @Override
        public abstract String name();
        @Override
        public abstract int calories();
    }

(3)创建一个一样的抽象类——Filling。

    (4)接下来,创建Bread的具体类:
    public class Bagel extends Bread {
        @Override
        public String name() {
            return "Bagel";
        }
        @Override
        public int calories() {
            return 250;
        }
    }

(5)对Filling类进行相同的操作。每种类型定义两个类,已经足够达到演示的目的:

    public class SmokedSalmon extends Filling {
        @Override
        public String name() {
            return "Smoked salmon";
        }
        @Override
        public int calories() {
            return 400;
        }
    }

(6)现在可以创建Sandwich类:

    public class Sandwich {
        private static final String DEBUG_TAG = "tag";
        //创建持有原料的列表
        private List<Ingredient> ingredients = new ArrayList<Ingredient>();
        //计算总热值
        public void getCalories() {
            int c = 0;
            for (Ingredient i : ingredients) {
                c+= i.calories();
            }
            Log.d(DEBUG_TAG, "Total calories : "+c+" kcal");
        }
        //添加原料
        public void addIngredient(Ingredient ingredient) {
            ingredients.add(ingredient);
        }
        //输出原料
        public void getSandwich() {
            for (Ingredient i : ingredients) {
                Log.d(DEBUG_TAG, i.name()+" : "+i.calories()+" kcal");
            }
        }
    }

(7)最后,创建SandwichBuilder类:

    public class SandwichBuilder {
        //现成的三明治
        public static Sandwich readyMade() {
            Sandwich sandwich = new Sandwich();
              sandwich.addIngredient(new Bagel());
              sandwich.addIngredient(new SmokedSalmon());
              sandwich.addIngredient(new CreamCheese());
              return sandwich;
            }
            //定制三明治
            public static Sandwich build(Sandwich s, Ingredient i) {
              s.addIngredient(i);
              return s;
            }
        }

建造者设计模式已经完成,至少目前如此,如图2-12所示。

图2-12

这里,我们为建造者提供了两个功能:返回现成的三明治和用户定制的三明治。现在还没有可使用的界面,但是我们可以通过客户端代码模拟用户的选择。

我们还将输出的职责委托给了Sandwich类,这通常是一个明智的选择,因为这有助于保持客户端代码整洁和清晰,正如下边的代码所示:

        //创建一个定制的三明治
        SandwichBuilder builder = new SandwichBuilder();
        Sandwich custom = new Sandwich();
        //模拟用户的选择
        custom = builder.build(custom, new Bun());
        custom = builder.build(custom, new CreamCheese());
        Log.d(DEBUG_TAG, "CUSTOMIZED");
        custom.getSandwich();
        custom.getCalories();
        //创建一个现成的三明治
        Sandwich offTheShelf = SandwichBuilder.readyMade();
        Log.d(DEBUG_TAG, "READY MADE");
        offTheShelf.getSandwich();
        offTheShelf.getCalories();

代码将产生如下输出:

    D/tag: CUSTOMIZED
    D/tag: Bun : 150 kcal
    D/tag: Cream cheese : 350 kcal
    D/tag: Total calories : 500 kcal
    D/tag: READY MADE
    D/tag: Bagel : 250 kcal
    D/tag: Smoked salmon : 400 kcal
    D/tag: Cream cheese : 350 kcal
    D/tag: Total calories : 1000 kcal

建造者最大的优点之一是非常容易添加、删除和修改具体类,甚至在修改接口或抽象类时,也不需要修改客户端源代码。这使得建造者模式成为最强大的模式之一,它可以在许多场景下使用。这并不是说,它总是比工厂模式更好。对于简单的对象,工厂模式通常是最好的选择。当然,模式存在于不同的尺度上,工厂模式嵌套在建造者模式中并不罕见,反之亦然。