做晚饭与建造者模式

建造者模式是一个相对比较简单的设计模式,我们都知道建造者模式是用来创建对象的。接下来我们用做晚饭的例子来全面的了解一下建造者模式。

建造者模式

建造者设计模式可以使一个复杂对象的构造与它的表示分离,让同样的构造过程可以创建不同的表示。它将一个复杂的对象分解为多个简单的对象,然后一步一步构建而成。它将变与不变分离,即产品的组成部分是不变的,但是每一部分是可以灵活选择的

做晚饭

做晚饭是一个很麻烦的过程,需要考虑到有几个人吃饭、主食是是什么,谁来做,有没有汤,有没有其他菜等。所以是一个相对复杂的对象。就像下面这样:

/**
 * 一顿饭
 */
public class Meal {
    private String chef;
    private int userCount;
    private String mealType;
    private String stapleFood;
    private String soup;
    private List<String> dishs;

    public String getChef() {
        return chef;
    }

    public void setChef(String chef) {
        this.chef = chef;
    }

    public int getUserCount() {
        return userCount;
    }

    public void setUserCount(int userCount) {
        this.userCount = userCount;
    }

    public String getMealType() {
        return mealType;
    }

    public void setMealType(String mealType) {
        this.mealType = mealType;
    }

    public String getStapleFood() {
        return stapleFood;
    }

    public void setStapleFood(String stapleFood) {
        this.stapleFood = stapleFood;
    }

    public String getSoup() {
        return soup;
    }

    public void setSoup(String soup) {
        this.soup = soup;
    }

    public List<String> getDishs() {
        return dishs;
    }

    public void setDishs(List<String> dishs) {
        this.dishs = dishs;
    }

    @Override
    public String toString() {
        return "Meal{" +
                "厨师='" + chef + '\'' +
                ", 用餐人数=" + userCount +
                ", 类型='" + mealType + '\'' +
                ", 主食='" + stapleFood + '\'' +
                ", 汤='" + soup + '\'' +
                ", 菜品=" + dishs +
                '}';
    }
}

从上面的代码中可以看到,一顿饭有6个属性,而且有些属性是一定要有值的,同时有些属性是可以没有值的。这样的对象对于客户端来说简直是地狱,所以就有了建造者模式发挥的空间。

SimpleBuilder

一般来说我们都会使用这种builder来创建对象,简单易用,就像下面这样:

public class SimpleBuilder {
    private int userCount;
    private String mealType;
    private String stapleFood;
    private String chef;
    private String soup;
    private List<String> dishs;

    public SimpleBuilder(String chef,int userCount,String mealType,String stapleFood) {
        this.chef = chef;
        this.userCount = userCount;
        this.mealType = mealType;
        this.stapleFood = stapleFood;
    }

    public SimpleBuilder buildSoup(String soup) {
        this.soup = soup;
        return this;
    }

    public SimpleBuilder buildDish(String dish) {
        if (this.dishs == null) {
            this.dishs = new ArrayList<>();
        }
        this.dishs.add(dish);
        return this;
    }

    public Meal build(){
        Meal meal = new Meal();
        meal.setChef(chef);
        meal.setUserCount(userCount);
        meal.setSoup(soup);
        meal.setMealType(mealType);
        meal.setStapleFood(stapleFood);
        meal.setDishs(dishs);
        return meal;
    }
}

我们将Meal必须的属性放到SimpleBuilder的构造器中,这样可以保证属性一定会被设置上,非必须的属性放到builder的各个build方法上,最终调用builder.build()来或得一个完整的对象。就像下面这样:

public class BuilderMain {
    private static final Logger logger = LoggerFactory.getLogger(BuilderMain.class);
    public static void main(String[] args) {
        Meal meal = new SimpleBuilder("me",2, "晚饭", "面条")
                .buildSoup("紫菜汤").buildDish("麻婆豆腐")
                .build();
        logger.info("{}", meal);
    }
}

运行结果:
在这里插入图片描述
从结果中可以看出来,我们正常创建了Meal对象。

反思

上面的方式确实是使用建造者模式创建对象了,但是,如果我希望经常创建一个2人吃面条配麻婆豆腐和紫菜汤的晚饭,应该怎么做呢?难道我要到处写new SimpleBuilder("me",2, "晚饭", "面条") .buildSoup("紫菜汤").buildDish("麻婆豆腐").build();这样是不是也太不雅观了?

可复用的建造者

在建造者模式的官方定义中,一共有4个角色:

  1. builder:一个抽象的建造者
  2. concreateBuilder:一个实现了抽象builder的具体建造者
  3. director:使用抽象建造者的对象
  4. product:要创建的产品

接下来我们用这种思路重新实现一下上面的场景。

因为产品对象没有发生变化,所以产品就不再展示一次了。

抽象的建造者 BaseMealBuilder

/**
 * 基础的builder
 * @author skyline
 */
public abstract class BaseMealBuilder {
    private int userCount;
    private String mealType;
    private String stapleFood;
    private String chef;
    private String soup;
    private List<String> dishs;

    /**
     * 创建厨师
     * @return
     */
    public BaseMealBuilder buildChef(){
        this.chef = getChef();
        return this;
    }

    /**
     * 创建用餐人数
     * @return
     */
    public BaseMealBuilder buildUserCount(){
        this.userCount = getUserCount();
        return this;
    }

    /**
     * 创建饭类型
     * @return
     */
    public BaseMealBuilder buildMealType(){
        this.mealType = getMealType();
        return this;
    }

    /**
     * 创建主食
     * @return
     */
    public BaseMealBuilder buildStapleFood(){
        this.stapleFood = getStapleFood();
        return this;
    }

    /**
     * 创建汤
     * @return
     */
    public BaseMealBuilder buildSoup(){
        this.soup = getSoup();
        return this;
    }

    /**
     * 创建菜品
     * @return
     */
    public BaseMealBuilder buildDishs(){
        this.dishs = getDishs();
        return this;
    }

    /**
     * 获取用餐人数
     * @return
     */
    protected abstract int getUserCount();

    /**
     * 获取饭类型
     * @return
     */
    protected abstract String getMealType();

    /**
     * 获取主食
     * @return
     */
    protected abstract String getStapleFood();

    /**
     * 获取厨师信息
     * @return
     */
    protected abstract String getChef();

    /**
     * 获取汤
     * @return
     */
    protected abstract String getSoup();

    /**
     * 获取菜品
     * @return
     */
    protected abstract List<String> getDishs();

    public final Meal build(){
        if(chef==null){
            throw new RuntimeException("厨师不能为空");
        }
        if(userCount==0){
            throw new RuntimeException("用餐人数不能是0");
        }
        if(mealType==null){
            throw new RuntimeException("饭类型不能为空");
        }
        if(stapleFood==null){
            throw new RuntimeException("主食不能为空");
        }
        Meal meal = new Meal();
        meal.setChef(chef);
        meal.setUserCount(userCount);
        meal.setSoup(soup);
        meal.setMealType(mealType);
        meal.setStapleFood(stapleFood);
        meal.setDishs(dishs);
        return meal;
    }
}

在抽象的建造者中,返回Meal具体属性的方法依旧是抽象的。但是build方法不是抽象的,同时,在build方法中校验了Meal所必须的参数是否都存在。

具体的建造者 FatherMealBuilder & MotherMealBuilder

接下来我们定义两个具体的建造者,一个是父亲做饭,另一个是母亲做饭。
父亲做饭的套路如下:

/**
 * 父亲做饭
 * @author skyline
 */
public class FatherMealBuilder extends BaseMealBuilder{

    @Override
    protected int getUserCount() {
        return 3;
    }

    @Override
    protected String getMealType() {
        return "晚餐";
    }

    @Override
    protected String getStapleFood() {
        return "饼";
    }

    @Override
    protected String getChef() {
        return "父亲";
    }

    @Override
    protected String getSoup() {
        return "丸子汤";
    }

    @Override
    protected List<String> getDishs() {
        List<String> dishs = new ArrayList<>();
        dishs.add("鸡蛋西红柿");
        return dishs;
    }
}

然后是母亲做饭的套路:

/**
 * 母亲做饭
 * @author skyline
 */
public class MotherMealBuilder extends BaseMealBuilder{

    @Override
    protected int getUserCount() {
        return 3;
    }

    @Override
    protected String getMealType() {
        return "晚餐";
    }

    @Override
    protected String getStapleFood() {
        return "米饭";
    }

    @Override
    protected String getChef() {
        return "母亲";
    }

    @Override
    protected String getSoup() {
        return "鸡蛋汤";
    }

    @Override
    protected List<String> getDishs() {
        List<String> dishs = new ArrayList<>();
        dishs.add("肉末豆角");
        dishs.add("酸辣土豆丝");
        return dishs;
    }
}

指挥者 Baby

小baby要吃饭了,那么是父亲来做,还是母亲来做呢?所以Baby只依赖抽象的builder而不依赖具体的builder

/**
 * 小宝宝
 * @author skyline
 */
public class Baby {
    private BaseMealBuilder mealBuilder;
    public void wantEat(BaseMealBuilder mealBuilder){
        mealBuilder.buildChef()
                .buildDishs()
                .buildMealType()
                .buildSoup()
                .buildStapleFood()
                .buildUserCount();
        this.mealBuilder = mealBuilder;
    }

    public Meal getMeal(){
        return this.mealBuilder.build();
    }
}

在Baby的wantEat方法中封装了创建Meal的步骤,在getMeal方法中返回创建好的Meal对象。

使用

因为引入了Baby,所以使用的方式也就发生了变化。

public class BuilderMain {
    public static void main(String[] args) {
        Baby baby = new Baby();
        for (int i = 0; i < 7; i++) {
            System.out.print("第"+(i+1)+"天");
            if (i%2==0) {
                baby.wantEat(new FatherMealBuilder());
            }else {
                baby.wantEat(new MotherMealBuilder());
            }
            System.out.println(baby.getMeal());
        }
    }
}

运行结果:
在这里插入图片描述
奇数天爸爸做饭,偶数天妈妈做饭,代码是不是也简洁美观了。

总结

  1. 使用SimpleBuilder的方式虽然也可以使用建造者模式创建对象,但是不利于复用。如果存在一定的builder复用的场景,还是使用完整的建造者模式比较好。
  2. builder依赖于product,如果product频繁变化,那就不适用于建造者模式。
  3. 建造者模式创建的对象应该是比较复杂的对象,如果是特别简单的对象,强行使用建造者模式只会增加程序的复杂度。
  4. 相比对抽象工厂/工厂方法,建造者模式更注重一步一步的创建一个复杂的对象,前者更关注一组产品/产品的创建,而不关心具体的步骤。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值