我有不同的命令,它们都共享一些常见数据,因此我将其提取到超类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();
}
}
答案 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.