今天在面試別人的過程,問到了設(shè)計模式,他說命令模式,what??!我沒有聽過,我只能強裝淡定,問了問他,回來趕緊翻翻書,補一補。
命令模式
日常背書:命令模式(Command Pattern)是一種數(shù)據(jù)驅(qū)動的設(shè)計模式,它屬于行為型模式。請求以命令的形式包裹在對象中,并傳給調(diào)用對象。調(diào)用對象尋找可以處理該命令的合適的對象,并把該命令傳給相應(yīng)的對象,該對象執(zhí)行命令。先來講個故事吧,再來說說自己的理解。
板面的故事
板面相信大家都吃過吧,今天講的是公司那邊的一家板面店, 我們皮皮家族經(jīng)常去吃,這家店中只有老板和老板娘,每次都記不住我們點了什么,也記不住點餐的順序,經(jīng)常發(fā)生的場景就是,老板端著一碗板面說兄弟的板面加腸好了,你滿面黑線的說,我點的是蓋飯。下面我們用代碼來描述下這個板面的業(yè)務(wù)模型。
板面1.0:緊耦合
板面店:
package edu.design.pattern.command;
/**
* @author ZhaoWeinan
* @date 2018/3/21
* @description
*/
public class Noodler {
public void makeNoodle(){
System.out.println("兄弟,你的板面加腸加蛋!");
}
public void makeRice(){
System.out.println("兄弟,你的蓋飯!");
}
}
皮皮家族來到了板面店
package edu.design.pattern.command;
/**
* @author ZhaoWeinan
* @date 2018/3/21
* @description
*/
public class Demo {
public static void main(String[] args){
Noodler noodler = new Noodler();
//皮皮家族來到了板面店,點了3個板面,2個蓋飯
noodler.makeNoodle();
noodler.makeRice();
noodler.makeNoodle();
noodler.makeRice();
noodler.makeNoodle();
}
}
效果:

板面1.0,描述了目前板面店的業(yè)務(wù)模型,實現(xiàn)很簡單,但是暴露了很多問題。
從現(xiàn)實業(yè)務(wù)上來看
我們作為消費者直接與廚師(做飯也是老板和老板娘做的)交互,他們無法專注于自己的本質(zhì)工作(做飯),需要記住每個人點的東西,每個點餐的順序,就造成了目前的情況,不是看你點了什么,而是看他們做了什么,你點的東西可能已經(jīng)沒有原材料做不了了,但是也無法及時通知你,你想換一種飯,他可能無法顧及你的需求,這種不單一的職責(zé),讓他們無法同時顧及做飯與招呼客人點東西,兩邊的工作都可能做不好
從代碼上來看
這種設(shè)計,我們作為行為請求者,板面店作為行為執(zhí)行者,兩者是緊耦合,對于一些簡單的場景,這么做比較合適,請求者直接與執(zhí)行者交互,但在某些場合,比如需要對行為進(jìn)行記錄、撤銷或重做、事務(wù)等處理時,這種緊耦合的設(shè)計就不是很合適
板面2.0:使用命令模式為板面店解耦
我們皮皮家族一直在討論板面店的問題在,認(rèn)為關(guān)鍵點就是把招呼客人與做飯分開,在我們的代碼中,修改老板與老板娘的職責(zé),讓他們只負(fù)責(zé)做飯:
package edu.design.pattern.command;
/**
* @author ZhaoWeinan
* @date 2018/3/22
* @description
*/
public class Maker {
public void make(String s){
System.out.println("兄弟,我只負(fù)責(zé)做飯!我正在" + s);
}
//老板、老板娘查看廚房信息,看看客人點的是否能做
public boolean getInfo(String s){
if ("板面".equals(s)){
return true;
}else if ("蓋飯".equals(s)){
return true;
}else if ("韭菜水餃".equals(s)){
System.out.println("韭菜沒有了,不要讓客人點韭菜水餃了!");
return false;
}else {
return true;
}
}
}
為板面店做一個訂單系統(tǒng):
package edu.design.pattern.command;
/**
* @author ZhaoWeinan
* @date 2018/3/22
* @description
*/
public interface Command {
/**
* 訂單系統(tǒng)通知老板、老板娘做飯
*/
void execute();
/**
* 獲取廚房信息
* @return
*/
String getMakeInfo();
}
在訂單系統(tǒng)中,添加一個板面的信息
package edu.design.pattern.command;
/**
* @author ZhaoWeinan
* @date 2018/3/22
* @description
*/
public class NoodleCommand implements Command {
private Maker maker;
public NoodleCommand(Maker maker) {
this.maker = maker;
}
@Override
public void execute() {
System.out.println("訂單系統(tǒng)通知老板,做一碗板面!");
maker.make("做板面");
}
@Override
public String getMakeInfo() {
if (maker.getInfo("板面")){
System.out.println("老板查看廚房信息!");
System.out.println("發(fā)現(xiàn)有材料,可以做板面!");
System.out.println("通過訂單系統(tǒng),通知服務(wù)員招呼客人說:可以點板面!");
return "老板,給做一碗板面!";
}else {
System.out.println("沒有材料,做不了板面了!");
return "NULL";
}
}
}
為板面店招聘一個服務(wù)員,專門負(fù)責(zé)招呼客人:
package edu.design.pattern.command;
import java.util.ArrayList;
import java.util.List;
/**
* 服務(wù)員類
* @author ZhaoWeinan
* @date 2018/3/22
* @description
*/
public class Waiter {
//服務(wù)員控制訂單系統(tǒng)
private List<Command> commandList = new ArrayList<>();
//服務(wù)員招呼客人,把客人的點餐情況輸入點餐系統(tǒng)
//今天沒有韭菜了,韭菜水餃點不了了
public void setCommand(Command command){
if (command.getMakeInfo().equals("NULL")){
System.out.println("韭菜沒有了,包不了韭菜水餃了!");
}else {
commandList.add(command);
}
}
//服務(wù)員在訂單系統(tǒng)中通知老板、老板娘做飯
public void notifyMaker(){
if (commandList.size() == 0){
System.out.println("暫時沒有客人點餐!");
}
for (Command command : commandList){
command.execute();
}
}
}
板面店重新開張,皮皮家族又來了:
package edu.design.pattern.command;
/**
* @author ZhaoWeinan
* @date 2018/3/22
* @description
*/
public class CommandDemo {
public static void main(String[] args){
//老板登場
Maker maker = new Maker();
//服務(wù)員登場,招呼客人
Waiter waiter = new Waiter();
//服務(wù)員查看訂單系統(tǒng)通知,是否可以點板面
Command command = new NoodleCommand(maker);
//服務(wù)員通過訂單系統(tǒng)點了一份板面
waiter.setCommand(command);
//服務(wù)員點擊確認(rèn),訂單系統(tǒng)通知老板做飯
waiter.notifyMaker();
}
}
效果:

通過這次板面2.0的升級,解決了1.0中的這些問題,板面的故事,講完了,讓我們來總結(jié)一下吧。
總結(jié)
從上面的故事我們來總結(jié)一下
需要解決的問題
行為請求者與執(zhí)行者之間緊耦合的設(shè)計,對于一些簡單的場景,這么做比較合適,請求者直接與執(zhí)行者交互,但在某些場合,比如需要對行為進(jìn)行記錄、撤銷或重做、事務(wù)等處理時,這種緊耦合的設(shè)計就不是很合適。
解決的方式
命令模式制定了三個主要的角色:命令執(zhí)行對象receiver、 命令對象command、命令請求的入口invoker,通過請求者通過命令請求的入口把命令傳遞給接受執(zhí)行者進(jìn)行命令的執(zhí)行,來把請求→執(zhí)行的過程進(jìn)行了解耦
優(yōu)點
1、降低了系統(tǒng)耦合度
2、可以比較容易的把命令記入日志
3、允許命令接收方?jīng)Q定是否接受命令
4、新的命令可以很容易添加到系統(tǒng)中去
命令模式就為大家說到這里,歡迎大家來交流,指出文中一些說錯的地方,讓我加深認(rèn)識。
謝謝大家!