初学 Java 设计模式(四):实战建造者模式 「单人年夜饭套餐」

一、建造者模式介绍

1. 解决的问题

主要解决在软件系统中,有时候面临着"一个复杂对象"的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。

2. 定义

建造者模式是一种创建型设计模式,能够分步骤创建复杂对象。该模式允许使用相同的创建代码生成不同类型和形式的对象。

建造者模式应用于一些基本组件不变,而其组合经常变化时,此时可以将变与不变分离开。由建造者创建和提供实例,主管者管理建造出来的实例的依赖关系。

二、建造者模式优缺点

1. 优点

  • 可以分步创建对象,暂缓创建步骤或递归运行创建步骤。
  • 生成不同形式的产品时,你可以复用相同的制造代码。
  • 单一职责原则:可以将复杂构造代码从产品的业务逻辑中分离出来。

2. 缺点

  • 由于采用该模式需要新增多个类,因此代码整体复杂程度会有所增加。

三、建造者模式应用实例:单人年夜饭套餐

1. 实例场景

今年很多人都响应了就地过年的倡议,但异地过年不心酸,我们可以点一份单人年夜饭套餐来犒劳异地过年的自己!

单人多肉年夜饭套餐:

  • 凉菜类:水煮花生
  • 热菜类:宫保鸡丁、农家小炒肉、地锅鸡、土家红烧肉
  • 主食类:米饭
  • 饮品:崂山啤酒

单人混合肉蔬年夜饭套餐:

  • 凉菜类:油炸花生米
  • 热菜类:木须肉、椒盐里脊、手撕包菜、地三鲜
  • 主食类:米饭
  • 饮品:夺命大乌苏

2. 建造者模式实现

2.1 工程结构
builder-pattern
└─ src
    ├─ main
    │    └─ java
    │    └─ org.design.pattern.builder
    │       ├─ model
    │       │  └─ cold
    │       │  │    ├─ BoiledPeanuts.java
    │       │  │    └─ FriedPeanuts.java
    │       │  └─ hot
    │       │  │    ├─ KungPaoChicken.java
    │       │  │    ├─ FarmhouseFriedPork.java
    │       │  │    ├─ LocalPotChicken.java
    │       │  │    ├─ TujiaBraisedPork.java
    │       │  │    ├─ MushuMeat.java
    │       │  │    ├─ SaltPepperTenderloin.java
    │       │  │    ├─ ShreddedCabbage.java
    │       │  │    └─ DiSanXian.java
    │       │  └─ staple
    │       │  │    └─ Rice.java
    │       │  └─ drink
    │       │  │    ├─ LaoShanBeer.java
    │       │  │    └─ WuSuBeer.java
    │       │  └─ Goods.java
    │       ├─ builders
    │       │    └─ MealBuilder.java    
    │       └─ director
    │           └─ MealDirector.java
    └─ test
        └─ java
            └─ org.design.pattern.builder.test
                  └─ MealDirectorTest.java
2.2 代码实现
2.2.1 菜品

菜品接口

所有菜都需要提供菜名以及价格。

/**
 * 菜品
 */
public interface Goods {
    String getName();
    float getPrice();
}

水煮花生

/**
 * 水煮花生
 */
public class BoiledPeanuts implements Goods {
    @Override
    public String getName() {
        return "水煮花生";
    }

    @Override
    public float getPrice() {
        return 8;
    }
}
2.2.2 年夜饭生成器
/**
 * 年夜饭生成器
 */
@Getter
@Setter
public class MealBuilder {
    /**
     * 冷菜类
     */
    private List coldDishes;

    /**
     * 热菜类
     */
    private List hotDishes;

    /**
     * 主食
     */
    private Goods stapleFood;

    /**
     * 饮料
     */
    private Goods drink;

    /**
     * 获取花销
     * @return
     */
    public float getCost() {
        float result = 0.0f;
        result += getSingleDishesCost(coldDishes);
        result += getSingleDishesCost(hotDishes);
        result += stapleFood.getPrice();
        result += drink.getPrice();
        return result;
    }

    /**
     * 展示菜单
     */
    public void showMenu() {
        System.out.println("凉菜类:");
        showSingleDishesName(coldDishes);
        System.out.println("热菜类:");
        showSingleDishesName(hotDishes);
        System.out.println("主食:");
        System.out.println(stapleFood.getName());
        System.out.println("饮料:");
        System.out.println(drink.getName());
    }

    /**
     * 获取单类菜品价格
     * @param goodsList
     * @return
     */
    private float getSingleDishesCost(List goodsList) {
        float result = 0.0f;
        for (Goods goods : goodsList) {
            result += goods.getPrice();
        }
        return result;
    }

    /**
     * 展示单类菜品菜单
     * @param goodsList
     */
    private void showSingleDishesName(List goodsList) {
        for (Goods goods : goodsList) {
            System.out.println(goods.getName());
        }
    }
}
2.2.3 年夜饭主管类
/**
 * 年夜饭主管类
 */
public class MealDirector {
    /**
     * 单人多肉年夜饭套餐
     * @return
     */
    public MealBuilder constructMeatDinner() {
        MealBuilder mealBuilder = new MealBuilder();
        //冷菜
        List coldDishes = new ArrayList<>(1);
        coldDishes.add(new BoiledPeanuts());
        mealBuilder.setColdDishes(coldDishes);
        //热菜
        List hotDishes = new ArrayList(4);
        hotDishes.add(new KungPaoChicken());
        hotDishes.add(new FarmhouseFriedPork());
        hotDishes.add(new LocalPotChicken());
        hotDishes.add(new TujiaBraisedPork());
        mealBuilder.setHotDishes(hotDishes);
        //主食
        mealBuilder.setStapleFood(new Rice());
        //饮料
        mealBuilder.setDrink(new LaoShanBeer());
        return mealBuilder;
    }

    /**
     * 单人混合肉蔬年夜饭套餐
     * @return
     */
    public MealBuilder constructMeanAndVegetablesDinner() {
        MealBuilder mealBuilder = new MealBuilder();
        //冷菜
        List coldDishes = new ArrayList<>(1);
        coldDishes.add(new FriedPeanuts());
        mealBuilder.setColdDishes(coldDishes);
        //热菜
        List hotDishes = new ArrayList(4);
        hotDishes.add(new MushuMeat());
        hotDishes.add(new SaltPepperTenderloin());
        hotDishes.add(new ShreddedCabbage());
        hotDishes.add(new DiSanXian());
        mealBuilder.setHotDishes(hotDishes);
        //主食
        mealBuilder.setStapleFood(new Rice());
        //饮料
        mealBuilder.setDrink(new WuSuBeer());
        return mealBuilder;
    }
}
2.3 测试验证
2.3.1 测试验证类
public class MealDirectorTest {
    @Test
    public void testConstructMeatDinner() {
        MealDirector mealDirector = new MealDirector();
        MealBuilder mealBuilder = mealDirector.constructMeatDinner();
        mealBuilder.showMenu();
        System.out.println("单人多肉年夜饭套餐花费:" + mealBuilder.getCost());
    }

    @Test
    public void testConstructMeanAndVegetablesDinner() {
        MealDirector mealDirector = new MealDirector();
        MealBuilder mealBuilder = mealDirector.constructMeanAndVegetablesDinner();
        mealBuilder.showMenu();
        mealBuilder.getCost();
        System.out.println("单人混合肉蔬年夜饭套餐:" + mealBuilder.getCost());
    }
}
2.3.2 测试结果
凉菜类:
水煮花生
热菜类:
宫保鸡丁
农家小炒肉
地锅鸡
土家红烧肉
主食:
米饭
饮料:
崂山啤酒
单人多肉年夜饭套餐花费:141.0

凉菜类:
油炸花生米
热菜类:
木须肉
椒盐里脊
手撕包菜
地三鲜
主食:
米饭
饮料:
夺命大乌苏
单人混合肉蔬年夜饭套餐:112.0

四、建造者模式结构

初学 Java 设计模式(四):实战建造者模式 「单人年夜饭套餐」_第1张图片

  1. 生成器 (Builder) 接口声明在所有类型生成器中通用的产品构造步骤。
  2. 具体生成器 (Concrete Builders) 提供构造过程的不同实现。 具体生成器也可以构造不遵循通用接口的产品。
  3. 产品 (Products) 是最终生成的对象。 由不同生成器构造的产品无需属于同一类层次结构或接口。
  4. 主管 (Director) 类定义调用构造步骤的顺序, 这样就可以创建和复用特定的产品配置。
  5. 客户端 (Client) 必须将某个生成器对象与主管类关联。 一般情况下, 只需通过主管类构造函数的参数进行一次性关联即可。 此后主管类就能使用生成器对象完成后续所有的构造任务。 但在客户端将生成器对象传递给主管类制造方法时还有另一种方式。 在这种情况下,在使用主管类生产产品时每次都可以使用不同的生成器。

设计模式并不难学,其本身就是多年经验提炼出的开发指导思想,关键在于多加练习,带着使用设计模式的思想去优化代码,就能构建出更合理的代码。

源码地址: https://github.com/yiyufxst/d...

参考资料:
小博哥重学设计模式:https://github.com/fuzhengwei...
深入设计模式:https://refactoringguru.cn/de...

你可能感兴趣的:(java设计模式建造者模式)