初學 Java 設計模式(十五):實戰命令模式 「掃碼點餐」

1、命令模式介紹

1. 解決的問題

主要解決在系統中,行爲請求者和行爲實現者緊耦合的問題。java

2. 定義

命令模式是一種行爲設計模式,它可將請求轉換爲一個包含與請求相關的全部信息的獨立對象。這個轉換會根據不一樣的請求將方法參數化、延遲請求執行或將其放入隊列中,且能實現可撤銷操做。git

3. 應用場景

  • 須要經過操做來參數化對象,可以使用命令模式。
  • 將操做放入隊列中、延遲操做的執行或者遠程執行操做,可以使用命令模式。
  • 實現操做回滾功能,可以使用命令模式。

2、命令模式優缺點

1. 優勢

  • 單一職責原則:能夠解耦觸發和執行操做的類。
  • 開閉原則:能夠在不修改已有客戶端代碼的狀況下,在程序中建立新的命令
  • 能夠實行撤銷和恢復功能。
  • 能夠實現操做的延遲執行。
  • 能夠將一組簡單命令組合成一個複雜命令。

2. 缺點

  • 代碼可能會變得更加複雜,由於在發送者和接收者之間增長了一個全新的層次。

3、命令模式應用實例:掃碼點餐

1. 實例場景

今年五一五天假,不少小夥伴可能以及火燒眉毛地要出去玩了。出去除了看風景之外,最重要的就是體驗一下當地的美食。找到心儀的店,固然少不了排隊,排隊的同時服務員會給一個二維碼提早點餐了,當咱們選好了想要吃的美食,肯定下單,後臺系統自動將訂單打印給不一樣的廚師。github

這時,咱們的訂單就是一個命令,它在廚師製做以前一直在隊列中等待。命令包含製做這些美食的全部信息。廚師能夠根據這個訂單直接製做,不須要和咱們確認訂單信息。設計模式

今天,咱們就以掃碼點餐爲例,介紹一下命令模式。ide

2. 命令模式實現

2.1 工程結構
command-pattern
└─ src
    ├─ main
    │    └─ java
    │    └─ org.design.pattern.command
    │       ├─ model
    │       │    ├─ cook
    │       │    │    ├─ Cook.java
    │       │    │    └─ impl
    │       │    │         ├─ ChiefCook.java
    │       │    │         └─  CustomCook.java
    │       │    └─ dish
    │       │         ├─ Dish.java
    │       │         └─ impl
    │       │              ├─ BraisedIntestines.java
    │       │              ├─ FriedPeanuts.java
    │       │              ├─ LocalPotChicken.java
    │       │              └─ ShreddedCabbage.java
    │       └─ service
    │            └─ OrderService.java
    │                 └─ impl
    │                    └─ OrderServiceImpl.java
    └─ test
        └─ java
            └─ org.design.pattern.command
                  └─ OrderServiceTest.java
2.2 代碼實現
2.2.1 實體類

廚師接口函數

/**
 * 廚師接口
 */
public interface Cook {

    /**
     * 作飯
     * @param dishName 菜名
     */
    void cooking(String dishName);
}

主廚實體類測試

/**
 * 主廚
 */
@Slf4j
public class ChiefCook implements Cook {

    /**
     * 作飯
     *
     * @param dishName 菜名
     */
    @Override
    public void cooking(String dishName) {
        log.info("主廚製做{}", dishName);
    }
}

普通廚師實體類優化

/**
 * 普通廚師
 */
@Slf4j
public class CustomCook implements Cook {

    /**
     * 作飯
     *
     * @param dishName 菜名
     */
    @Override
    public void cooking(String dishName) {
        log.info("普通廚師製做{}", dishName);
    }
}

美食接口this

/**
 * 美食接口
 */
public abstract class Dish {

    /**
     * 廚師
     */
    protected Cook cook;

    public Dish(Cook cook) {
        this.cook = cook;
    }

    /**
     * 製做
     */
    abstract public void cook();
}

九轉大腸實體類spa

/**
 * 九轉大腸
 */
public class BraisedIntestines extends Dish {

    public BraisedIntestines(Cook cook) {
        super(cook);
    }

    /**
     * 製做
     */
    @Override
    public void cook() {
        this.cook.cooking("九轉大腸");
    }
}

油炸花生米實體類

/**
 * 油炸花生米
 */
public class FriedPeanuts extends Dish {

    public FriedPeanuts(Cook cook) {
        super(cook);
    }

    /**
     * 製做
     */
    @Override
    public void cook() {
        this.cook.cooking("油炸花生米");
    }
}

地鍋雞實體類

/**
 * 地鍋雞
 */
public class LocalPotChicken extends Dish {

    public LocalPotChicken(Cook cook) {
        super(cook);
    }

    /**
     * 製做
     */
    @Override
    public void cook() {
        this.cook.cooking("地鍋雞");
    }
}

手撕包菜實體類

/**
 * 手撕包菜
 */
public class ShreddedCabbage extends Dish {

    public ShreddedCabbage(Cook cook) {
        super(cook);
    }

    /**
     * 製做
     */
    @Override
    public void cook() {
        this.cook.cooking("手撕包菜");
    }
}
2.2.2 服務類

點餐服務接口

/**
 * 點餐服務接口
 */
public interface OrderService {

    /**
     * 點菜
     * @param dish 菜
     */
    void order(Dish dish);

    /**
     * 下單
     */
    void placeOrder();
}

點餐服務實現類

/**
 * 點餐服務實現類
 */
public class OrderServiceImpl implements OrderService {

    private List<Dish> dishList = new ArrayList<>();

    /**
     * 點菜
     *
     * @param dish 菜
     */
    @Override
    public void order(Dish dish) {
        dishList.add(dish);
    }

    /**
     * 下單
     */
    @Override
    public synchronized void placeOrder() {
        for (Dish dish : dishList) {
            dish.cook();
        }
        dishList.clear();
    }
}
2.3 測試驗證
2.3.1 測試驗證類
public class OrderServiceTest {

    @Test
    public void testPlaceOrder() {
        Cook chiefCook = new ChiefCook();
        Cook customCook = new CustomCook();
        Dish braisedIntestines = new BraisedIntestines(chiefCook);
        Dish localPotChicken = new LocalPotChicken(chiefCook);
        Dish friedPeanuts = new FriedPeanuts(customCook);
        Dish shreddedCabbage = new ShreddedCabbage(customCook);

        OrderService orderService = new OrderServiceImpl();
        orderService.order(braisedIntestines);
        orderService.order(localPotChicken);
        orderService.order(friedPeanuts);
        orderService.order(shreddedCabbage);
        orderService.placeOrder();
    }
}
2.3.2 測試結果
21:08:41.617 [main] INFO  o.d.p.c.model.cook.impl.ChiefCook - 主廚製做九轉大腸
21:08:41.620 [main] INFO  o.d.p.c.model.cook.impl.ChiefCook - 主廚製做地鍋雞
21:08:41.620 [main] INFO  o.d.p.c.model.cook.impl.CustomCook - 普通廚師製做油炸花生米
21:08:41.620 [main] INFO  o.d.p.c.model.cook.impl.CustomCook - 普通廚師製做手撕包菜

Process finished with exit code 0

4、命令模式結構

命令模式-模式結構圖

  1. 觸發者(Invoker)—— 亦稱 「發送者(Sender)」類負責對請求進行初始化,其中必須包含一個成員變量來存儲對命令對象的引用。

    發送者觸發命令,而不向接收者這集發送請求,且發送者並不負責建立命令對象:一般會經過構造函數從客戶端處獲取預先生成的命令。

  2. 命令(Command)接口一般僅聲明一個執行命令的方法。
  3. 具體命令(Concrete Commands)會實現各類類型的請求。

    具體命令自身並不完成工做,而是會將調用委派給一個業務邏輯對象,固然爲了簡化代碼,也合併該對象。

    接收對象執行方法所需的參數可聲明爲具體命令的成員對象,同時能夠將命令對象設爲不可變,僅容許經過構造早函數來對這些成員變量進行初始化。

  4. 接收者(Receiver)類包含部分業務邏輯。

    基本上,任何對象均可以做爲接收者。絕大部分命令只處理如何將請求傳遞給接收者的細節,接收者自行完成實際的工做。

  5. 客戶端(Client)會建立並配置具體命令對象。

    客戶端必須將接收者實體在內的全部請求參數傳遞給命令的構造函數,以後,生成的命令就能夠與一個或多個發送者相關聯。

設計模式並不難學,其自己就是多年經驗提煉出的開發指導思想,關鍵在於多加練習,帶着使用設計模式的思想去優化代碼,就能構建出更合理的代碼。

源碼地址:https://github.com/yiyufxst/design-pattern-java

參考資料:
小博哥重學設計模式:https://github.com/fuzhengwei/itstack-demo-design
深刻設計模式:https://refactoringguru.cn/design-patterns/catalog

相關文章
相關標籤/搜索