1. 程式人生 > 實用技巧 >大話設計模式讀書筆記(命令模式)

大話設計模式讀書筆記(命令模式)

人物:大鳥,小菜

事件:小菜和大鳥去吃燒烤,發現路邊的燒烤攤人多擁擠,老闆在烤肉的同時可能記不住點菜人的名字,導致給錯,沒收錢,沒烤熟等一系列問題,而且白天有城管,晚上太晚人們也不願意吃路邊攤,這些都導致了路邊燒烤攤賺錢不穩定,而有店面的燒烤店,雖然要交一定的租金,但是沒了哪些不穩定因素,收入就相對穩定了,大鳥就燒烤店好過燒烤攤這個情景,引出了命令模式。


命令模式:

1.借烤肉事件設計程式碼,初次設計為緊耦合設計

2.在明確緊耦合設計的不好點後,又進行了鬆耦合設計

3.進一步完善鬆耦合設計

4.最後借上述例子引出命令模式並闡述了優點

緊耦合設計

趁著烤串還沒出來,大鳥讓小菜設計實現燒烤攤營業模式

Barbecuer類,路邊烤肉實現:

@Slf4j
public class Barbecuer {
    public void BakeMutton() {
        log.info("烤羊肉串");
    }

    public void BakeChickenWing() {
        log.info("烤雞翅!");
    }
}

客戶端:

public class BarbecueClient {
    public static void main(String[] args) {
        Barbecuer boy = new Barbecuer();
        boy.BakeMutton();
        boy.BakeMutton();
        boy.BakeMutton();
        boy.BakeChickenWing();
        boy.BakeMutton();
        boy.BakeMutton();
        boy.BakeChickenWing();
    }
}

大鳥:這樣人一多,請求一多久容易亂,你再試試燒烤店營業模式

鬆耦合設計

程式碼結構圖:

Command類,即抽象命令類:

public abstract class Command {
    protected Barbecuer receiver;

    public Command(Barbecuer receiver) {
        this.receiver = receiver;
    }

    abstract public void excuteCommand();
}

烤羊肉串,即具體命令類:

public class BakeMuttonCommand extends
Command { public BakeMuttonCommand(Barbecuer receiver) { super(receiver); } @Override public void excuteCommand() { receiver.BakeMutton(); } }

烤雞翅,即具體命令類:

public class BakeChickenWingCommand extends Command {
    public BakeChickenWingCommand(Barbecuer receiver) {
        super(receiver);
    }

    @Override
    public void excuteCommand() {
        receiver.BakeChickenWing();
    }
}

Waiter類,即服務員類:

public class Waiter {
    private Command command;

    public void setCommand(Command command) {
        this.command = command;
    }

    public void notifyCook() {
        command.excuteCommand();
    }
}

客戶端程式碼:

public class BarbecueClient {
    public static void main(String[] args) {
        //開店前的準備
        Barbecuer boy = new Barbecuer();
        Command bakeMuttonCommand1 = new BakeMuttonCommand(boy);
        Command bakeMuttonCommand2 = new BakeMuttonCommand(boy);
        Command bakeChickenWingCommand1 = new BakeChickenWingCommand(boy);
        Waiter girl = new Waiter();

        //開門營業
        girl.setCommand(bakeMuttonCommand1);
        girl.notifyCook();
        girl.setCommand(bakeMuttonCommand2);
        girl.notifyCook();
        girl.setCommand(bakeChickenWingCommand1);
        girl.notifyCook();
    }
}

大鳥:不錯,功能基本實現了,但是和實際場景有點不符合,因為一般叫烤串是一次叫6串,而不是一串一串叫叫6次,還有就是客戶也不知道到底有沒有賣完,最後就是客戶點了哪些食物飲料,都是需要記錄的,不然人一多,就忘了。

鬆耦合的進一步優化

小菜進行了第三版編寫:

服務員類:

@Slf4j
public class Waiter {
    private List<Command> orders = new ArrayList<>();

    /**
     * 設定訂單
     *
     * @param command
     */
    public void setOrder(Command command) {
        if (command.getClass().getName().equalsIgnoreCase("com.example.pmingup.service.designModeWan.BakeChickenWingCommand")) {
            log.info("服務員,雞翅沒有了,請點別的燒烤");
        } else {
            orders.add(command);
            log.info("增加訂單:" + command.getClass().getName() + "事件: " + new Date());
        }
    }

    /**
     * 取消訂單
     *
     * @param command
     */
    public void cancelOrder(Command command) {
        orders.remove(command);
        log.info("取消訂單:" + command.toString() + "事件:" + new Date());
    }

    /**
     * 通知全部執行
     */
    public void notifyCook() {
        for (Command cd : orders) {
            cd.excuteCommand();
        }
    }
}

客戶端程式碼:

public class BarbecueClient {
    public static void main(String[] args) {
        //開店前的準備
        Barbecuer boy = new Barbecuer();
        Command bakeMuttonCommand1 = new BakeMuttonCommand(boy);
        Command bakeMuttonCommand2 = new BakeMuttonCommand(boy);
        Command bakeChickenWingCommand1 = new BakeChickenWingCommand(boy);
        Waiter girl = new Waiter();

        //開門營業
       girl.setOrder(bakeMuttonCommand1);
       girl.setOrder(bakeMuttonCommand2);
       girl.setOrder(bakeChickenWingCommand1);

       girl.notifyCook();
    }
}

命令模式

1.概念:將一個請求封裝成一個物件,從而使你可用不同的請求對客戶進行引數化,對請求排隊或記錄請求日誌,以及支援可撤銷的操作。

2.結構圖:

3.程式碼如下:

CommandDemo類,來宣告執行操作的介面:

public abstract class CommandDemo {
    protected Receiver reciever;

    public CommandDemo(Receiver reciever) {
        this.reciever = reciever;
    }

    abstract public void execute();
}

ConcreteCommand類,將一個接受者物件繫結一個動作,呼叫接受者相應的操作,以實現execute():

public class ConcreteCommand extends CommandDemo {
    public ConcreteCommand(Receiver reciever) {
        super(reciever);
    }

    @Override
    public void execute() {
        reciever.action();
    }
}

Invoker類,要求該命令執行這個請求:

public class Invoker {
    private CommandDemo commandDemo;

    public void setCommandDemo(CommandDemo commandDemo) {
        this.commandDemo = commandDemo;
    }

    public void executeCommand() {
        commandDemo.execute();
    }
}

Receiver類,知道如何實施與執行一個與請求相關的操作,任何類都可能作為一個接受者:

@Slf4j
public class Receiver {
    public void action() {
        log.info("執行請求!");
    }
}

客戶端程式碼:

public class BarbecueClient {
    public static void main(String[] args) {
        Receiver r = new Receiver();
        CommandDemo c = new ConcreteCommand(r);
        Invoker i = new Invoker();
        i.setCommandDemo(c);
        i.executeCommand();
    }
}

4.優點:

(1)能較容易地設計一個命令佇列

(2)需要時,能較容易地將命令記入日誌

(3)允許接收請求的一方決定是否要求否決請求

(4)容易實現對請求的撤銷和重做

(5)新加的命令不會影響其他類,因此增加新的命令很容易

(6)命令模式把請求一個操作的物件與知道怎麼執行操作的一個物件分割開來