CSC/ECE 517 Fall 2011/ch4 4h kp

From Expertiza_Wiki
Revision as of 01:24, 21 October 2011 by Lpatil (talk | contribs) (→‎Examples)
Jump to navigation Jump to search

Introduction

This wiki article discusses about some of the commonly used & easy to understand Desgin patterns[1] in the software industry. Specifically, we will be studying about the Singleton, Adapter, Command & Strategy patterns.

Design Pattern

Definition

Some of the common definitions available online are as follows.

  • "A design pattern is a repeatable solution to a software engineering problem. Unlike most program-specific solutions, design patterns are used in many programs. Design patterns are not considered finished product; rather, they are templates that can be applied to multiple situations and can be improved over time, making a very robust software engineering tool. Because development speed is increased when using a proven prototype, developers using design pattern templates can improve coding efficiency and final product readability." -Techopedia <ref>Techopedia explains Design Pattern</ref>

In a layman's term, Design Patterns are

  • descriptions of what experienced designers know.
  • hints for choosing classes & methods
  • higher order abstractions for program organization
  • used to weigh design tradeoffs
  • used to avoid limitations of implementation language. <ref>Design Patterns in Dynamic Languages - Peter Norvig</ref>

Examples

There are various types of Design Patterns, viz creational patterns, structural patterns, and behavioral patterns, each of which consists numerous examples inturn. But that is not the scope of our discussion. So, let us limit ourselves to the following 4 pattern examples.

  1. Singleton pattern(type of Creational pattern).
  2. Adapter pattern(type Structural pattern).
  3. Command pattern(type of Behavioral pattern).
  4. Strategy pattern(type of Behavioral pattern).

Singleton

Adapter

Command

Command Design Pattern: Encapsulating Invocations

Definition

"By encapsulating method invocation, we can crystallize pieces of computation, so that the object invoking the computation doesn't need to worry about how things are done. It just uses the crystallized method to get things done" - Head First Design Patterns

Overview

The command pattern allows one to decouple the requester of an action from the object that actually performs the action.

This decoupling is achieved by introducing a command object which encapsulates the request to do something on an object i.e.the action to be taken or the methods to be invoked and exposes these via a single API. So the requester will only need to invoke this well exposed and simple API of the command object and the implementation of this API will contain calls to the methods of the object on which actual actions are to be done. [2] It achieves two things - One, the requester object code can remain simple. It does not need to take into consideration the details about how the action is going to be performed, it will only need to worry about the API exposed by the Command Object corresponding to the object whose actions are to be invoked. And two, the object on which the actions are invoked need not worry about who invoked them. The only "route" via which they would have been invoked is via the Command object specific to this object. [3]

Example Usage of Command Pattern

  • A calculator with unlimited number of undo’s and redo’s
  • "Thread pools” - A typical, general-purpose thread pool class might have a public addTask() method that adds a work item to an internal queue of tasks waiting to be done. It maintains a pool of threads that execute commands from the queue. [4]
  • Say we have designed an image editor and user can have the option of opening file from various ways like menu, tool bar, double click on a file in the explorer. The solution is the command pattern where the FileOpen command is associated in the viewer itself and when the command executes the file is shown in the viewer.

[5]

Conceptual work-out of the Command Pattern

  • Let us assume that there is an object "Client" which needs to invoke some action of Object "Receiver".
  • Let us also assume that this "action" is a cumulative computation and it might involve invoking multiple methods of Receiver object.

In absence of Command Pattern, "Client" will invoke "Receiver's" methods via code statements like receiver.method1(), receeiver.method2() etc. This would mean that Client has to know exactly which methods and in which sequence are required to be called for the specified action to take place. Thus, it implies a higher level of coupling and hence a dependency between Client and Receiver objects. However, using a Command object specific to the Receiver object will help in decoupling this dependency.

  • The Command object will only provide a single method, say "execute()" and this method will encapsulate all the method invocations of Receiver object. In this way, the methods of Receiver object for executing an action and the Receiver object itself are bound together in the Command object. [6]
  • The Client object will then create the Command Object and pass it to an intermediate "Invoker" object where it will get stored.

At some point in the future, the Invoker object will invoke the "execute()" method of the Command Object passed to it, which in turn will culminate in calling all the methods of Receiver Object that are required to be called for the original specified action to take place.

Pictorially, this can be visualized as follows -

[7]

Coding example: Command Pattern in Java

This example is inspired from the example given in Head First Design Patterns

Suppose there is a "RemoteControl" that operates lights and other such devices in a house. The remote might have on and off buttons for every light and pressing those buttons might only invoke "on" and "off" actions on those lights. However what if the same Remote is to be used for controlling a GarageDoor? GarageDoor will not have on and off methods but "up" and "down" methods. One way around this would be including "if" statements in RemoteControl code that checks if the device being controlled is a GarageDoor, then invoke "up" instead of "on". However, this is not a robust method. If you decide to support a new device in the future, more "else if" statements will get added. The RemoteControl logic will become complex and it will be closely coupled with all the devices. This does not exhibit good Object Oriented design. However, using Command Pattern will ease out the problem. We can have one Command Object per device and specifically for each action on each device.

This command object's execute() method will encapsulate calls to "on" or "off". This achieves two things. One, the RemoteControl code can now be simple. It will only invoke "execute()" method of whichever device is being controlled. Also adding support of new devices in the future becomes easy.

Java provides an Interface called "Command". It supports only one method, "execute()" The Command Interface can be follows

public interface Command {
   public void execute();
};

Now, there will be a GarageDoorOpen command object that should implement the Command Interface.

public class GarageDoorOpenCommand implements Command {
          GarageDoor garagedoor;

public GarageDoorOpenCommand(GarageDoor garagedoor) { this.garagedoor = garagedoor; }

/* execute method encapsultes the call to "up" */ public void execute() { garagedoor.up(); }

}

Now remains the client and Invoker classes. Our Invoker class should only call execute method on whatever Command Object is passed to it. Also, the Client object should create a Command object, pass it to Invoker object.

public class RemoteControlInvoker {
          Command cmd;

public void setCommand(Command cmd) { this.cmd = cmd; }

public void buttonPressed() { cmd.execute(); }

/* The Invoker object will only call execute on the command object to which its local

               Command object was set to. 
           */
}
public class RemoteControl {
     public static void main(String[] args) {

RemoteControlInvoker rmtInvoker = new RemoteControlInvoker();

GarageDoor garageDoor = new GarageDoor(); GarageDoorOpenCommand gdOpenCmd = new GarageDoorOpenCommand(garageDoor);

rmtInvoker.setCommand(gdOpenCmd); //Pass the created Command Object to Invoker rmtInvoker.buttonPressed(); //Let the Invoker invoke action on the Receiver GarageDoor }

}

In this way, the Command object encapsulates a request by binding together a set of actions on a specific receiver. It does this by packaging the actions and the receiver and exposing just one method - execute(). From the outside, no other objects really know what actions get performed on a receiver, they just know that by calling "execute()", their request will be processed.

Coding example: Command Pattern in Ruby

In Ruby, because of the presence of "Closures", implementing a Command Pattern is a fairly straightforward. [8]

Formally, a closure is basically a function or a method (or even a block, in the context of Ruby) that has the following twp properties:

  • It is a first-class function, meaning it can be passed around like an object or as a value parameter to other functions/methods.blocks
  • It saves the lexical environment, meaning it captures the variables within scope at its creation and maintains them even if they later go out of scope.

[9]

The “Proc” class in Ruby supports a method “call” by which the block of code saved in the Proc class can be “called” and executed. In this scenario, the Proc class (which might be an output of the ‘lambda’ function) can be considered as an equivalent of the “invoker” class and the block of code passed to it to be the “Command” object.

However, using Procs for implementing Command Pattern is not applicable always. Whenever it is required to pass commands between address spaces or save commands to a file and later load them into a Ruby program. Proc objects can not be stored or restored using marshal module. Therefore, for following two reasons, using Proc objects is not enough for Command Pattern -

  • When command objects need to be Persistent
  • When command objects need to be sent to a remote objects.

[10]

In such cases, one would have to explicitly implement Command objects via “Command” class.

Below is an example of Command Pattern in Ruby using an explicit class and not using the Proc class. In this example, “CheckMemory” extends Command class and the Installer class supports “install”, “uninstall” methods. Installer class need not worry about the instance of class passed to the “add_command” method. [11]

class Command
   def initialize(description, proc1, proc2)
      @description = description
      @function =  proc1
      @inverse = proc2
   end
   def execute
      @function.call unless @function.nil?
   end
   def unexecute
      @inverse.call unless @inverse.nil?
   end
end
class CheckMemory < Command ##ireversible
   def initialize
      proc1 = Proc.new do
        puts "Checking Memory..."
      end
      proc2 = Proc.new do
        puts "Mem Check need not be reversed"
      end
      super("Checking memory avalability", proc1, proc2)
   end
end
class Installer
   def initialize
      @commands = []
   end
   def add_command command
      @commands << command
   end
   def install
      @commands.each {|c| c.execute}
   end
   def uninstall
      @commands.reverse.each do |command|
         command.unexecute
      end
   end
end
installer = Installer.new
installer.add_command(CheckMemory.new)
installer.add_command(CreateFile.new)
installer.installinstaller.uninstall

Advanced Command Pattern Features

  • Supporting Undo operation:

Providing "undo" functionality to a software is an important feature addition. Many softwares like editors need to keep track of previous actions performed and then let user undo some or all of them on demand. The Command Pattern lends itself in a very nice manner for implementing such functionality. [12]

When Commands support undo, they have an undo() method that mirros the execute() method. Whatever execute() last did, undo() reverses it. In the above code example, the Command Interface would need to have a method undo and the GarageDoorOpenCommand class will need to implement it. It can just have a call to "down" method on the GarageDoor. In more general sense, we would need to have a Command object that always points to the last Command that was executed. Whenever an undo action needs to be done, the undo() method of object to which this command object is pointing to would be called. [13]

  • The "NoCommand" Object: The NoCommand object is an example of null object. A null object is useful when there is no meaningful object

to return and the responsibility of handling null objects is not adviced. [14]

  • The "Meta" Command Pattern: Meta Command Pattern allows us to create macros of Command objects so that multiple actions can be executed

via a single call. This means that the execute() method of a MetaCommand Object will involve a series of calls of "execute()" methods of other Command Objects. [15]

Strategy

Conclusion

References

<references/>

External Links