命令模式

定义与特点

将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。两者通过命令对象进行沟通,这样方便将命令对象进行存储、传递、调用、增加与管理。

优点:

  • 用过引入中间件(抽象接口),降低系统的耦合度;
  • 扩展性良好,增加或删除命令非常方便。常用命令模式增加和删除命令不会影响其他类,满足“开闭原则”;
  • 可以实现宏命令。命令模式额可以与组合模式结合,将多个命令装配成一个组合命令。
  • 方便实现 Undo和Redo操作。操作模式可以与备忘录模式结合,实现命令的撤销与恢复。
  • 可以在现有命令的基础上,增加额外的功能。比如日志记录,结合装饰者模式会更加灵活。

缺点:

  • 会产生大量的具体命令类。因为每一个具体操作都需要设计一个具体命令类,增加了系统的复杂性。
  • 效果是,接收方的执行结果,为了以命令的形式进行架构、解耦请求与实现,引入了额外类型结构(引入请求方与抽象命令接口),增加代码结构理解上的难度。

    应用场景

  • 请求调用者需要与请求接收者解耦时,命令模式可以使调用者和接收者不直接交互

  • 系统随机请求命令或经常增加和删除命令时,该模式比较方便
  • 当系统需要执行一组操作时,该模式可以定义宏命令来实现该功能
  • 当系统需要支持命令的撤销(Undo)和恢复(Redo)操作时,可以将命令对象存储起来,采用备忘录模式来实现

    代码结构与实现

整个代码就是相当于,具体的业务功能放在了实现者/接收者的特定方法里面(action()),然后使用中间件具体命令类和对应的Receiver给封装起来,交给Invoker方法来实现。使用起来有点和抽象工厂模式有些类似

  • 抽象命令类(Command):声明执行命令的接口,拥有执行命令的抽象方法 execute()
  • 具体命令类(Concrete Command):具体实现类,拥有接收者对象,并通过调用接收者的功能来完成命令要执行的操作;
  • 实现者/接收者(Receiver):执行命令功能的相关操作,是具体命令对象业务的真正实现者;
  • 调用者/请求者(Invoker):是请求的发送者,它通常拥有很多的命令对象,并通过访问命令对象来执行相关请求,它不会执行访问接收者。

抽象命令接口

  1. public abstract class Command {
  2. public abstract void execute();
  3. }

实现者/接收者

  1. public class ReceiverA {
  2. public void action(){
  3. System.out.println("接收者的方法");
  4. }
  5. }
  6. public class ReceiverB {
  7. public void action(){
  8. System.out.println("接收者的方法B,其实就是具体的功能业务");
  9. }
  10. }

具体抽象命令类

  1. public class ConcreteCommandeA extends Command{
  2. private ReceiverA receiverA;
  3. public ConcreteCommandeA(){
  4. receiverA = new ReceiverA();
  5. }
  6. @Override
  7. public void execute() {
  8. //System.out.println("具体命令a");
  9. receiverA.action();
  10. }
  11. }
  12. public class ConcreteCommandeB extends Command{
  13. private ReceiverB receiver;
  14. public ConcreteCommandeB(){
  15. receiver = new ReceiverB();
  16. }
  17. @Override
  18. public void execute() {
  19. //System.out.println("具体命令B");
  20. receiver.action();
  21. }
  22. }

调用者/请求者

  1. public class Invoker {
  2. private Command command;
  3. public Invoker(Command command){
  4. this.command =command;
  5. }
  6. public void setCommand(Command co){
  7. this.command =co;
  8. }
  9. public void call(){
  10. System.out.println("调用者执行命令command");
  11. command.execute();
  12. }
  13. }

main

  1. public class CommandMain {
  2. public static void main(String[] args) {
  3. Command command = new ConcreteCommandeA();
  4. Invoker invoker = new Invoker(command);
  5. invoker.call();
  6. Command commandB = new ConcreteCommandeB();
  7. Invoker invokerB = new Invoker(commandB);
  8. invokerB.call();
  9. }
  10. }

调用者/请求者(另外一种写法)

  1. public class Invoker {
  2. private Command command;
  3. public Invoker(Command command){
  4. this.command =command;
  5. }
  6. public void setCommand(Command co){
  7. public class InvokerB {
  8. private Command commandA,commandB;
  9. public void setA(Command a){
  10. this.commandA =a;
  11. }
  12. public void setB(Command b){
  13. this.commandB =b;
  14. }
  15. public void callA(){
  16. commandA.execute();
  17. }
  18. public void callB(){
  19. commandB.execute();
  20. }
  21. }

main对应得调用方法

  1. public class CommandMainB {
  2. public static void main(String[] args) {
  3. Command coA =new ConcreteCommandeA();
  4. Command coB =new ConcreteCommandeB();
  5. InvokerB invokerB = new InvokerB();
  6. invokerB.setA(coA);
  7. invokerB.setB(coB);
  8. invokerB.callA();
  9. invokerB.callB();
  10. }
  11. }

命令模式的扩展(宏命令模式)

将命令模式与组合模式联合使用,这就构成了宏命令模式,也叫组合命令模式。宏命令包含了一组命令,它充当了具体命令与调用者的双重角色,执行它时将递归调用它所包含的所有命令

为了简便,其中的抽象接口,以及实现者和具体的命令类都是用上面的,这里就只写管理者和调用方法(main)

调用者/管理者

命令宏—-命令+组合(树)模式:
充当了调用者和命令者(管理者)。

  1. public class CompositeInvoker {
  2. private ArrayList<Command> children = new ArrayList<>();
  3. public void add(Command co){
  4. children.add(co);
  5. }
  6. public void remove(Command co){
  7. children.remove(co);
  8. }
  9. public Command getChildren(int i){
  10. return children.get(i);
  11. }
  12. public void excute(){
  13. for (Object obj:children) {
  14. ((Command)obj).execute();
  15. }
  16. }
  17. }

main

  1. public class MacroCommand {
  2. public static void main(String[] args) {
  3. ConcreteCommandeA cca = new ConcreteCommandeA();
  4. ConcreteCommandeB ccb = new ConcreteCommandeB();
  5. CompositeInvoker ci = new CompositeInvoker();
  6. ci.add(cca);
  7. ci.add(ccb);
  8. ci.excute();
  9. }
  10. }