设计模式第6式:命令模式
创始人
2024-05-22 21:11:42
0

前言

命令模式关注这样一种场景:指令发布者无需关注指令是怎么执行的,只需要指定具体的执行者,具体的指令由执行者来完成。命令模式将指令发布动作和指令执行动作解耦。

我在刚开始学习命令模式的时候,比较困惑它的使用场景。它不像之前讲的工厂模式,观察者模式那样有明确的使用场景。其实可以将命令模式应用在这个场景就比较好理解了:软件项目经理不可能完成所有的开发任务,他将软件开发的不同阶段设置为不同的指令,比如软件设计指令、软件开发指令、软件测试指令,然后将不同的指令指定不同的人员。

正文

1、餐厅点单场景

我们以面向对象的思维来看真实的餐厅点单场景是什么样的:
1、顾客创建一个订单(createorder());
2、招待者拿走订单(takeOrder());放在后厨柜台,然后通知厨师备餐(orderUp());
3、厨师备餐出餐(doSomething(),output);

我们来分析其中的角色:
1、把订单想象成封装了备餐请求的对象,对象是可以传递的。订单接口只包含一个方法orderUp(),这个方法封装备餐需要的所有动作。订单实现类内有一个厨师的引用。这些都被封装起来,所以招待者不需要知道订单上有什么,也不需要谁来备餐。
2、招待者的工作只是接受订单,然后调用订单的orderUp方法。
3、厨师具有备餐的技能。厨师和招待者是完全解耦的。

将以上场景进行抽象成命令模式,那这个模式的核心就是将“发出请求的对象”和“接受与执行请求的对象”解耦。

2、将餐厅抽象成命令模式

我们重新来讲命令模式:
在这里插入图片描述
三个角色:
1、调用者:发起命令的角色;
2、命令:封装执行者及其方法;
3、执行者:真正的动作实施人;

3、实现命令模式

上面讲了一堆,我们来实际写一个命令模式。首先,我们需要一个命令类接口,接口很简单,只有一个执行方法。

public interface Command {void execute();
}

然后,实现一个打开电灯的具体命令类。

public class LightOnCommand implements Command {Light light; // 命令类持有接受者的引用public LightOnCommand(Light light) {this.light = light;}@Overridepublic void execute() {  // 封装了接受者的一系列方法light.on(); // 这里可以编排多个方法}
}

命令对象持有了一个接受者,它是实际干活的。

public class Light {public void on() {sout("打开灯");}
}

有了命令类后,我们来看怎么使用它。我们来创建一个调用者。

public class Controller {Command command; // 持有一个命令对象public void setCommand(Command command) { // 可设置具体命令对象this.command = command;}public void pressButton() { // 调用者按下按钮就能发起命令command.execute(); // 调用命令对象封装好的方法}
}

最后,我们来验证一下调用者怎么执行。

public class Test {main() {Controller contr = new Controller();Light light = new Light();LightCommand lightcomm = new LightCommand(light); //命令封装了接受者和一系列实现动作contr.setCommand(lightcomm); // 调用者配置命令contr.pressButton(); // 调用者执行方法}
}

4、怎么理解“命令”

通过上面这个简单的例子,已经完整展示了命令模式的全貌。如果没有“命令”这个类,那么上面这个例子将是什么样的呢?那么调用者将要直接面对千百个功能复杂的接受者,调用者将要创建出多个接受者,并排列组合一系列方法。这会导致调用者类非常复杂,难以维护,后续改变功能或新增功能都要改动调用者。

现在使用命令模式后,将调用者和接受者解耦开,调用者只关注要给谁下命令,由具体的命令对象来编排众多调用者及其方法。

那么怎么理解“命令”呢?万物皆对象,“命令”首先是一个对象,它做了什么呢?它的重点工作就是编排功能,它可以持有真正执行动作的对象,然后在execute()方法中编排方法。

上面的案例中,调用者按下按钮就会打开灯,如果我想改变按钮的功能呢?比如改成关灯,或者先关灯再拉窗帘。这种情况下,调用者Controller的代码是不用改动的,我们要做的是新增命令类,然后在命令类的execute()方法中改成调用执行者的关灯方法,或者先调关灯方法再调拉窗帘方法。这样就真正做到了对修改关闭,对扩展开放。

5、命令模式在Java中的应用

Java中的线程类Thread就是使用命令模式的典型案例。线程启动的方法是Thread.start(),线程具体做什么事Thread类不关心,是在Runnable接口的run()方法中实现的。

类比上面讲的例子,Thread类就是调用者,它只管调用start()方法来启动线程,线程做什么由命令来执行;Runnable接口就是命令类Command,里面的run()方法就是命令类的execute()方法,具体执行什么由Runnable的实现类进行实现。这样一来,Thread类永远不用变化,试想一下如果Thread类需要改变,它怎么作为JDK中的工具类供大家使用。

总结

命令模式常用于解耦动作发起者和动作执行者,并提供了编排动作的功能。

我一开始学习设计模式的时候,容易分不清“客户端”是谁。比如命令模式中我会认为调用者Controller是“客户端”,观察者模式中的主题Subject是“客户端”。之所以会这样认为,是因为它们中有一个触发动作的方法,比如Controller中按按钮动作,主题Subject中发布事件的方法。其实它们是作为各自模式的一部分,只是它们作为门面直接面向了客户端,真正的客户端应该是调用它们的类。有没有发现,很多设计模式的目标就是保持这个门面代码不用修改,去扩展门面后面的类。

相关内容

热门资讯

南丹山作文 南丹山作文  在学习、工作或生活中,大家都不可避免地要接触到作文吧,借助作文可以宣泄心中的情感,调节...
天目山游记作文 天目山游记作文  在日常的学习、工作、生活中,大家总免不了要接触或使用作文吧,借助作文可以宣泄心中的...
女孩的故事优秀作文800字 女孩的故事优秀作文800字  在一个飘雨的季节,一个孤苦伶仃的小女孩只穿着一件单衣,她的怀中抱着一个...
中秋节的作文400字 【实用】中秋节的作文400字6篇  无论在学习、工作或是生活中,大家对作文都再熟悉不过了吧,借助作文...
故事会 故事会故事会正文:故事会故事会广西区南宁市 新兴学校 三(1)班 梁缘今天下午有一节语言课,张老师走...
超越自我的作文 关于超越自我的作文今年是我国成立的第62个年头,在这个举国欢庆的日子里,新东方特意整理收集了大量的有...
生活的启示作文 生活的启示作文(精选55篇)  在平平淡淡的日常中,大家一定都接触过作文吧,借助作文可以宣泄心中的情...
忙碌的暑假作文 忙碌的暑假作文(精选3篇)  无论在学习、工作或是生活中,大家总免不了要接触或使用作文吧,作文要求篇...
我的暑假生活作文450字 我的暑假生活作文450字(精选34篇)  在日常学习、工作抑或是生活中,大家都跟作文打过交道吧,作文...
中国传统节日作文700字 中国传统节日作文700字(精选51篇)  作文是经过人的思想考虑和语言组织,通过文字来表达一个主题意...
未来的海洋作文 有关未来的海洋作文合集六篇  在学习、工作或生活中,大家总少不了接触作文吧,借助作文可以宣泄心中的情...
林志炫《你永远不知道》歌词 林志炫《你永远不知道》歌词  久违的声线、从容的台风,以一首《你永远不知道》回归的林志炫在亮相瞬间就...
春节大扫除的作文 春节大扫除的作文(集合15篇)  在学习、工作乃至生活中,大家都跟作文打过交道吧,作文是经过人的思想...
孔明灯 孔明灯孔明灯1  今天,是一年一度的中秋佳节。盼到傍晚,我们一家来爷爷奶奶家吃了顿晚饭,舅妈提议一起...
感恩父亲节作文 感恩父亲节作文13篇  在日常生活或是工作学习中,大家对作文都再熟悉不过了吧,作文是从内部言语向外部...
寒假中的一件事作文 寒假中的一件事作文(通用31篇)  在生活、工作和学习中,大家都有写作文的经历,对作文很是熟悉吧,借...
清联家杜文秀题戏台对联 清联家杜文秀题戏台对联  演戏看戏心中有戏;  修枝打枝节外生枝。  ——题戏台(1)  杜文秀还喜...
国庆见闻作文600字 国庆见闻作文600字6篇国庆见闻作文600字1  今天是“十·一”国庆节,天气非常晴朗,我与爸爸妈妈...
七夕情人节的名言警句 关于七夕情人节的名言警句  天阶夜色凉如水,坐看牵牛织女星。小编收集了关于七夕情人节的`名言警句,欢...