命令模式实现或改编

时间:2015-07-28 15:03:09

标签: design-patterns command-pattern

我有不同的命令,它们都共享一些常见数据,因此我将其提取到超类Command。所有具体命令在实现Foo方法时对execute的对象进行操作。该命令的实际调用者是一个客户端TheClient,它创建所需命令的新对象并直接执行它。

我已经以这样的方式实现它,即客户端和inovker独立于命令中的实现细节,更重要的是,独立于类Foo

我有以下问题:

1)这是命令模式的实现,还是以某种方式采用它?

2)根据我对命令模式的理解,在这个例子中Foo是接收者。这是对的吗?

3)与纯命令模式相比,我已将调用者和客户端合并到同一个类中。有人说在命令模式中这是可以的,因为两者都是理论构造,在实际实现中它们可以在同一个类中。这是对的吗?

public abstract class Command {
  protected String a;
  protected Foo foo;
  public Command(String a) {
    this.a = a;
    this.foo = new Foo(a);
  }
  public abstract void execute();
}

public class StartCommand extends Command {
  private String b;
  public StartCommand(String a, String b) {
    super(a);
    this.b = b;
  }
  public void execute() {
    this.foo.doSomething("start " + a + " with " + b);
  }
}

// ... other commands ...

public class Foo {
  protected String name;
  public Foo(String name) {
    this.name = name;
  }
  public void doSomething(String action) {
    // does something...
  }
}

public class TheClient {
  public static void main(String[] args) {
    Command command = new StartCommand("x", "y");
    command.execute();
  }
}

2 个答案:

答案 0 :(得分:1)

1st question: Yes. It is an adaptation of the Command pattern.

2nd question: Foo is the receiver.

3rd question: Yes, the invoker is merged with the client class. Here comes a little problem. The Foo is not independent from the concrete StartCommand. It's true, that You have to modify the Command class both implementation, when a rename occurs on the Foo class for example, but the instantiation of the Foo should be somewhere in the main if You ask Uncle Bob. In Your example it's in the Command's constructor.

EDIT

You can instantiate Foo in somewhere else, than wrap it around with the ConcreteCommand. Than Invoker will latch around the command. Both wrapping around will embrace Decoupling, and Single Responsibility.

For example You can test Foo's every behaviour without being afraid of any side effect of the Command. In the test You create a Foo, test it around, no surprise. Testing the StartCommand You have to check if any method chaining happens. Like this:

public Command.SetAOfFoo(string a) {
    this.foo.SetA(a);
}

You have to check all Foo's properties to hold, that You checked in the first time with the unit tests of Foo. You will find some failing testcases because some calling to the StartCommand modified the state of Foo, and that lead to failing tests.

I know that is far away from the code that You provided, but this.foo = new Foo(a); is close to the imaginary this.foo.SetA(a) that I wrote as an example.

In Your example, there was a possible programming problem. Both the constructor of Foo and the StartCommand's constructor got the value 'a' as parameter. What if You make it public? And You are very surprised, by setting the 'a' in the StartCommand, the printout changes, but Foo behaves differently. The constructor of Foo stored the previous value, and You maybe forget to change of the code of the Command, to pass the new value to Foo, where You forget to make the 'a' public.

The whole example's headscratching can be avoided with removing the construction of Foo from Command.

答案 1 :(得分:0)

1)it seems to be adaption to command pattern.

2)Foo is receiver so it knows how to carry out request.doSomething()

3).They can be in same class but it violates some design principles like single resposibility.Purpose of client in command pattern is creating objects and injecting receivers.The role of invoker is to hold commands in some data structure and calling their execute method.Simply it schedules commands and you can do a lot of thing like logging,command history and so on.And putting this kind of logic into client is too much burden for client.