The Ambiguous Command Antipattern
Jun 15th, 2005 by phil
The Command pattern is very powerful and flexible. Swing and Struts both make heavy use of the Command pattern in their respective action classes. However, it can be a little too flexible, and if you aren’t careful it can turn into the Ambiguous Command antipattern. This particularly nasty antipattern can take a nice, loosely-coupled design based on the Command pattern and turn it into an unmaintainable mess.
According to the Gang of Four book the intent of the Command pattern is to :
Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.
(See the PatternStories wiki for more background on the Command pattern.)
The Ambiguous Command antipattern takes two forms: ambiguous arguments and ambiguous return value. In the first case a command object takes so many parameters that developers trying to use it are never sure what the command will actually do. In the second case, a command object returns an Object or some other suitably generic type from its execute() method in order to return the results of the operation. However, because the result is generic a developer will have to cast to an expected return type and may have to deal with ugly consequences when an unexpected type is returned.
Consider a hypothetical system that implements a data access layer using the Command pattern. A client tier packages the request into an object and then sends the command object to the business tier where it is executed. The command object obviously needs to know what data to retrieve; it needs parameters. Since the execute() method (or equivalent) is generic there are two routes that you can take to add parameters: pass parameters to the constructor or add JavaBeans style properties to the command object itself. The second option is a good one, but this is where the Ambiguous Command begins to creep in. Here is an example class that displays this problem:
// Command interface has one method: Object execute();
public class AmbiguousCommand implements Command {
private int param1;
private String param2;
public AmbiguousCommand() {
}
public int getParam1() {
return param1;
}
public void setParam1(int param1) {
this.param1 = param1;
}
public String getParam2() {
return param1;
}
public void setParam2(String param2) {
this.param2 = param2;
}
// This method is specified in the interface...
public Object execute() {
// Do some work here...
// and return an object.
}
}
This design is OK as long as the execute() method behaves consistently. If we were writing a method instead of a command object we could overload the method with different parameter lists and have each overloaded method use a slightly different algorithm. It is tempting to add all of the parameters we might need to this command object and have the execute() method determine what needs to be done based on the values of the paramters. Down this path madness lay. This is where the ambiguity comes in; for a command object to remain usable it must behave consistently.
The second aspect of this antipattern is also visible in the example class. The execute() method returns an Object. Often it is not necessary for command objects to return a value, but sometimes you may wish to encapsulate some form of query in a command. The problem with this is that a query must return a value, so you have the command return an object to maintain the generic nature of the command only to discover that you still have to cast the return value to a usable type. Generics can help with this situation, but do not solve it completely. In the end, it is probably better to avoid having command objects return values.
Like many patterns, it is easy to get carried away when applying the Command pattern and use it inappropriately. It is important that your command objects behave consistently and that you not try to ‘overload’ them with too many parameters. Also, queries may be better encapsulated in a DAO or facade than in a command object. Just remember that with patterns there are no hard and fast rules, just guidelines and suggestions.




