定義
命令模式(Command Pattern):將一個(gè)請(qǐng)求封裝為一個(gè)對(duì)象,從而讓我們可用不同的請(qǐng)求對(duì)客戶進(jìn)行參數(shù)化;對(duì)請(qǐng)求排隊(duì)或者記錄請(qǐng)求日志,以及支持可撤銷的操作。其別名為動(dòng)作(Action)模式或事務(wù)(Transaction)模式。
通俗理解
我們都有找過(guò)10086的客服,如果你的手機(jī)出什么問(wèn)題了,都會(huì)撥打這個(gè)號(hào)碼,請(qǐng)求客服的解決。通常請(qǐng)求客服的時(shí)候,你都會(huì)說(shuō)明一下你的問(wèn)題,是流量問(wèn)題、還是話費(fèi)問(wèn)題,然后客服會(huì)根據(jù)你的這些問(wèn)題,進(jìn)行解決,然后你就很愉快地掛了電話。
實(shí)際上卻沒(méi)有這么簡(jiǎn)單,如果一個(gè)人打電話過(guò)來(lái)了,說(shuō)他們家的小區(qū)出現(xiàn)了“乒乓效應(yīng)”[1],老是斷線。估計(jì)客服聽(tīng)到這個(gè)詞都會(huì)一臉懵逼,不知道怎么處理吧。再或者,有一個(gè)人的問(wèn)題是他的流量充值了,但是還沒(méi)有到賬,客服查明之后往這個(gè)人的賬戶上沖流量,結(jié)果一個(gè)手抖,多打了兩個(gè)零,系統(tǒng)還不支持撤銷,遇到這種情況又得扣工資。還有... ...
可見(jiàn),客服也不容易。那么怎么樣可以讓客服更好地進(jìn)行服務(wù),減少犯錯(cuò)的幾率呢?我們保持一個(gè)觀點(diǎn)就好:專業(yè)的事情讓專業(yè)的人去做。這時(shí)候,客服只是一個(gè)客服系統(tǒng)的門(mén)面,當(dāng)客服接聽(tīng)到電話的時(shí)候,他會(huì)將問(wèn)題轉(zhuǎn)發(fā)給專業(yè)的人士,例如流量問(wèn)題就讓流量組的人去處理、基站問(wèn)題就讓設(shè)備維護(hù)人員去處理;第二個(gè)是,專業(yè)人士有相關(guān)的撤銷方法,如果手抖了輸多了一個(gè)零,可以撤銷這個(gè)請(qǐng)求,中午還能加雞腿??,當(dāng)然,用戶不滿意的時(shí)候,還可以重復(fù)用戶請(qǐng)求,把他的要求再做一遍。
這個(gè)就是命令模式。用戶(Client)通過(guò)和客服(Invoker)交流,客服根據(jù)不同的問(wèn)題,將這些問(wèn)題轉(zhuǎn)發(fā)到不同的組(Command),然后相應(yīng)的組的成員去完成這個(gè)工作。
示例
客服系統(tǒng)作為示例。
渣渣程序
話費(fèi)服務(wù)和流量服務(wù)
public class TrafficService {
public void answer() {
System.out.println("流量服務(wù)");
}
}
public class PhoneChargeService {
public void answer() {
System.out.println("話費(fèi)服務(wù)");
}
}
客服
public class CustomerService {
public void service(String type) {
switch (type){
case "phoneCharge":
new PhoneChargeService().answer();
break;
case "traffic":
new TrafficService().answer();
break;
default:
System.out.println("服務(wù)不支持");
break;
}
}
}
程序主入口
public class Main {
public static void main(String[] args) {
CustomerService service = new CustomerService();
service.service("phoneCharge");
}
}
//話費(fèi)服務(wù)
缺點(diǎn)顯而易見(jiàn):
- 客服需要知道每一種問(wèn)題的處理方法,即使
TrafficService是其他客服,門(mén)面的客服也需要知道得調(diào)用TrafficService什么方法; - 新增加處理類型需要修改
CustomerService.service(String type)方法,違反開(kāi)閉原則; - 沒(méi)有撤銷。
優(yōu)化
類圖
程序
話費(fèi)服務(wù)和流量服務(wù)
public class PhoneChargeService {
public void answer() {
System.out.println("話費(fèi)服務(wù)");
}
public void undo() {
System.out.println("撤回話費(fèi)服務(wù)");
}
}
//TrafficService類似,省略
命令接口和實(shí)現(xiàn)
public interface ICommand {
/**
* 執(zhí)行
*/
void execute();
/**
* 撤銷
*/
void undo();
/**
* 重做
*/
void redo();
}
public class PhoneChangeCommand implements ICommand {
private PhoneChargeService phoneChargeService;
private boolean change;
// 構(gòu)造器,getter和setter方法
public void execute() {
phoneChargeService.answer();
change = true;
}
public void undo() {
if(change) {
phoneChargeService.undo();
} else {
System.out.println("沒(méi)有操作話費(fèi)服務(wù),撤銷失敗");
}
}
public void redo() {
execute();
}
}
//TrafficCommand類似,省略
客服
public class Invoker {
private List<ICommand> commands = new ArrayList<>();
public List<ICommand> getCommands() {
return commands;
}
public void setCommands(List<ICommand> commands) {
this.commands = commands;
}
public void execumentCommand(ICommand command) {
commands.add(command);
command.execute();
}
public void redoCommand() {
if(commands.size() > 0) {
commands.get(commands.size()-1).redo();
commands.add(commands.get(commands.size()-1));
} else {
System.out.println("任務(wù)列表當(dāng)中無(wú)任務(wù),不能撤銷");
}
}
public void undoCommand(){
if(commands.size() > 0) {
commands.get(commands.size()-1).undo();
commands.remove(commands.size()-1);
} else {
System.out.println("沒(méi)有服務(wù)可以撤銷");
}
}
}
主入口
public class Main {
public static void main(String[] args) {
ICommand phoneCommand = new PhoneChangeCommand(new PhoneChargeService());
ICommand trafficCommand = new TrafficCommand(new TrafficService());
Invoker server = new Invoker();
server.execumentCommand(phoneCommand);
server.execumentCommand(trafficCommand);
server.redoCommand();
server.undoCommand();
server.undoCommand();
server.undoCommand();
server.undoCommand();
}
}
//話費(fèi)服務(wù)
//流量服務(wù)
//流量服務(wù)
//撤回流量服務(wù)
//撤回流量服務(wù)
//撤回話費(fèi)服務(wù)
//沒(méi)有服務(wù)可以撤銷
優(yōu)點(diǎn)
- 如果需要添加一個(gè)新的服務(wù)類型,不需要修改任何的內(nèi)容,只需要添加一個(gè)服務(wù)的處理類,一個(gè)命令實(shí)現(xiàn)類實(shí)現(xiàn)命令接口,并在這個(gè)命令實(shí)現(xiàn)類里面注入服務(wù)的處理類即可,符合開(kāi)閉原則;
- 請(qǐng)求和接收解耦,Invoker通過(guò)
ICommand調(diào)用PhoneChargeService,而Main則調(diào)動(dòng)Invoker去調(diào)用服務(wù)方法; - 容易設(shè)計(jì)出一個(gè)命令隊(duì)列或者組合命令;
- 撤銷undo和恢復(fù)redo。
缺點(diǎn)
- 類?的真多。
應(yīng)用場(chǎng)景
- 請(qǐng)求與調(diào)用的解耦,就像10086的客服一樣,我只需要知道我的問(wèn)題解決了就可以,不需要知道這個(gè)過(guò)程,另外相關(guān)的維護(hù)組的人員也只需要知道有工作要做就行,不需要知道這個(gè)工作室誰(shuí)派下來(lái)的;
- 需要支持撤銷和恢復(fù)的操作;
- 需要將命令組合在一起。
實(shí)例
JDK Awt和Swing包,MenuItem類
代碼
http://www.itdecent.cn/p/8be50cac2929
-
通信術(shù)語(yǔ),指的是手機(jī)在兩個(gè)基站覆蓋范圍的中間地帶,手機(jī)不停切換對(duì)這兩個(gè)基站的連接,造成信號(hào)的丟失,一般的解決辦法是加大基站的功率,使得基站的覆蓋范圍更廣,或者是增加基站的數(shù)量。 ?