設計模式:命令(Command)模式

設計模式之命令(Command)模式

在軟件開發系統中,常常出現“方法的請求者”與“方法的實現者”之間存在緊密的耦合關係。這不利於軟件功能的擴展與維護。例如,想對行爲進行“撤銷、重做、記錄”等處理都很不方便,因此“如何將方法的請求者與方法的實現者解耦?”變得很重要,命令模式能很好地解決這個問題。

在現實生活中,這樣的例子也很多,例如,電視機遙控器(命令發送者)通過按鈕(具體命令)來遙控電視機(命令接收者),還有計算機鍵盤上的“功能鍵”等。

定義與特點

命令(Command)模式的定義如下:將一個請求封裝爲一個對象,使發出請求的責任和執行請求的責任分割開。這樣兩者之間通過命令對象進行溝通,這樣方便將命令對象進行儲存、傳遞、調用、增加與管理。

命令模式的主要優點如下。

  1. 降低系統的耦合度。命令模式能將調用操作的對象與實現該操作的對象解耦。
  2. 增加或刪除命令非常方便。採用命令模式增加與刪除命令不會影響其他類,它滿足“開閉原則”,對擴展比較靈活。
  3. 可以實現宏命令。命令模式可以與組合模式結合,將多個命令裝配成一個組合命令,即宏命令。
  4. 方便實現 Undo 和 Redo 操作。命令模式可以與後面介紹的備忘錄模式結合,實現命令的撤銷與恢復。

其缺點是:可能產生大量具體命令類。因爲計對每一個具體操作都需要設計一個具體命令類,這將增加系統的複雜性。

結構與實現

可以將系統中的相關操作抽象成命令,使調用者與實現者相關分離,其結構如下。

結構

命令模式包含以下主要角色。

  1. 抽象命令類(Command)角色:聲明執行命令的接口,擁有執行命令的抽象方法 execute()。
  2. 具體命令角色(Concrete Command)角色:是抽象命令類的具體實現類,它擁有接收者對象,並通過調用接收者的功能來完成命令要執行的操作。
  3. 實現者/接收者(Receiver)角色:執行命令功能的相關操作,是具體命令對象業務的真正實現者。
  4. 調用者/請求者(Invoker)角色:是請求的發送者,它通常擁有很多的命令對象,並通過訪問命令對象來執行相關請求,它不直接訪問接收者。

其結構圖如圖 1 所示。

在這裏插入圖片描述

實現

命令模式的代碼如下:

package com.design.pattern.behaviorPattern.commandPattern;

public class CommandPattern {

    public static void main(String[] args) {
        Command command = new ConcreteCommand();
        Invoker invoker = new Invoker(command);
        invoker.call();
    }
}

//抽象命令
interface Command{
    public  void execute();
}

//接受者
class Receiver{

    public void action(){
        System.out.println("接受者的action()方法被調用...");
    }
}

//具體命令
class ConcreteCommand implements Command{

    private Receiver receiver;

    public ConcreteCommand() {
        receiver = new Receiver();
    }

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

//調用者
class Invoker{

    private Command command;

    public Invoker(Command command) {
        this.command = command;
    }

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

    public void call(){
        System.out.println("調用者執行命令command...");
        command.execute();
    }
}

應用實例

【例1】用命令模式實現客戶去餐館喫早餐的實例。

分析:客戶去餐館可選擇的早餐有腸粉、河粉和餛飩等,客戶可向服務員選擇以上早餐中的若干種,服務員將客戶的請求交給相關的廚師去做。這裏的點早餐相當於“命令”,服務員相當於“調用者”,廚師相當於“接收者”,所以用命令模式實現比較合適。

首先,定義一個早餐類(Breakfast),它是抽象命令類,有抽象方法 cooking(),說明要做什麼;再定義其子類腸粉類(ChangFen)、餛飩類(HunTun)和河粉類(HeFen),它們是具體命令類,實現早餐類的 cooking() 方法,但它們不會具體做,而是交給具體的廚師去做;具體廚師類有腸粉廚師(ChangFenChef)、餛蝕廚師(HunTunChef)和河粉廚師(HeFenChef),他們是命令的接收者,由於本實例要顯示廚師做菜的效果圖(點此下載要顯示的效果圖),所以把每個廚師類定義爲 JFrame 的子類;最後,定義服務員類(Waiter),它接收客戶的做菜請求,併發出做菜的命令。客戶類是通過服務員類來點菜的,圖 2 所示是其結構圖。

在這裏插入圖片描述

程序代碼如下:

package com.design.pattern.behaviorPattern.commandPattern;

import javax.swing.*;

public class CookingCommand {

    public static void main(String[] args) {
        Breakfast food1 = new ChangFen();
        Breakfast food2 = new HunTun();
        Breakfast food3 = new HeFen();
        Waiter waiter = new Waiter();
        waiter.setChangFen(food1);
        waiter.setHunTun(food2);
        waiter.setHeFen(food3);
        waiter.chooseChangFen();
        waiter.chooseHeFen();
        waiter.chooseHunTun();
    }
}
//抽象命令:早餐
interface Breakfast
{
    public void cooking();
}
//具體命令:腸粉
class ChangFen implements Breakfast{

    private ChangFenChef changFenChef;

    public ChangFen() {
        changFenChef = new ChangFenChef();
    }

    @Override
    public void cooking() {
        changFenChef.cooking();
    }
}

//具體命令:餛飩
class HunTun implements Breakfast
{
    private HunTunChef receiver;
    HunTun()
    {
        receiver=new HunTunChef();
    }
    public void cooking()
    {
        receiver.cooking();
    }
}
//具體命令:河粉
class HeFen implements Breakfast
{
    private HeFenChef receiver;
    HeFen()
    {
        receiver=new HeFenChef();
    }
    public void cooking()
    {
        receiver.cooking();
    }
}


//接收者:腸粉廚師
class ChangFenChef extends JFrame {
    JLabel l=new JLabel();
    ChangFenChef()
    {
        super("煮腸粉");
        l.setIcon(new ImageIcon("D:\\IdeaProjects\\DesignPattern\\src\\resources\\picture\\ChangFen.jpg"));
        this.add(l);
        this.setLocation(30, 30);
        this.pack();
        this.setResizable(false);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
    public void cooking()
    {
        this.setVisible(true);
    }


}
//接收者:餛飩廚師
class HunTunChef extends JFrame{
    JLabel l=new JLabel();
    HunTunChef()
    {
        super("煮餛飩");
        l.setIcon(new ImageIcon("D:\\IdeaProjects\\DesignPattern\\src\\resources\\picture\\HunTun.jpg"));
        this.add(l);
        this.setLocation(350, 50);
        this.pack();
        this.setResizable(false);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
    public void cooking()
    {
        this.setVisible(true);
    }
}

//接收者:河粉廚師
class HeFenChef extends JFrame {
    JLabel l=new JLabel();
    HeFenChef()
    {
        super("煮河粉");
        l.setIcon(new ImageIcon("D:\\IdeaProjects\\DesignPattern\\src\\resources\\picture\\HeFen.jpg"));
        this.add(l);
        this.setLocation(200, 280);
        this.pack();
        this.setResizable(false);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
    public void cooking()
    {
        this.setVisible(true);
    }
}

//調用者:服務員
class Waiter{
    private Breakfast changFen,hunTun,heFen;
    public void setChangFen(Breakfast f)
    {
        changFen=f;
    }
    public void setHunTun(Breakfast f)
    {
        hunTun=f;
    }
    public void setHeFen(Breakfast f)
    {
        heFen=f;
    }
    public void chooseChangFen()
    {
        changFen.cooking();
    }
    public void chooseHunTun()
    {
        hunTun.cooking();
    }
    public void chooseHeFen()
    {
        heFen.cooking();
    }

}

應用場景

命令模式通常適用於以下場景。

  1. 當系統需要將請求調用者與請求接收者解耦時,命令模式使得調用者和接收者不直接交互。
  2. 當系統需要隨機請求命令或經常增加或刪除命令時,命令模式比較方便實現這些功能。
  3. 當系統需要執行一組操作時,命令模式可以定義宏命令來實現該功能。
  4. 當系統需要支持命令的撤銷(Undo)操作和恢復(Redo)操作時,可以將命令對象存儲起來,採用備忘錄模式來實現。

模式的擴展

在軟件開發中,有時將命令模式與前面學的組合模式聯合使用,這就構成了宏命令模式,也叫組合命令模式。宏命令包含了一組命令,它充當了具體命令與調用者的雙重角色,執行它時將遞歸調用它所包含的所有命令,其具體結構圖如圖 3 所示。

在這裏插入圖片描述

程序代碼如下:

package com.design.pattern.behaviorPattern.commandPattern;

import java.util.ArrayList;

public class CompositeCommandPattern {

    public static void main(String[] args) {
        AbstractCommand cmd1 = new ConcreteCommand1();
        AbstractCommand cmd2 = new ConcreteCommand2();
        CompositeInvoker invoker = new CompositeInvoker();
        invoker.add(cmd1);
        invoker.add(cmd2);
        invoker.execute();
    }
}
//抽象命令
interface AbstractCommand
{
    public void execute();
}

//接收者
class CompositeReceiver{
    public void action1(){
        System.out.println("接收者的action1()方法被調用...");
    }
    public void action2(){
        System.out.println("接收者的action2()方法被調用...");
    }
}

//樹葉構件: 具體命令1
class ConcreteCommand1 implements AbstractCommand{
    private CompositeReceiver receiver;

    public ConcreteCommand1() {
        receiver = new CompositeReceiver();
    }

    @Override
    public void execute() {
        receiver.action1();
    }
}
//樹葉構件: 具體命令2
class ConcreteCommand2  implements AbstractCommand{

    private CompositeReceiver receiver;

    public ConcreteCommand2() {
        receiver = new CompositeReceiver();
    }

    @Override
    public void execute() {
        receiver.action2();
    }
}

//樹枝構件: 調用者
class CompositeInvoker implements AbstractCommand{

    private ArrayList<AbstractCommand> children = new ArrayList<>();

    public void add(AbstractCommand command){
        children.add(command);
    }

    public void remove(AbstractCommand command){
        children.remove(command);
    }

    public AbstractCommand getChild(int index){
        return children.get(index);
    }


    @Override
    public void execute() {
        for (AbstractCommand abstractCommand :  children){
            abstractCommand.execute();
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章