参考:https://blog.csdn.net/weixin_48052161/article/details/119463786
为什么需要命令模式?
在我们的软件开发系统中,行为请求者和真正的执行者通常都是一种紧耦合关系,但是这种情况下,当我们需要修改行为时,如需要撤销或者重做时,只能修改请求者的源代码,
命令模式会通过在行为请求者和执行者之间引入一个抽象接口来将请求者和执行者进行解耦,这样如果需要修改行为时,只需要增加对应行为的命令就可以了,完全不需要修改请求者的源代码。
通过在你的请求者和真正的执行者之间加上了一个中间人的角色,来达到分离耦合的目的。通过对中间人角色的特殊设计来形成不同的模式。
命令模式的目的就是达到命令的发出者和执行者之间解耦,实现请求和执行分开
代码演示:
我们对最普通的文本文字,做一系列处理命令
包括复制文本内容,插入指定文本内容,删除指定长度文字内容
并且可以撤销以上操作,实现回滚.
继承的父类命令类command:
package com.example.dtest.design23.command;
public abstract class Command {
public abstract void doit(); //exec run
public abstract void undo();
}
具体的实现命令类:
复制命令类:
package com.example.dtest.design23.command;
public class CopyCommand extends Command{
Content c;
public CopyCommand(Content c){
this.c = c;
}
@Override
public void doit() {
c.msg = c.msg + c.msg;
}
@Override
public void undo() {
c.msg = c.msg.substring(0,c.msg.length()/2);
}
}
删除命令类:
package com.example.dtest.design23.command;
public class DeleteCommand extends Command{
Content c;
String deleted;
public DeleteCommand(Content c){
this.c = c;
}
@Override
public void doit() {
deleted = c.msg.substring(0,5);
c.msg = c.msg.substring(5,c.msg.length());
}
@Override
public void undo() {
c.msg = deleted +c.msg;
}
}
插入命令类:
package com.example.dtest.design23.command;
public class InsertCommand extends Command{
Content c;
String strToInsert = "http://www.mashibing.com";
public InsertCommand(Content c){
this.c = c;
}
@Override
public void doit() {
c.msg = c.msg + strToInsert;
}
@Override
public void undo() {
c.msg = c.msg.substring(0,c.msg.length() - strToInsert.length());
}
}
工具类:
package com.example.dtest.design23.command;
public class Content {
String msg = "hello everybody";
}
门面责任链类:
package com.example.dtest.design23.command;
import java.util.ArrayList;
import java.util.List;
public class ContextInvoker {
//责任链模式
private List<Command> commands = new ArrayList<>();
public ContextInvoker(List<Command> commandList){
this.commands = commandList;
}
/*
* 执行指定命令
* */
public void execute(Command command){
command.doit();
}
/*
* 执行所有doit命令
* */
public void doitAll(){
for(Command command:commands){
command.doit();
}
}
/**
* 执行所有undo命令
*/
public void undoAll(){
for(int i= commands.size()-1; i>=0; i--) {
commands.get(i).undo();
}
}
}
测试类:
package com.example.dtest.design23.command;
import java.util.ArrayList;
import java.util.List;
public class CommandTest {
public static void main(String[] args) {
// 创建一个接受者对象
Content content = new Content();
System.out.println("原始文字内容: ==> " + content.msg);
// 创建一系列命令,来对接受者执行操作
List<Command> commands = new ArrayList<>();
commands.add(new InsertCommand(content));
commands.add(new CopyCommand(content));
commands.add(new DeleteCommand(content));
// 创建一个请求者角色
ContextInvoker invoker = new ContextInvoker(commands);
// 发送命令doit,执行一系列命令
invoker.doitAll();;
System.out.println("执行插入,复制,删除后的内容: == >"+ content.msg);
// 发送命令undo,执行操作回滚
invoker.undoAll();
System.out.println("执行undo后的内容: ==> "+ content.msg);
}
}
使用场景:
现实语义中存在具备“命令”的操作,如:dos命令,shell命令
数据库中的事务机制的底层实现
需要支持命令宏(即命令组合)操作。
命令的撤销和恢复:增加相应的撤销和恢复命令的方法(比如数据库中的事务回滚)
优点:
通过引入中间件(抽象接口),解耦了命令请求与实现。降低了系统耦合度
扩展性良好,可以很容易地增加新命令。新的命令可以很容易添加到系统中去。
支持组合命令,支持命令队列。
可以在现有命令的基础上,增加额外功能,比如日志记录,结合装饰器模式会更加灵活
缺点:
具体命令类可能过多。
命令模式的结果其实就是接收方的执行结果,但是为了以命令的形式进行架构、解耦请求与实现,引入额外类型结构(引入请求方与抽象命令接口)。
增加了理解上的困难。不过这也是设计模式的通病抽象必然会额外增加类的数量;代码抽离肯定比代码聚合更难理解。