In general, an object-oriented application consists of a set of interacting objects each offering limited, focused functionality.
In terms of implementation, the application may depend on a designated object that invokes methods on these objects by passing the required data as arguments. This designated object can be referred to as an invoker as it invokes operations on different objects.
The invoker may be treated as part of the client application. The set of objects that actually contain the implementation to offer the services required for the request processing can be referred to as Receiver objects.
(client_invoker_receiver)
In this design, the application that forwards the request and the set of Receiver objects that offer the services required to process the request are closely tied to each other in that they interact with each other dir ectly. This could result in a set of conditional if statements in the implementation of the invoker.
if (RequestType=TypeB){ //do something }
When a new type of feature is to be added to the application, the existing code needs to be modified and it violates the basic object-oriented open-closed principle.
Using the Command pattern, the invoker that issues a request on behalf of the client and the set of service-rendering Receiver objects can be decoupled. The Command pattern suggests creating an abstraction for the processing to be carried out or the action to be taken in response to client requests.
A given Command object is responsible for offering the functionality required to process the request it represents, but it does not contain the actual implementation of the functionality. Command objects make use of Receiver objects in offering this functionality.
(client_invoker_receiver_abstraction)
When the client application needs to offer a service in response to user (or other application) interaction:
- 1. It creates the necessary Receiver objects.
- 2. It creates an appropriate Command object and configures it with the Receiver objects created in Step 1.
- 3. It creates an instance of the invoker and configures it with the Command object created in Step 2.
- 4. The invoker invokes the execute() method on the Command object.
- 5. As part of its implementation of the execute method, a typical Command object invokes necessary methods on the Receiver objects it contains to provide the required service to its caller.
In the new design:
- The client/invoker does not directly interact with Receiver objects and therefore, they are completely decoupled from each other.
- When the application needs to offer a new feature, a new Command object can be added. This does not require any changes to the code of the invoker. Hence the new design conforms to the open-closed principle.
- Because the request is designed in the form of an object, it opens up a whole new set of possibilities such as:
(1) – Storing a Command object to persistent media:
– To be executed later.
– To apply reverse processing to support the undo feature.
(2) Grouping together different Command objects to be executed as a single unit.
Example
Let us build an application that simulates the working of an FTP client. In Java, a simple FTP client user interface can be designed using:
- Two JList objects for the local and remote file systems display
- Four JButton objects for initiating different types of requests such as upload, download, delete and exit
(command_pattern_gui_example)
Let us define an abstraction in the form of a CommandInterface interface for the functionality associated with different button objects in the FTP client UI for avoiding inelegant conditional statements and as more button and menu item objects are added to the FTP UI.
interface CommandInterface {
public void processEvent();
}
Different button objects themselves can implement this interface and behave as individual command objects. But this is not recommended as: so lets define it as follows.
(button_class_hirachy)
The FTP UI can be built using objects of this new set of JButton subclasses. The rest of the application remains unchanged and the actionPerformed method gets highly simplified to a mere two lines of code.
class buttonHandler implements ActionListener {
public void actionPerformed(ActionEvent e) {
CommandInterface CommandObj =
(CommandInterface) e.getSource();
CommandObj.processEvent();
}
}
In the new design whenever a new button or a menu item is to be added, a new Command object needs to be created as an implementer of the CommandInterface. The new Command object can be added to the application in a seamless manner without requiring changes to the existing actionPerformed method code. On the negative side, this results in a larger number of classes.
class DownloadButton extends JButton
implements CommandInterface {
public void processEvent() {
int index = remoteList.getSelectedIndex();
String selectedItem =
remoteList.getSelectedValue().toString();
((DefaultListModel) remoteList.getModel()).remove(
index);
((DefaultListModel) localList.getModel()).addElement(
selectedItem);
}
public DownloadButton(String name) {
super(name);
}
}
Example 2
Let us build an application to manage items in a library item database. Typical library items include books, CDs, videos and DVDs. These items are grouped into categories and a given item can belong to one or more categories. For example, a new movie video may belong to both the Video category and the NewReleases category.
Let us define two classes Item and Category
let us suppose that the library item management application deals only with adding and deleting items. Applying the Command pattern, the action to be taken to process add item and delete item requests can be designed as implementers of a common CommandInterface interface.
The CommandInterface implementers AddCommand and DeleteCommand
(command_library_system_classes)
(command_library_system_seq)
No comments:
Post a Comment