設(shè)計模式之命令模式

模式定義

命令模式(Command),將一個請求封裝為一個對象顶捷,從而使你可用不同的請求對客戶進(jìn)行參數(shù)化;對請求排隊或記錄請求日志,以及支持可撤銷的操作键畴。

類圖

命令模式類圖

Command:用來聲明執(zhí)行操作的接口。

abstract class Command {

    Receiver receiver;

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

    abstract void execute();
}

ConcreteCommand:將一個接收者對象綁定于一個動作突雪,調(diào)用接收者相應(yīng)的操作起惕,以實(shí)現(xiàn)Execute。

class ConcreteCommand extends Command {

    public ConcreteCommand(Receiver receiver){
        super(receiver);
    }

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

Invoker:要求該命令執(zhí)行這個請求咏删。

class Invoker {

    Command command;

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

    public void ExecuteCommand(){
        command.execute();
    }
}

Receiver:知道如何實(shí)施與執(zhí)行一個與請求相關(guān)的操作惹想,任何類都可能作為一個接收者。

class Receiver {
    
    public void action(){
        System.out.println("執(zhí)行請求督函!");
    }
    
}

客戶端代碼:

public static void main(String[] args) {
    Receiver receiver = new Receiver();
    Command command = new ConcreteCommand(receiver);
    
    Invoker invoker = new Invoker();

    invoker.setCommand(command);
    invoker.ExecuteCommand();
}

撤銷和重做

如果需要有命令撤銷和重做嘀粱,Command接口不止需要execute()方法,還要有undo()方法辰狡。

abstract class Command {

    //執(zhí)行命令和重做
    abstract void execute();

    //撤銷
    abstract void undo();

}

如果需要實(shí)現(xiàn)多次撤銷重做锋叨,需要調(diào)用方維護(hù)undo和redo兩個盛放Command的棧(用List實(shí)現(xiàn)),首次執(zhí)行一個Command時宛篇,執(zhí)行execute()并將其放入undo棧內(nèi)娃磺,同時要清空redo棧;當(dāng)執(zhí)行撤銷操作時把undo棧內(nèi)最上面一個Command拿出來執(zhí)行undo()叫倍,然后將其放入redo棧內(nèi)偷卧;執(zhí)行重做操作時把redo棧內(nèi)最上面一個Command拿出來執(zhí)行execute(),然后將其放入undo棧內(nèi)段标。

示例代碼:

class Invoker {

    //可撤銷的步數(shù)
    int undoCount;

    List<Command> undoList = new ArrayList();
    List<Command> redoList = new ArrayList();

    public Invoker(){
        undoCount = 5;
    }

    /**
     * 執(zhí)行新操作
     * @param command
     */
    public void executeCommand(Command command){
        
        command.execute();

        //保留最近undoCount次操作涯冠,刪除最早操作
        if (undoList.size() >= undoCount){
            undoList.remove(0);
        }

        undoList.add(command);

        //執(zhí)行新操作后清空redoList,因為這些操作不能恢復(fù)了
        redoList.clear();
    }

    /**
     * 執(zhí)行撤銷
     */
    public void undo(){
        if (undoList.size() <= 0){
            return;
        }

        //undoList中出棧第一個執(zhí)行
        Command command = undoList.get(undoList.size() - 1);
        command.undo();

        undoList.remove(command);
        redoList.add(command);
    }

    /**
     * 執(zhí)行重做
     */
    public void redo(){
        if (redoList.size() <= 0){
            return;
        }

        //redoList中出棧第一個執(zhí)行
        Command command = redoList.get(redoList.size() - 1);
        command.execute();

        redoList.remove(command);
        undoList.add(command);

    }
    
}

如果命令棧中存放很多Command對象會占用大量內(nèi)存逼庞,所以用undoCount做限制蛇更。

應(yīng)用場景

  1. 系統(tǒng)需要將請求調(diào)用者和請求接收者解耦,使得調(diào)用者和接收者不直接交互。
  2. 系統(tǒng)需要在不同的時間指定請求派任、將請求排隊和執(zhí)行請求砸逊。
  3. 系統(tǒng)需要支持命令的撤銷(Undo)操作和恢復(fù)(Redo)操作。
  4. 系統(tǒng)需要將一組操作組合在一起掌逛,即支持宏命令师逸。

優(yōu)缺點(diǎn)

優(yōu)點(diǎn)

  • 降低對象之間的耦合度。
  • 新的命令可以很容易地加入到系統(tǒng)中豆混。
  • 可以比較容易地設(shè)計一個組合命令篓像。

缺點(diǎn)

  • 使用命令模式可能會導(dǎo)致某些系統(tǒng)有過多的具體命令類。因為針對每一個命令都需要設(shè)計一個具體命令類皿伺,因此某些系統(tǒng)可能需要大量具體命令類员辩。

JDK中的命令模式

java.lang.Runnable

看下示例代碼

MyRunnable:

class MyRunnable implements Runnable {

    @Override
    public void run() {
        System.out.println("開始執(zhí)行");
    }
}

main:

public static void main(String[] args) {
    //相當(dāng)與命令模式的Command command = new ConcreteCommand();
    MyRunnable myRunnable = new MyRunnable();

    //相當(dāng)與命令模式的new Invoker()后執(zhí)行invoker.setCommand(command)
    Thread thread = new Thread(myRunnable);
    thread.start();
}

這段代碼中,

Runnable相當(dāng)于抽象命令接口Command鸵鸥,run()方法相當(dāng)于Command的execute()奠滑;

MyRunnable對象相當(dāng)于具體命令對象ConcreteCommand;

Thread對象對應(yīng)命令模式的Invoker,new Thread(myRunnable)相當(dāng)于通過setCommand方法傳入命令對象妒穴。

你可能會說沒有找到Receiver宋税,因為MyRunnable已經(jīng)擁有要執(zhí)行的具體邏輯了,這里不需要接收者讼油。

命令模式與策略模式的區(qū)別

  • 目標(biāo)不同:策略模式主要針對統(tǒng)一動作杰赛,采用不同的實(shí)現(xiàn)方式;而命令模式針對不同命令汁讼。
  • 打個比方:命令模式相當(dāng)于菜單里的復(fù)制淆攻、移動、壓縮等嘿架;而策略模式是其中一個功能比如:壓縮瓶珊,的不同種實(shí)現(xiàn)。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末耸彪,一起剝皮案震驚了整個濱河市伞芹,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蝉娜,老刑警劉巖唱较,帶你破解...
    沈念sama閱讀 216,324評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異召川,居然都是意外死亡南缓,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評論 3 392
  • 文/潘曉璐 我一進(jìn)店門荧呐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來汉形,“玉大人纸镊,你說我怎么就攤上這事「沤” “怎么了逗威?”我有些...
    開封第一講書人閱讀 162,328評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長岔冀。 經(jīng)常有香客問我凯旭,道長,這世上最難降的妖魔是什么使套? 我笑而不...
    開封第一講書人閱讀 58,147評論 1 292
  • 正文 為了忘掉前任罐呼,我火速辦了婚禮,結(jié)果婚禮上童漩,老公的妹妹穿的比我還像新娘弄贿。我一直安慰自己,他們只是感情好矫膨,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著期奔,像睡著了一般侧馅。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上呐萌,一...
    開封第一講書人閱讀 51,115評論 1 296
  • 那天馁痴,我揣著相機(jī)與錄音,去河邊找鬼肺孤。 笑死罗晕,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的赠堵。 我是一名探鬼主播小渊,決...
    沈念sama閱讀 40,025評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼茫叭!你這毒婦竟也來了酬屉?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,867評論 0 274
  • 序言:老撾萬榮一對情侶失蹤揍愁,失蹤者是張志新(化名)和其女友劉穎呐萨,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體莽囤,經(jīng)...
    沈念sama閱讀 45,307評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡谬擦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了朽缎。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片惨远。...
    茶點(diǎn)故事閱讀 39,688評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡蔚舀,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出锨络,到底是詐尸還是另有隱情赌躺,我是刑警寧澤,帶...
    沈念sama閱讀 35,409評論 5 343
  • 正文 年R本政府宣布羡儿,位于F島的核電站礼患,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏掠归。R本人自食惡果不足惜缅叠,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望虏冻。 院中可真熱鬧肤粱,春花似錦、人聲如沸厨相。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蛮穿。三九已至庶骄,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間践磅,已是汗流浹背单刁。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留府适,地道東北人羔飞。 一個月前我還...
    沈念sama閱讀 47,685評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像檐春,于是被迫代替她去往敵國和親逻淌。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評論 2 353

推薦閱讀更多精彩內(nèi)容