命令模式简介
命令模式定义
命令模式(Command Pattern)是一种在面向对象程序设计中常用的行为型设计模式。命令模式的核心思想在于将请求封装成一个对象,从而使发出请求的责任和执行请求的责任分割开。它可以让请求发送者和请求接收者之间消除彼此之间的耦合关系。这种模式的关键在于将请求的行为参数化,使得不同的请求可以在不同时间由不同的对象来处理。同时,它支持对请求排队、记录请求日志,以及实现可撤销的操作。
命令模式包含以下角色:
- 命令角色(Command):这是一个抽象类或接口,它定义了执行命令的方法,通常包含执行(execute)方法,但不实现具体的命令行为。
- 具体命令角色(Concrete Command):实现了命令接口,持有接收者的引用,并在执行方法中调用接收者的方法来执行具体的命令行为;实现execute()方法,负责调用接收者的相应操作。
- 接收者角色(Receiver):接收者是执行命令的对象,它实现了命令所需要执行的具体操作。接收者可以有多个,每个接收者都可以执行不同的操作。
- 请求者角色(Invoker):请求者持有命令对象的引用,并通过调用命令的execute()方法来执行请求。它不需要知道命令的具体实现细节,只需知道如何调用命令对象。负责调用命令对象执行请求,相关的方法叫做行动方法(如action())。
- 客户端(Client):创建具体命令对象并设置其接收者,将命令对象传递给调用者执行请求。
命令模式优缺点:
优点:
- 解耦合:命令模式降低了调用者和接收者之间的耦合度。调用者只需知道命令的接口,而不需要知道命令的具体实现细节,从而降低系统的耦合度。
- 可扩展性:新的命令可以很容易地添加到系统中,而无需修改现有的代码结构,从而不影响现有的命令,符合开闭原则。
- 支持撤销和重做:由于命令被封装成对象,在具体命令类中保存状态,可以轻松地实现命令的撤销和重做功能。
- 日志记录:可以在命令对象中增加日志记录功能,记录命令的执行情况。这对于系统的调试和审计非常有帮助。
- 容易实现队列和宏命令:可以创建宏命令(复合命令),即把多个简单命令组合成一个更复杂的命令。
- 更好的安全性:命令模式可以帮助限制对敏感方法的访问权限,因为命令接口可以隐藏这些方法的实现细节。
缺点:
- 过多的具体命令类:每个具体命令都需要一个单独的类,可能会导致类的数量增加。如果系统中的命令种类非常多,可能会导致产生大量的具体命令类
- 系统复杂性:引入了额外的类和对象,增加了系统的复杂性。
- 执行效率:由于命令被封装成对象,可能会导致执行效率略有降低,特别是在需要大量命令对象的情况下。
- 可能增加对象数量:为了支持撤销操作,每个命令可能需要保存额外的状态信息,这会增加对象的数量。
使用场景
- 撤销和重做操作:当需要支持撤销和重做操作时,可以使用命令模式将每个操作封装成一个命令对象,从而实现撤销和重做功能。
- 队列请求:当需要将请求排队、记录请求日志、支持事务等场景时,可以使用命令模式将请求封装成命令对象,然后由调用者依次执行这些命令对象。
- 宏命令:当需要将多个简单命令组合成一个复合命令时,可以使用命令模式。
- 远程控制和自动化:在远程控制系统中,可以使用命令模式来发送远程指令给接收设备。
- 日程安排系统:在日程安排系统中,可以使用命令模式实现对日程的增加、删除、修改等操作,支持撤销和重做功能。
- GUI 应用程序:在图形用户界面中,命令模式可以用来处理用户的输入事件,例如按钮点击、菜单选项选择等
- 日志记录和历史追踪:命令模式可以用来记录操作的历史,这对于审计和调试非常有用。
以下举一个命令模式的例子:
下面通过一个简单的例子来演示。
假如我们自己开发一个音乐播放器,有播放功能、切换上一首功能、切换下一首功能、暂停功能,我们自己去操作的时候并不是直接调用音乐播放器的方法,而是通过一个控制条去传达指令给播放器内部。那么每个按钮就相当于是对一条命令的封装。
创建接收者角色(Receiver)
java
/**
* 音乐播放器,作为接收者角色(Receiver)
*/
public class MusicPlayer {
public void play() {
System.out.println("播放");
}
public void stop() {
System.out.println("暂停");
}
public void previousSong() {
System.out.println("拖动进度条");
}
public void nextSong() {
System.out.println("停止播放");
}
}
创建命令角色(Command)
java
/**
* 命令角色(Command)
*/
public interface Command {
void execute();
}
创建4个具体命令角色(Concrete Command),分别是实现播放,暂停,上一首,下一首的操作
java
/**
* 播放操作
*/
public class Play implements Command{
private MusicPlayer musicPlayer;
public Play(MusicPlayer musicPlayer) {
this.musicPlayer = musicPlayer;
}
@Override
public void execute() {
musicPlayer.play();
}
}
java
/**
* 暂停操作
*/
public class Stop implements Command{
private MusicPlayer musicPlayer;
public Stop(MusicPlayer musicPlayer) {
this.musicPlayer = musicPlayer;
}
@Override
public void execute() {
musicPlayer.stop();
}
}
java
/**
* 切换上一首歌操作
*/
public class PreviousSong implements Command{
private MusicPlayer musicPlayer;
public PreviousSong(MusicPlayer musicPlayer) {
this.musicPlayer = musicPlayer;
}
@Override
public void execute() {
musicPlayer.previousSong();
}
}
java
/**
* 切换下一首歌操作
*/
public class NextSong implements Command{
private MusicPlayer musicPlayer;
public NextSong(MusicPlayer musicPlayer) {
this.musicPlayer = musicPlayer;
}
@Override
public void execute() {
musicPlayer.nextSong();
}
}
创建请求者角色(Invoker)
java
import java.util.ArrayList;
import java.util.List;
/**
* 请求者角色(Invoker)
*/
public class Action {
private List<Command> actions = new ArrayList<Command>();
public void addAction(Command action){
actions.add(action);
}
public void execute(Command action){
action.execute();
}
public void executes(){
for (Command action:actions) {
action.execute();
}
actions.clear();
}
}
创建客户端(Client)
java
/**
* 客户端(Client)
*/
public class Client {
public static void main(String[] args) {
MusicPlayer musicPlayer = new MusicPlayer();
Action action = new Action();
//立即执行操作
action.execute(new Play(musicPlayer));
action.execute(new Stop(musicPlayer));
action.execute(new PreviousSong(musicPlayer));
action.execute(new NextSong(musicPlayer));
System.out.println("=========================");
// 将方法添加到列表中,方便执行多条操作
// 例如音乐播放器点下一首时,会切换下一首歌,并进行播放,这就设计一个按钮就行,然后这个按钮可以直接实现,切换下一首歌并播放的操作
Action actionOther = new Action();
actionOther.addAction(new NextSong(musicPlayer));
actionOther.addAction(new Play(musicPlayer));
actionOther.executes();
}
}
输出结果如下所示:
java
播放
暂停
切换到上一首歌
切换到下一首歌
=========================
切换到下一首歌
播放
在上述例子中,
展示了命令模式在音乐播放器中的应用。MusicPlayer 类作为接收者,提供了播放、暂停、上一首和下一首的操作。Command 接口定义了命令的通用执行方法。四个具体命令类 (Play, Stop, PreviousSong, NextSong) 实现了这些操作,并持有 MusicPlayer 的引用。Action 类作为调用者,管理命令列表并执行命令。客户端通过 Action 对象立即执行单个命令或批量执行一系列命令,实现了命令的解耦和灵活管理。
总而言之:
命令模式是一种行为设计模式,用于将请求封装为对象,以便使用不同的请求对客户端进行参数化,支持命令队列、日志记录和撤销操作。核心概念包括命令接口(Command)、具体命令类(Concrete Command)、接收者(Receiver)和调用者(Invoker)。命令接口定义了所有命令共有的执行方法,具体命令类实现了命令接口并封装了接收者对象的引用,接收者执行实际的业务逻辑,调用者则请求命令对象执行命令。命令模式的优势在于降低调用者与接收者之间的耦合度,支持撤销操作和宏命令,易于扩展新命令。但同时也可能导致产生大量具体命令类,客户端需要管理命令对象,且可能存在性能开销。适用于GUI应用程序、事务处理、宏命令、命令行界面和需要撤销操作的场景。
以上代码下载请点击该链接:https://github.com/Yarrow052/Java-package.git