340 likes | 536 Views
Command. Explained. Intent. Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations. Behavioral Pattern
E N D
Command Explained
Intent • Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations. • Behavioral Pattern • A command object encapsulates a request by binding together a set of actions on a specific receiver. • When two objects communicate, often one object is sending a command to the other object to perform a particular function. The most common way to accomplish this is for the first object (the “issuer”) to hold a reference to the second (the “recipient”). The issuer executes a specific method on the recipient to send the command. • An object-oriented callback.
Motivation • Sometimes it's necessary to issue requests to objects without knowing anything about the operation being requested or the receiver of the request. For example, user interface toolkits include objects like buttons and menus that carry out a request in response to user input. • But the toolkit can't implement the request explicitly in the button or menu, because only applications that use the toolkit know what should be done on which object. • As toolkit designers we have no way of knowing the receiver of the request or the operations that will carry it out
Motivation • The Command pattern lets toolkit objects make requests of unspecified application objects by turning the request itself into an object. • This object can be stored and passed around like other objects. The key to this pattern is an abstract Command class, which declares an interface for executing operations. In the simplest form this interface includes an abstract Execute operation. • Concrete Command subclasses specify a receiver-action pair by storing the receiver as an instance variable and by implementing Execute to invoke the request. • The receiver has the knowledge required to carry out the request
Motivation • Menus can be implemented easily with Command objects. Each choice in a Menu is an instance of a Menu Item class. An Application class creates these menus and their menu items along with the rest of the user interface. The Application class also keeps track of Document objects that a user has opened
Motivation • The application configures each Menu Item with an instance of a concrete Command subclass. • When the user selects a Menu Item, the Menu Item calls Execute on its command, and Execute carries out the operation. Menu Items don't know which subclass of Command they use. • Command subclasses store the receiver of the request and invoke one or more operations on the receiver
Motivation • For example, PasteCommand supports pasting text from the clipboard into a Document. PasteCommand's receiver is the Document object, it is supplied upon instantiation. • The Execute operation invokes Paste on the receiving Document
Motivation • OpenCommand's Execute operation is different: it prompts the user for a document name, creates a corresponding Document object, adds the document to the receiving application, and opens the document
Motivation • Sometimes a MenuItem needs to execute a sequence of commands. For example, a MenuItem for centering a page at normal size could be constructed from a CenterDocumentCommand object and a NormalSizeCommand object. Because it's common to string commands together in this way, we can define a MacroCommand class to allow a MenuItem to execute an open-ended number of commands. MacroCommand is a concrete Command subclass that simply executes a sequence of Commands. MacroCommand has no explicit receiver, because the commands it sequences define their own receiver
Motivation • In each of these examples, notice how the command pattern decouples the object that invokes the operation from the one having the knowledge to perform it. This gives us a lot of flexibility in designing our user interface. • An application can provide both a menu and a push button interface to a feature just by making the menu and the push button share an instance of the same concrete Command subclass • We can replace commands dynamically, which would be useful for implementing context-sensitive menus. We can also support command scripting by composing commands into larger ones. All of this is possible because the object that issues a request only needs to know how to issue it; it doesn't need to know how the request will be carried out
Applicability Use Command pattern when you want to • Parameterize objects by an action to perform, as MenuItem objects did above. You can express such parameterization in a procedural language with a callback function,that is, a function that's registered somewhere to be called at a later point. Commands are an object-oriented replacement for callbacks • Specify, queue, and execute requests at different times. A Command object can have a lifetime independent of the original request.
Applicability • Support undo. The Command's Execute operation can store state for reversing its effects in the command itself. The Command interface must have an added Unexecute operation that reverses the effects of a previous call to Execute. Executed commands are stored in a history list. Unlimited-level undo and redo is achieved by traversing this list backwards and forwards calling Unexecute and Execute, respectively. • Support logging changes so that they can be reapplied in case of a system crash. By augmenting the Command interface with load and store operations, you can keep a persistent log of changes. Recovering from a crash involves reloading logged commands from disk and re-executing them with the Execute operation
Participants • Command • declares an interface for executing an operation • Concrete Command (PasteCommand, OpenCommand) • defines a binding between a Receiver object and an action • implements Execute by invoking the corresponding operation(s) on Receiver • Client (Application) • creates a ConcreteCommand object and sets its receiver • Invoker (MenuItem) • asks the command to carry out the request • Receiver (Document, Application) • knows how to perform the operations associated with carrying out a request. Any class may serve as a Receiver
Collaborations • Command declares an interface for all commands, providing a simple execute() method which asks the Receiver of the command to carry out an operation. • The Receiver has the knowledge of what to do to carry out the request. • The Invoker holds a command and can get the Command to execute a request by calling the execute method. • The Client creates ConcreteCommands and sets a Receiver for the command. • The ConcreteCommanddefines a binding between the action and the receiver. When the Invoker calls execute the ConcreteCommand will run one or more actions on the Receiver.
Consequences • The Command pattern has the following consequences: • Command decouples the object that invokes the operation from the one that knows how to perform it. • Commands are first-class objects. They can be manipulated and extended like any other object. • It's easy to add new Commands, because you don't have to change existing classes.
Order orderUp takeOrder Make Burger & Shake Understanding Command Pattern through the Diner Example createOrder() Burger & Shake Order Slip Customer takeOrder() Waiter orderUp() Command Cook make_X()
How it works • Customer know what he wants and creates an order • Order consists of an order slip and the customer’s menu items that are written on it. • The waiter takes the order and when he gets around it he calls orderup() to begin the order preparation • Short order cook takes the order and prepares the meal according to the knowledge he has.
Example: Home Automation Remote Problem Statement • Build a remote that will control variety of home devices • Sample devices: lights, stereo, TV, ceiling light, thermostat, sprinkler, hot tub, garden light, ceiling fan, garage door
action() Receiver RemoteSlot execute() execute() execute() LightOnCommand GarageDoorOpen StereoOff Command Pattern Applied to Home Automation An encapsulated Request execute(){ receiver.action() } Invoker Command
Light/Receiver <<Interface>> Command execute() undo() LightOnCommand LightOffCommand execute() undo() execute() undo() Class Diagram RemoteControl/Invoker RemoteLoader/Client onCommands offCommands setCommand() onButtonPushed() offButtonPushed() on() off()
Command Interface • All command objects implement the same interface which consists of one method. In the diner example it was the orderUp() method. Here we can use execute public interface ICommand { //All we need is one method Execute() void Execute(); }
Concrete commands • Now its time to create the actual commands • We can create the commands for the Light object. The vendor classes give us the detail about how to turn the light on and how to turn it off. public class Light { private string _DescLight = "Light"; public string DescLight { get { return _DescLight; } set { _DescLight = value; } } public Light(string place) { this._DescLight = place; } public void On()//Turning the Light on { System.Console.WriteLine(this.DescLight + " is ON."); } public void Off()//Turning the Light off { System.Console.WriteLine(this.DescLight + " is OFF."); } }
Concrete Commands public class LightOnCommand : ICommand { Light lg; //The constructor is passed the specific light like Living room public LightOnCommand(Light my) { this.lg = my; } public void Execute() { this.lg.On(); } }
Concrete Commands public class LightOffCommand : ICommand { Light lg; public LightOffCommand(Light my) { this.lg = my; } public void Execute() { this.lg.Off(); } }
Null Command public class NoCommand : ICommand { public void Execute() { System.Console.WriteLine("No command"); } } • The NoCommand is a Null object. The Null object is useful when you don’t have a meaningful object to return and yet you want to remove the responsibility of handling null, from the client.
Remote Control public class RemoteControl //The Invoker { ICommand[] onCommands; //Array to store On commands ICommand[] offCommands;//Array to store Off commands public RemoteControl() {//Instantiating and initializing the Arrays in the constructor this.onCommands = new ICommand[7]; this.offCommands = new ICommand[7]; for (int i = 0; i < 7; i++) { this.onCommands[i] = new NoCommand(); this.offCommands[i] = new NoCommand(); } }
Setting up the Commands public void stCommand(int slot, ICommand on, ICommand off) {//This function sets the commands to take a slot position in the on and //off arrays this.onCommands[slot] = on; this.offCommands[slot] = off; } public void onButtonPushed(int slot) {//On Button on the remote for a particular slot this.onCommands[slot].Execute(); } public void offButtonPushed(int slot) {//Off Button on the remote for a particular slot this.offCommands[slot].Execute(); } }
Client Class or Remote Loader class Program { static void Main(string[] args) { RemoteControl remote = new RemoteControl();//Remote Control Object Light LivingRoomLight = new Light("Living Room");//Living Room Light Light KichenLight = new Light("Kichen");//Kichen Light //Command Objects LightOnCommandLivingRoomLightOn = new LightOnCommand(LivingRoomLight); LightOnCommandKichenLightOn = new LightOnCommand(KichenLight); LightOffCommandLivingRoomLightOff = new LightOffCommand(LivingRoomLight); LightOffCommandKichenLightOff = new LightOffCommand(KichenLight); //Setting Up Commands remote.stCommand(0, LivingRoomLightOn, LivingRoomLightOff); //Executing Commands remote.onButtonPushed(0); remote.offButtonPushed(0); Console.ReadLine(); } }
Assignment # 2 • Make a group of 3 people • Extend the above code and work out to design a full Remote Control for home automation with following aspects • Friendly GUI for easy access to all the remote control functions • All the Vendor classes mentioned in this lecture should be included in the scope. • The on and off functions could be shown graphically or with the help of messages • Create an Undo Command button on the remote to undo current command and go back to previous command’s results. • This assignment would be due on 01 Jan 2014. The teams would have to show the outputs to the whole class in form of a presentation containing program output