命令模式如何将发送者与接收者解耦?

nit*_*.kk 6 java design-patterns command-pattern

Command 模式有一个 IReceiver 接口,其中有几个方法,并且与每个方法相对应,有具体的 Command 对象(实现一个接口ICommand)。

我读到客户端知道具体接收器和具体命令,并且通常是客户端在具体命令对象中设置接收器对象。那为什么说它解耦了发送者和接收者呢?

当客户端已经知道具体的接收者时,我觉得这不是松耦合,而且在这种情况下客户端可以直接调用接收者对象上的 API(方法)。

Rav*_*abu 3

您可以将命令模式工作流程视为如下。

  1. Command为所有命令声明一个接口,提供一个简单的execute()方法,要求命令的接收者执行操作。

  2. 他们Receiver知道如何执行请求。

  3. 持有Invoker一个命令,可以Command通过调用execute方法来执行请求。

  4. 创建并设置命令的a ClientConcreteCommandsReceiver

  5. 定义ConcreteCommand了动作和接收者之间的绑定。

  6. Invoker调用执行时ConcreteCommand,将在接收器上运行一项或多项操作。

查看示例代码以更好地理解事物。

public class CommandDemoEx{
    public static void main(String args[]){

        // On command for TV with same invoker 
        Receiver r = new TV();
        Command onCommand = new OnCommand(r);
        Invoker invoker = new Invoker(onCommand);
        invoker.execute();

        // On command for DVDPlayer with same invoker 
        r = new DVDPlayer();
        onCommand = new OnCommand(r);
        invoker = new Invoker(onCommand);
        invoker.execute();

    }
}
interface Command {
    public void execute();
}

class Receiver {
    public void switchOn(){
        System.out.println("Switch on from:"+this.getClass().getSimpleName());
    }
}

class OnCommand implements Command{

    private Receiver receiver;

    public OnCommand(Receiver receiver){
        this.receiver = receiver;
    }
    public void execute(){
        receiver.switchOn();
    }
}

class Invoker {
    public Command command;

    public Invoker(Command c){
        this.command=c;
    }
    public void execute(){
        this.command.execute();
    }
}

class TV extends Receiver{
    public TV(){

    }
    public String toString(){
        return this.getClass().getSimpleName();
    }
}
class DVDPlayer extends Receiver{
    public DVDPlayer(){

    }
    public String toString(){
        return this.getClass().getSimpleName();
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

java CommandDemoEx
Switch on from:TV
Switch on from:DVDPlayer
Run Code Online (Sandbox Code Playgroud)

回答你的问题:

我读过客户端了解具体接收器和具体命令,通常是客户端在具体命令对象中设置接收器对象。那为什么说它解耦了发送者和接收者

为了标准化词语,请将“发送者”替换为“调用者”。现在看一下代码。

  1. Invoker simply executes the ConcreteCommand(本例中为 OnCommand)通过传递 ConcreteReceiver。
  2. ConcreteCommand executes Command 通过ConcreteReceiver即ConcreteCommand defines binding between Action and Receiver.
  3. 如果你看到工作流程,Invoker不会随着额外的命令而改变,你可以在execute()Invoker的方法中添加业务逻辑,如java.lang.Thread,这已经解释如下。
  4. 这样Client (sender) and Receiver are loosely couple through Invoker, which has knowledge of what command to be executed

链接中的线程示例

您可以通过实现 Runnable 对象来创建线程。

Thread t = new Thread (new MyRunnable()).start();
Run Code Online (Sandbox Code Playgroud)

=>

 Invoker invoker = new Invoker(new ConcreteCommand());
 invoker.start() 
Run Code Online (Sandbox Code Playgroud)

并且您在 start() 中有逻辑来调用 ConcreteCommand.execute() ,在上面的情况下是 run() 。

start()方法会调用Thread中的run()方法。如果直接调用run()方法会发生什么?它不会被视为线程

与该线程的start()方法一样,您可以在Invoker中添加一些业务逻辑。

public synchronized void start() {
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();
        group.add(this);
        start0();
        if (stopBeforeStart) {
            stop0(throwableFromStop);
        }
    }

private native void start0(); // Native code is not here but this method will call run() method

public void run() {
    if (target != null) {
        target.run();
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑:

在您的最后一个查询中

这里我们创建了命令对象、接收者对象和调用者对象。然后将接收者对象传递给命令对象,然后将命令对象传递给调用者对象。我们为每个接收器执行此操作,就像我们在此处为电视和 DVD 播放器执行的操作一样。此外,在方法中,TV 和 DVDPlayer 的“主”对象是已知的并且实际上是被创建的。我们可以简单地执行 tvObject.switchOn() 和 dvdPlayer.switchOn()。命令模式有何帮助

客户无需担心Receiver班级的变化。Invoker直接作用于ConcreteCommand,它有Receiver对象。Receiver对象将来可能会siwtchOn从 ()变为 ()。switchOnDevice但客户端交互并没有改变。

如果您有两个不同的命令,例如switchOn() 和switchOff(),您仍然可以使用相同的Invoker.