大話設計模式——》用java程式碼實現
設計模式之命令模式
概念:在軟體系統中,“行為請求者”與“行為實現者”通常呈現一種”緊耦合”。但在某些場合,比如要對行為進行“記錄、撤銷/重做、事務”等處理,這種無法抵禦變化的緊耦合是不合適的。在這種情況下,如何將”行為請求者”與”行為實現者”解耦?將一組行為抽象為物件,實現二者之間的鬆耦合。這就是命令模式(Command Pattern)。
這裡就借用大話設計模式裡的例子來說明下命令模式的假設場景:
假設我們去路邊攤吃烤肉,我們要多少烤肉,什麼型別的烤肉,直接就跟老闆說,老闆幫我們烤;然而呢,如果我們去烤肉店吃,我們得先告訴服務員,我們要吃什麼樣的烤肉,服務員記錄在紙上,然後再去告訴廚師,廚師烤完肉,服務員端出來給你。
前者,我們直接面對烤肉者,如果人不多還好,萬一人多了呢,有人插隊,老闆也不記得,這就造成了佇列的混亂;或者說,你的烤肉還沒烤,你不想要了,但是你給過錢了,老闆甚至都有可能不記得。
後者,因為每個客戶都按順序記錄在紙上,這樣就不會造成錯亂,而且,如果烤肉還沒上, 你可以直接告訴服務員你哪些不要了,可以讓她劃掉。在結束用餐後,由於你點東西都記錄在紙上,賬單並不會錯。
其實這個例子就很好的用上了命令模式,我們可以模擬一下路邊攤烤肉的程式碼
我們先來一個烤肉者(老闆)
public class Barbecuer {
public void chicken(){
System.out .println("==========烤了一串雞肉============");
}
public void mutton(){
System.out.println("==========烤了一串羊肉============");
}
public void beef(){
System.out.println("==========烤了一串牛肉============");
}
}
再來一個客戶類這裡寫程式碼片
public class Client {
public static void main (String[] args) {
//建立遙控器
Barbecuer barbecuer = new Barbecuer();
//呼叫相應的命令
barbecuer.chicken();
barbecuer.chicken();
barbecuer.chicken();
barbecuer.beef();
barbecuer.beef();
}
}
我們可以看出,請求者和執行者嚴重的耦合了在一起,這樣人一多,就容易出現亂子!!
現在來看看燒烤店的程式碼
首先,燒烤店多了一個服務員,所以我們建立一個服務員
服務員
public class Waiter {
//首先,服務員要能接收指令,但是我們不知道具體的指令是什麼,所以要將指令抽象出來
private Command command;
//將具體的指令傳達給服務員
public void setCommand(Command command) {
this.command=command;
}
//服務員呼叫你傳達的指令
public void executeCommand(){
command.execute();
}
}
服務員有了,我們就缺少我們要發號的指令
烤雞肉串的命令程式碼
public class ChickenCommand implements Command{
//這裡我們要告訴他,誰來負責烤肉
private Barbecuer bar;
public ChickenCommand (Barbecuer bar) {
this.bar = bar;
}
public void execute() {
bar.chicken();
}
@Override
public String toString() {
return "客戶點了串雞肉串";
}
}
烤牛肉串命令程式碼
public class BeefCommand implements Command{
private Barbecuer bar;
public BeefCommand(Barbecuer bar) {
this.bar = bar;
}
public void execute() {
bar.beef();
}
@Override
public String toString() {
return "客戶點了串牛肉串";
}
}
我們發現,烤肉具體的要由廚師來烤,所以,我們還需要一個具體的廚師
public class Barbecuer {
public void chicken(){
System.out.println("==========烤了一串雞肉============");
}
public void mutton(){
System.out.println("==========烤了一串羊肉============");
}
public void beef(){
System.out.println("==========烤了一串牛肉============");
}
}
客戶程式碼
public class Client {
public static void main(String[] args) {
//建立服務員
Waiter waiter = new Waiter();
//建立烤肉者
Barbecuer bar = new Barbecuer();
/*//建立烤肉命令
Command chicken = new ChickenCommand(bar);
chicken.setBar(bar);
Command beef= new BeefCommand (bar);
//將命令交給服務員
waiter.setCommand(chicken);
waiter.executeCommand();
waiter.setCommand(chicken);
waiter.executeCommand();
waiter.setCommand(chicken);
waiter.executeCommand();
waiter.setCommand(beef);
waiter.executeCommand();
waiter.setCommand(beef);
waiter.executeCommand();
}
}
但是這樣又有一個問題,客戶每次點菜,服務員要傳遞一次,這就造成了資源浪費,而且萬一雞肉串沒有了,那麼怎麼辦,而且如果我烤肉串不想要這麼多了怎麼辦,命令已經執行了。
修改程式碼:
服務員的程式碼
public class Waiter {
//將命令放在集合中
private List<Command> commands = new ArrayList<Command>();
public void setCommand(Command command) {
//如果點的是雞肉串,就告訴客戶沒有雞肉串了
if (command instanceof ChickenCommand) {
System.out.println("沒有雞肉串了!要不換成羊肉串?");
return;
}else{
System.out.println("日誌:"+command.toString());
commands.add(command);
}
}
//取消不要的命令
public void cancel(Command command){
if(commands.contains(command){
commands.remove(command);
}
}
public void executeCommand(){
System.out.println("========開始上菜了=======");
for (Command command : commands) {
command.execute();
}
}
}
客戶程式碼
public class Client {
public static void main(String[] args) {
//建立服務員
Waiter waiter = new Waiter();
//建立烤肉者
Barbecuer bar = new Barbecuer();
//建立烤肉命令
ChickenCommand chicken = new ChickenCommand();
chicken.setBar(bar);
MuttonCommand mutton = new MuttonCommand();
mutton.setBar(bar);
//將命令交給服務員
waiter.setCommand(chicken);
waiter.setCommand(chicken);
waiter.setCommand(chicken);
waiter.setCommand(beef);
waiter.setCommand(beef);
waiter.setCommand(beef);
waiter.setCommand(beef);
//服務於統一執行
waiter.executeCommand();
}
}
補上一張UML圖
這樣,我們客戶只要負責點烤肉,吃烤肉;具體的烤肉是怎麼烤的,我們根本不用關心,吃完,付賬,走人。即使烤肉點多了,不想要了,也可以刪掉。
總結一下—》
優點:
1)他能較容易的設計一個命令佇列
2)需要的情況下,可以更方便的記錄日誌
3)允許接收請求的一方決定是否接收請求
4)可以對請求進行撤銷和重做
5)新增具體的命令,不會影響已有的命令
UML圖
*模式心得*:其實所有的設計模式都是為了簡化程式碼,所以敏捷開發模式告訴我們,不要為程式碼新增基於猜測的,實際上不需要的功能。如果不清楚一個系統是否需要命令模式,那就不要著急的去實現它,重構這個模式並不難