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

1、建造者模式介绍

1. 解决的问题

主要解决在软件系统中,有时候面临着"一个复杂对象"的建立工做,其一般由各个部分的子对象用必定的算法构成;因为需求的变化,这个复杂对象的各个部分常常面临着剧烈的变化,可是将它们组合在一块儿的算法却相对稳定。java

2. 定义

建造者模式是一种建立型设计模式,可以分步骤建立复杂对象。该模式容许使用相同的建立代码生成不一样类型和形式的对象。git

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

2、建造者模式优缺点

1. 优势

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

2. 缺点

  • 因为采用该模式须要新增多个类,所以代码总体复杂程度会有所增长。

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

1. 实例场景

今年不少人都响应了就地过年的倡议,但异地过年不心酸,咱们能够点一份单人年夜饭套餐来犒劳异地过年的本身!算法

单人多肉年夜饭套餐:设计模式

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

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

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

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<Goods> coldDishes;

    /**
     * 热菜类
     */
    private List<Goods> 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<Goods> goodsList) {
        float result = 0.0f;
        for (Goods goods : goodsList) {
            result += goods.getPrice();
        }
        return result;
    }

    /**
     * 展现单类菜品菜单
     * @param goodsList
     */
    private void showSingleDishesName(List<Goods> 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<Goods> coldDishes = new ArrayList<>(1);
        coldDishes.add(new BoiledPeanuts());
        mealBuilder.setColdDishes(coldDishes);
        //热菜
        List<Goods> hotDishes = new ArrayList<Goods>(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<Goods> coldDishes = new ArrayList<>(1);
        coldDishes.add(new FriedPeanuts());
        mealBuilder.setColdDishes(coldDishes);
        //热菜
        List<Goods> hotDishes = new ArrayList<Goods>(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

4、建造者模式结构

建造者模式-模式结构图

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

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

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

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

相关文章
相关标签/搜索