鸣谢:《大话设计模式》
敏捷开发原则告诉我们,不要为代码添加基于猜测的、实际不需要的功能。如果不清楚一个系统是否需要命令模式,一般就不要着急去实现它,事实上,在需要的时候通过重构实现这个模式并不困难,只有在真正需要如撤销/恢复操作等功能时,把原来的代码重构为命令模式才有意义。
简介
是一种行为型设计模式
核心 就是 将 命令 抽象成一个对象 ;
将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。 命令模式将发送者和执行者之间完全解耦
主要组成部分:
- 命令(Command):命令是一个抽象类或接口,它定义了执行操作的方法。通常包括一个execute()方法,用于执行请求。
-
具体命令(Concrete Command):具体命令是命令接口的实现,它包含了对接收者的引用以执行具体操作。具体命令负责将请求参数化。
-
接收者(Receiver):接收者是执行实际操作的对象。它知道如何执行具体操作,并在接收到命令时执行这些操作。
-
调用者(Invoker):调用者是客户端的一部分,它负责创建命令对象并将其发送给接收者来执行请求。
-
客户端(Client):客户端是命令模式的使用者,它创建具体命令对象并设置其接收者。客户端将命令对象传递给调用者来执行请求。
工作流程
-
客户端创建具体命令对象,并将其与接收者关联,将请求参数化。
-
客户端将具体命令对象传递给调用者。
-
调用者接收到命令后,调用命令的execute()方法来执行具体操作。
-
接收者执行实际操作,根据命令中的参数执行相应的操作。
例子
结构
代码
命令(Command)
/**
* @author: lpy
* @Date: 2023/11/03
*/
public interface Command {
void excute();
}
具体命令(Concrete Command)
/**
* @author: lpy
* @Date: 2023/11/03
*/
public class PlayCommand implements Command{
private Video video;
public PlayCommand(Video video) {
this.video = video;
}
@Override
public void excute() {
video.play();
}
}
/**
* @author: lpy
* @Date: 2023/11/03
*/
public class StopCommand implements Command{
private Video video;
public StopCommand(Video v){
this.video = v;
}
@Override
public void excute() {
video.stop();
}
}
接收者(Receiver)
/**
* @author: lpy
* @Date: 2023/11/03
*/
public class Staff {
private List<Command> commands = new ArrayList<>();
public void addCommands(Command c){
commands.add(c);
}
public void excuteCommands(){
commands.forEach(o->{
o.excute();
});
commands.clear();
}
}
调用者(Invoker)
/**
* @author: lpy
* @Date: 2023/11/03
* @desc: 相当于书中waiter服务员的角色
*/
public class Video {
private String name;
public Video(String name){
this.name = name;
}
public void play(){
System.out.println(name+"播放");
}
public void stop(){
System.out.println(name+"停止播放");
}
}
客户端(Client)
/**
* @author: lpy
* @Date: 2023/11/03
*/
public class TestCommand {
public static void main(String[] args) {
Staff staff = new Staff();
Video video = new Video("视频");
staff.addCommands(new PlayCommand(video));
staff.addCommands(new PlayCommand(video));
staff.addCommands(new StopCommand(video));
staff.excuteCommands();
}
}
应用
可以和备忘录模式结合,用备忘录模式保存之前的命令
- 遥控器控制器:遥控器通常使用命令模式,每个按钮对应一个具体命令对象,当按下按钮时,命令对象执行相应的操作,例如控制电视、音响、灯光等。
-
菜单操作:图形用户界面(GUI)应用程序中,菜单项通常与具体命令相关联。用户点击菜单项时,相应的命令执行相应的操作,例如复制、粘贴、撤销等。
-
多级撤销和重做:命令模式允许记录执行的命令历史,支持撤销和重做操作。用户可以按需撤销一系列操作,然后再重做。
-
任务队列:命令模式支持将命令排队以便逐个执行。这在多线程应用程序和任务调度中非常有用。
-
日程调度:命令模式可以用于实现日程调度系统,用户可以创建计划任务,并在指定时间执行这些任务。
-
文件操作:命令模式可用于实现文件操作,例如创建文件、复制文件、删除文件等。每个文件操作可以表示为一个命令对象。
-
数据库事务管理:数据库事务可以看作一系列数据库操作的命令,可以将数据库操作封装成命令对象,从而支持事务的管理和回滚。
-
文本编辑器:文本编辑器可以使用命令模式来支持编辑、撤销、重做等功能。每个用户操作可以表示为一个命令对象。
-
机器人控制:命令模式可用于控制机器人的移动、抓取物体、执行任务等。每个机器人动作可以表示为一个命令对象。
-
工作流程引擎:工作流程引擎通常使用命令模式来定义和执行工作流程中的各个步骤。
命令模式优缺点
-
优点:
- 降低耦合
-
容易扩展新命令或一组命令
-
缺点:
- 命令的无限扩展会增加类的数量,提高系统的复杂度
7 大设计原则
不能全部遵守 , 也不能不遵守 , 注意平衡 功能 和 系统复杂度 , 找到最合适的一个点 ;
- 单一职责原则(Single Responsibility Principle - SRP):
这个原则规定一个类应该只有一个改变的理由,也就是说,一个类应该只有一个单一的职责。这有助于保持类的简单性和可维护性,因为每个类只需关注一个特定的功能。
- 开放-封闭原则(Open-Closed Principle - OCP):
开放-封闭原则要求系统中的软件实体(类、模块、函数等)应该对扩展开放,对修改封闭。这意味着当需要添加新功能时,应该通过扩展现有代码来实现,而不是修改原有代码。
- 里氏替换原则(Liskov Substitution Principle - LSP):
里氏替换原则规定,子类应该能够替换其父类,而不会影响程序的正确性。也就是说,子类应该继承父类的行为,但可以扩展或修改该行为,但不应该改变父类的行为。
- 接口隔离原则(Interface Segregation Principle - ISP):
接口隔离原则要求一个类不应该强迫其客户端依赖于它们不需要的接口。应该根据客户端的需求定义小而精确的接口,而不是大而笨重的接口。
- 依赖倒置原则(Dependency Inversion Principle - DIP):
依赖倒置原则要求高级模块不应该依赖于低级模块,它们都应该依赖于抽象。具体来说,这意味着应该通过抽象接口或抽象类来定义模块之间的依赖关系,而不是直接依赖于具体实现。
- 迪米特法则(Law of Demeter - LoD):
如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。
- 合成复用原则(Composite Reuse Principle - CRP):
合成复用原则鼓励通过组合(合成)已有的类来实现新功能,而不是通过继承现有的类。这样可以降低系统的耦合度,并且更加灵活。
源码位置
全部源码github.com/xs-alpha/designPattern
参考文献
- 《大话设计模式》
-
慕课视频《Java设计模式精讲 Debug方式+内存分析》
-
【设计模式】命令模式 ( 简介 | 适用场景 | 优缺点 | 代码示例 )
如有内容侵权,麻烦联系博主删除