初學 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...

相關文章
相關標籤/搜索