1. 程式人生 > >大話設計模式——》用java程式碼實現

大話設計模式——》用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圖
這裡寫圖片描述

*模式心得*:其實所有的設計模式都是為了簡化程式碼,所以敏捷開發模式告訴我們,不要為程式碼新增基於猜測的,實際上不需要的功能。如果不清楚一個系統是否需要命令模式,那就不要著急的去實現它,重構這個模式並不難