CSC/ECE 517 Fall 2010/ch4 4f ls: Difference between revisions
No edit summary |
No edit summary |
||
(7 intermediate revisions by the same user not shown) | |||
Line 5: | Line 5: | ||
This information includes the method name, the object that owns the method and values for the method parameters. Client, invoker and receiver are always associated with the command pattern. The client instantiates the command object and provides the information required to call the method at a later time. The invoker decides when the method should be called. The receiver is an instance of the class that contains the method's code. | This information includes the method name, the object that owns the method and values for the method parameters. Client, invoker and receiver are always associated with the command pattern. The client instantiates the command object and provides the information required to call the method at a later time. The invoker decides when the method should be called. The receiver is an instance of the class that contains the method's code. | ||
Using command objects makes it easier to construct general components that need to delegate, sequence or execute method calls at a time of their choosing without the need to know the owner of the method or the method parameters. | Using command objects makes it easier to construct general components that need to delegate, sequence or execute method calls at a time of their choosing without the need to know the owner of the method or the method parameters. | ||
The Command pattern solves this problem: A program needs to issue requests to objects. The code that is doing the requesting doesn’t know what the receiver will be, or what operation will be requested. | |||
== What are static and dynamic languages? == | == What are static and dynamic languages? == | ||
Dynamic programming language is used to describe a class of high-level programming languages that execute at runtime many common behaviors that other languages, which are usually called static language for convenience, might perform during compilation. These behaviors could include extension of the program, by adding new code, by extending objects and definitions, or by modifying the type system, all during program execution. These behaviors can be emulated in nearly any language of sufficient complexity, but dynamic languages provide direct tools to make use of them. Most dynamic languages are dynamically typed, but not all. | Dynamic programming language is used to describe a class of high-level programming languages that execute at runtime many common behaviors that other languages, which are usually called static language for convenience, might perform during compilation | ||
[http://en.wikipedia.or/wiki/Dynamic_programming_language]. These behaviors could include extension of the program, by adding new code, by extending objects and definitions, or by modifying the type system, all during program execution. These behaviors can be emulated in nearly any language of sufficient complexity, but dynamic languages provide direct tools to make use of them. Most dynamic languages are dynamically typed, but not all. | |||
Actually, the notion of dynamic language is ambiguous sometime because it attempts to make distinctions between code and data as well as between compilation and runtime which are not universal. Virtual machines, just-in-time compilation, and the ability of many programming languages on some systems to directly modify machine code make the distinction abstract. In general, the assertion that a language is dynamic is more an assertion about the ease of use of dynamic features than it is a clear statement of the capabilities of the language. Particularly, the following are generally considered dynamic languages: | Actually, the notion of dynamic language is ambiguous sometime because it attempts to make distinctions between code and data as well as between compilation and runtime which are not universal. Virtual machines, just-in-time compilation, and the ability of many programming languages on some systems to directly modify machine code make the distinction abstract. In general, the assertion that a language is dynamic is more an assertion about the ease of use of dynamic features than it is a clear statement of the capabilities of the language. Particularly, the following are generally considered dynamic languages[http://en.wikipedia.org/wiki/Dynamic_programming_language]: | ||
* Ruby | * Ruby | ||
* Javascript | * Javascript | ||
Line 18: | Line 21: | ||
== Structure == | == Structure == | ||
===Terminology === | ===Terminology === | ||
We first introduce some terminology terms used to describe command pattern implementations. | We first introduce some terminology terms used to describe command pattern implementations[http://www.patterndepot.com/put/8/command.pdf]. | ||
* Client: the button, toolbar button, or menu item clicked, the shortcut key pressed by the user. | * Client: the button, toolbar button, or menu item clicked, the shortcut key pressed by the user. | ||
* Command: declares an interface for executing an operation; | * Command: declares an interface for executing an operation; | ||
Line 53: | Line 56: | ||
We consider in this article some typical static languages C#, java and C++. | We consider in this article some typical static languages C#, java and C++. | ||
===C#=== | ===C#=== | ||
This structural code demonstrates the Command pattern which stores requests as objects allowing clients to execute or playback the requests. | In C++ and C#, they use the "pointer to methods" to implement command pattern. This structural code demonstrates the Command pattern which stores requests as objects allowing clients to execute or playback the requests. | ||
// Command pattern -- Structural example | // Command pattern -- Structural example | ||
using System; | using System; | ||
namespace DoFactory.GangOfFour.Command.Structural | namespace DoFactory.GangOfFour.Command.Structural | ||
{ | |||
{ | |||
/// <summary> | /// <summary> | ||
/// MainApp startup class for Structural | /// MainApp startup class for Structural | ||
/// Command Design Pattern. | /// Command Design Pattern. | ||
/// </summary> | /// </summary> | ||
class MainApp | class MainApp | ||
{ | { | ||
/// <summary> | /// <summary> | ||
/// Entry point into console application. | /// Entry point into console application. | ||
/// </summary> | /// </summary> | ||
static void Main() | static void Main() | ||
{ | { | ||
// Create receiver, command, and invoker | // Create receiver, command, and invoker | ||
Receiver receiver = new Receiver(); | Receiver receiver = new Receiver(); | ||
Command command = new ConcreteCommand(receiver); | Command command = new ConcreteCommand(receiver); | ||
Invoker invoker = new Invoker() | |||
Invoker invoker = new Invoker() | |||
// Set and execute command | // Set and execute command | ||
invoker.SetCommand(command); | invoker.SetCommand(command); | ||
invoker.ExecuteCommand(); | invoker.ExecuteCommand(); | ||
// Wait for user | // Wait for user | ||
Console.ReadKey(); | Console.ReadKey(); | ||
} | } | ||
} | |||
} | |||
/// <summary> | /// <summary> | ||
/// The 'Command' abstract class | /// The 'Command' abstract class | ||
/// </summary> | /// </summary> | ||
abstract class Command | abstract class Command | ||
{ | { | ||
protected Receiver receiver; | protected Receiver receiver; | ||
// Constructor | // Constructor | ||
public Command(Receiver receiver) | public Command(Receiver receiver) | ||
{ | { | ||
this.receiver = receiver; | this.receiver = receiver; | ||
} | } | ||
public abstract void Execute(); | public abstract void Execute(); | ||
} | } | ||
/// <summary> | /// <summary> | ||
/// The 'ConcreteCommand' class | /// The 'ConcreteCommand' class | ||
/// </summary> | /// </summary> | ||
class ConcreteCommand : Command | class ConcreteCommand : Command | ||
{ | { | ||
// Constructor | // Constructor | ||
public ConcreteCommand(Receiver receiver) : | public ConcreteCommand(Receiver receiver) : | ||
base(receiver) | base(receiver) | ||
{ | { | ||
} | } | ||
public override void Execute() | public override void Execute() | ||
{ | { | ||
receiver.Action(); | |||
} | } | ||
} | } | ||
/// <summary> | /// <summary> | ||
/// The 'Receiver' class | /// The 'Receiver' class | ||
/// </summary> | /// </summary> | ||
class Receiver | class Receiver | ||
{ | { | ||
public void Action() | public void Action() | ||
{ | { | ||
Console.WriteLine("Called Receiver.Action()"); | Console.WriteLine("Called Receiver.Action()"); | ||
} | } | ||
} | |||
} | /// <summary> | ||
/// The 'Invoker' class | /// The 'Invoker' class | ||
/// </summary> | /// </summary> | ||
class Invoker | class Invoker | ||
{ | { | ||
private Command _command | |||
private Command _command | |||
public void SetCommand(Command command) | public void SetCommand(Command command) | ||
{ | { | ||
this._command = command; | this._command = command; | ||
} | } | ||
public void ExecuteCommand() | |||
{ | { | ||
_command.Execute(); | _command.Execute(); | ||
} | } | ||
} | } | ||
} | } | ||
Line 242: | Line 144: | ||
import java.lang.reflect.*; | import java.lang.reflect.*; | ||
public class CommandReflect { | public class CommandReflect { | ||
private int state; | private int state; | ||
Line 295: | Line 196: | ||
=== C++ === | === C++ === | ||
Similar to C#, C++ uses "pointers to methods" to implement command patter. | |||
#include <iostream> #include <string> using namespace std; | #include <iostream> #include <string> using namespace std; | ||
class Person; | class Person; | ||
class Command | class Command | ||
{ | { | ||
Line 316: | Line 216: | ||
(object-> *method)(); // invoke the method on the object | (object-> *method)(); // invoke the method on the object | ||
} | } | ||
}; | }; | ||
class Person | |||
class Person | { | ||
{ | |||
string name; | string name; | ||
Line 377: | Line 276: | ||
<?php | <?php | ||
class BookCommandee { | class BookCommandee { | ||
private $author; | private $author; | ||
private $title; | private $title; | ||
Line 407: | Line 306: | ||
return $this->getTitle().' by '.$this->getAuthor(); | return $this->getTitle().' by '.$this->getAuthor(); | ||
} | } | ||
} | } | ||
abstract class BookCommand { | abstract class BookCommand { | ||
protected $bookCommandee; | protected $bookCommandee; | ||
function __construct($bookCommandee_in) { | function __construct($bookCommandee_in) { | ||
Line 415: | Line 314: | ||
} | } | ||
abstract function execute(); | abstract function execute(); | ||
} | } | ||
class BookStarsOnCommand extends BookCommand { | class BookStarsOnCommand extends BookCommand { | ||
function execute() { | function execute() { | ||
$this->bookCommandee->setStarsOn(); | $this->bookCommandee->setStarsOn(); | ||
} | } | ||
} | } | ||
class BookStarsOffCommand extends BookCommand { | class BookStarsOffCommand extends BookCommand { | ||
function execute() { | function execute() { | ||
$this->bookCommandee->setStarsOff(); | $this->bookCommandee->setStarsOff(); | ||
} | } | ||
} | } | ||
writeln('BEGIN TESTING COMMAND PATTERN'); | writeln('BEGIN TESTING COMMAND PATTERN'); | ||
writeln(''); | writeln(''); | ||
$book = new BookCommandee('Design Patterns', 'Gamma, Helm, Johnson, and Vlissides'); | $book = new BookCommandee('Design Patterns', 'Gamma, Helm, Johnson, and Vlissides'); | ||
writeln('book after creation: '); | writeln('book after creation: '); | ||
writeln($book->getAuthorAndTitle()); | writeln($book->getAuthorAndTitle()); | ||
writeln(''); | writeln(''); | ||
$starsOn = new BookStarsOnCommand($book); | $starsOn = new BookStarsOnCommand($book); | ||
callCommand($starsOn); | callCommand($starsOn); | ||
writeln('book after stars on: '); | writeln('book after stars on: '); | ||
writeln($book->getAuthorAndTitle()); | writeln($book->getAuthorAndTitle()); | ||
writeln(''); | writeln(''); | ||
$starsOff = new BookStarsOffCommand($book); | $starsOff = new BookStarsOffCommand($book); | ||
callCommand($starsOff); | callCommand($starsOff); | ||
Line 448: | Line 344: | ||
writeln($book->getAuthorAndTitle()); | writeln($book->getAuthorAndTitle()); | ||
writeln(''); | writeln(''); | ||
writeln('END TESTING COMMAND PATTERN'); | |||
writeln('END TESTING COMMAND PATTERN'); | |||
// the callCommand function demonstrates that a specified | // the callCommand function demonstrates that a specified | ||
// function in BookCommandee can be executed with only | // function in BookCommandee can be executed with only | ||
Line 457: | Line 351: | ||
$bookCommand_in->execute(); | $bookCommand_in->execute(); | ||
} | } | ||
function writeln($line_in) { | function writeln($line_in) { | ||
echo $line_in."<br/>"; | echo $line_in."<br/>"; | ||
} | } | ||
?> | ?> | ||
Line 472: | Line 364: | ||
classVariableNames: '' | classVariableNames: '' | ||
poolDictionaries: '' | poolDictionaries: '' | ||
Object subclass: #Light | Object subclass: #Light | ||
instanceVariableNames: '' | instanceVariableNames: '' | ||
classVariableNames: '' | classVariableNames: '' | ||
poolDictionaries: '' | poolDictionaries: '' | ||
Object subclass: #PressSwitch | Object subclass: #PressSwitch | ||
instanceVariableNames: '' | instanceVariableNames: '' | ||
classVariableNames: '' | classVariableNames: '' | ||
poolDictionaries: '' | poolDictionaries: '' | ||
!Switch class methods ! | !Switch class methods ! | ||
upMessage: flipUpMessage downMessge: flipDownMessage | upMessage: flipUpMessage downMessge: flipDownMessage | ||
^self new upMessage: flipUpMessage downMessge: flipDownMessage; yourself.! ! | ^self new upMessage: flipUpMessage downMessge: flipDownMessage; yourself.! ! | ||
!Switch methods ! | !Switch methods ! | ||
upMessage: flipUpMessage downMessge: flipDownMessage | upMessage: flipUpMessage downMessge: flipDownMessage | ||
flipUpCommand := flipUpMessage. | flipUpCommand := flipUpMessage. | ||
flipDownCommand := flipDownMessage.! | flipDownCommand := flipDownMessage.! | ||
flipDown | flipDown | ||
flipDownCommand perform.! | flipDownCommand perform.! | ||
flipUp | flipUp | ||
flipUpCommand perform.! ! | flipUpCommand perform.! ! | ||
!Light methods ! | !Light methods ! | ||
turnOff | turnOff | ||
Transcript show: 'The light is off'; cr.! | Transcript show: 'The light is off'; cr.! | ||
turnOn | turnOn | ||
Transcript show: 'The light is on'; cr.! ! | Transcript show: 'The light is on'; cr.! ! | ||
!PressSwitch class methods ! | !PressSwitch class methods ! | ||
switch: state | switch: state | ||
" This is the test method | " This is the test method | ||
| lamp switchUp switchDown switch | | | lamp switchUp switchDown switch | | ||
lamp := Light new. | lamp := Light new. | ||
switchUp := Message receiver: lamp selector: #turnOn. | switchUp := Message receiver: lamp selector: #turnOn. | ||
switchDown := Message receiver: lamp selector: #turnOff. | switchDown := Message receiver: lamp selector: #turnOff. | ||
switch := Switch upMessage: switchUp downMessge: switchDown. | |||
switch := Switch upMessage: switchUp downMessge: switchDown. | |||
state = #on ifTrue: [ ^switch flipUp ]. | state = #on ifTrue: [ ^switch flipUp ]. | ||
state = #off ifTrue: [ ^switch flipDown ] | state = #off ifTrue: [ ^switch flipDown ] | ||
Transcript show: 'Argument #on or #off is required.'. | Transcript show: 'Argument #on or #off is required.'. | ||
=Comparison: static vs dynamic= | |||
Based on the above-mentioned examples, we present a short summary on comparison of command pattern in static language (such as Java) and dynamic language (such as ruby). | |||
*Dynamic languages, such as ruby, can change their behavior conveniently at run time, such as reflection and metaprogramming. Therefore, the implementation of command pattern in dynamic languages is much easier and more convenient than that in static language. | |||
*Command pattern is to issue requests to objects without knowing anything about the operation being requested or the receiver of the request.” The Command design pattern suggests encapsulating (“wrapping”) in an object all (or some) of the following: an object, a method name, and some arguments. Although most actions of static language have to be performed at compile time, static languages have their own solutions to implement command pattern. For example, C and C# use "pointers to methods". Java does not support “pointers to methods”, but its reflection capability will do nicely. The “command” is a black box to the “client”. All the client does is call “execute()” on the opaque object. | |||
= Summary = | = Summary = |
Latest revision as of 17:02, 31 October 2010
Topic: The Command pattern in static and dynamic languages
Fundamentals
What is Command Pattern ?
In object-oriented programming, the command pattern is a design pattern in which an object is used to represent and encapsulate all the information needed to call a method at a later time[1]. This information includes the method name, the object that owns the method and values for the method parameters. Client, invoker and receiver are always associated with the command pattern. The client instantiates the command object and provides the information required to call the method at a later time. The invoker decides when the method should be called. The receiver is an instance of the class that contains the method's code. Using command objects makes it easier to construct general components that need to delegate, sequence or execute method calls at a time of their choosing without the need to know the owner of the method or the method parameters.
The Command pattern solves this problem: A program needs to issue requests to objects. The code that is doing the requesting doesn’t know what the receiver will be, or what operation will be requested.
What are static and dynamic languages?
Dynamic programming language is used to describe a class of high-level programming languages that execute at runtime many common behaviors that other languages, which are usually called static language for convenience, might perform during compilation [2]. These behaviors could include extension of the program, by adding new code, by extending objects and definitions, or by modifying the type system, all during program execution. These behaviors can be emulated in nearly any language of sufficient complexity, but dynamic languages provide direct tools to make use of them. Most dynamic languages are dynamically typed, but not all.
Actually, the notion of dynamic language is ambiguous sometime because it attempts to make distinctions between code and data as well as between compilation and runtime which are not universal. Virtual machines, just-in-time compilation, and the ability of many programming languages on some systems to directly modify machine code make the distinction abstract. In general, the assertion that a language is dynamic is more an assertion about the ease of use of dynamic features than it is a clear statement of the capabilities of the language. Particularly, the following are generally considered dynamic languages[3]:
- Ruby
- Javascript
- Perl
- PHP
- Smalltalk
Uses of Command Pattern
Command pattern encapsulates a request as an object, thereby letting you parametrize clients with different requests, queue or log requests, and support undoable operations. It is useful for implementing.
Structure
Terminology
We first introduce some terminology terms used to describe command pattern implementations[4].
- Client: the button, toolbar button, or menu item clicked, the shortcut key pressed by the user.
- Command: declares an interface for executing an operation;
- Execute: It may refer to running the code identified by the command object's execute method.
- Receiver: The actual work to be done by the command.
- ConcreteCommand: extends the Command interface, implementing the Execute method by invoking the corresponding operations on Receiver. It defines a link between the Receiver and the action.
- Invoker: It decides when the method should be called. It takes in the request and calls the receiver by passing the command to it and asks it to carry out the request.
- Concretecommand:
Illustration
We will use the following graph to illustrate the structure of Command Pattern.
Why Command Pattern?
“An object that contains a symbol, name or key that represents a list of commands, actions or keystrokes”. This is the definition of a macro, one that should be familiar to any computer user. From this idea the Command design pattern was given birth. The Macro represents, at some extent, a command that is built from the reunion of a set of other commands, in a given order. Just as a macro, the Command design pattern encapsulates commands (method calls) in objects allowing us to issue requests without knowing the requested operation or the requesting object. Command design pattern provides the options to queue commands, undo/redo actions and other manipulations.
Advantages and Disadvantages of Using Command Pattern
Now that we have understood how the pattern works, it's time to take a look at its advantages and disadvantages.
The intelligence of a command
- The command is just a link between the receiver and the actions that carry out the request.
- The command implements everything itself, without sending anything to the receiver.
We must always keep in mind the fact that the receiver is the one who knows how to perform the operations needed, the purpose of the command being to help the client to delegate its request quickly and to make sure the command ends up where it should.
Advantages of Command Pattern
The main advantage of the command design pattern is that it decouples the object that invokes the operation from the one that know how to perform it.
- It provides encapsulation of application logic so that it can be executed at a different point of time.
- It allows to execute the application in separate contexts, such as in a different thread or using a different state by separating the application logic and context.
- The separation between application logic and context allows to easier exchange the application logic.
Disadvantages of Command Pattern
The main benefits of the Command Pattern are discussed above. The major disadvantage of the pattern is that it results in lots of little Command classes that can clutter up a design. However, the routing information that Command objects encapsulate has to go somewhere. If this information is not contained in Command objects, then it will have to go into the Controller. The resulting bloat may necessitate partitioning the Controller into a subsystem, and it will certainly make the Controller harder to understand and maintain.
Command pattern in static and dynamic languages
In this section, we demonstrate the implementation of command pattern in both dynamic and static languages by some examples.
Command pattern in static languages
We consider in this article some typical static languages C#, java and C++.
C#
In C++ and C#, they use the "pointer to methods" to implement command pattern. This structural code demonstrates the Command pattern which stores requests as objects allowing clients to execute or playback the requests.
// Command pattern -- Structural example using System; namespace DoFactory.GangOfFour.Command.Structural { /// <summary> /// MainApp startup class for Structural /// Command Design Pattern. /// </summary> class MainApp { /// <summary> /// Entry point into console application. /// </summary> static void Main() { // Create receiver, command, and invoker Receiver receiver = new Receiver(); Command command = new ConcreteCommand(receiver); Invoker invoker = new Invoker() // Set and execute command invoker.SetCommand(command); invoker.ExecuteCommand(); // Wait for user Console.ReadKey(); } } /// <summary> /// The 'Command' abstract class /// </summary> abstract class Command { protected Receiver receiver; // Constructor public Command(Receiver receiver) { this.receiver = receiver; } public abstract void Execute(); } /// <summary> /// The 'ConcreteCommand' class /// </summary> class ConcreteCommand : Command { // Constructor public ConcreteCommand(Receiver receiver) : base(receiver) { } public override void Execute() { receiver.Action(); } } /// <summary> /// The 'Receiver' class /// </summary> class Receiver { public void Action() { Console.WriteLine("Called Receiver.Action()"); } } /// <summary> /// The 'Invoker' class /// </summary> class Invoker { private Command _command public void SetCommand(Command command) { this._command = command; } public void ExecuteCommand() { _command.Execute(); } } }
Java
Sometimes it is necessary to issue requests to objects without knowing anything about the operation being requested or the receiver of the request.” The Command design pattern suggests encapsulating (“wrapping”) in an object all (or some) of the following: an object, a method name, and some arguments. Java does not support “pointers to methods”, but its reflection capability will do nicely. The “command” is a black box to the “client”. All the client does is call “execute()” on the opaque object.
import java.lang.reflect.*; public class CommandReflect { private int state; public CommandReflect( int in ) { state = in; } public int addOne( Integer one ) { return state + one.intValue(); } public int addTwo( Integer one, Integer two ) { return state + one.intValue() + two.intValue(); }
static public class Command { private Object receiver; // the "encapsulated" object private Method action; // the "pre-registered" request private Object[] args; // the "pre-registered" arg list public Command( Object obj, String methodName, Object[] arguments ) { receiver = obj; args = arguments; Class cls = obj.getClass(); // get the object's "Class" Class[] argTypes = new Class[args.length]; for (int i=0; i < args.length; i++) // get the "Class" for each argTypes[i] = args[i].getClass(); // supplied argument // get the "Method" data structure with the correct name and signature try { action = cls.getMethod( methodName, argTypes ); } catch( NoSuchMethodException e ) { System.out.println( e ); } } public Object execute() { // in C++, you do something like --- return receiver->action( args ); try { return action.invoke( receiver, args ); } catch( IllegalAccessException e ) { System.out.println( e ); } catch( InvocationTargetException e ) { System.out.println( e ); } return null; } }
public static void main( String[] args ) { CommandReflect[] objs = { new CommandReflect(1), new CommandReflect(2) }; System.out.print( "Normal call results: " ); System.out.print( objs[0].addOne( new Integer(3) ) + " " ); System.out.print( objs[1].addTwo( new Integer(4), new Integer(5) ) + " " ); Command[] cmds = { new Command( objs[0], "addOne", new Integer[] { new Integer(3) } ), new Command( objs[1], "addTwo", new Integer[] { new Integer(4), new Integer(5) } ) }; System.out.print( "\nReflection results: " ); for (int i=0; i < cmds.length; i++) System.out.print( cmds[i].execute() + " " ); System.out.println(); } }
C++
Similar to C#, C++ uses "pointers to methods" to implement command patter.
#include <iostream> #include <string> using namespace std; class Person; class Command { // 1. Create a class that encapsulates an object and a member function // a pointer to a member function (the attribute's name is "method") Person *object; // void(Person:: *method)(); public: Command(Person *obj = 0, void(Person:: *meth)() = 0) { object = obj; // the argument's name is "meth" method = meth; } void execute() { (object-> *method)(); // invoke the method on the object } }; class Person { string name;
// cmd is a "black box", it is a method invocation // promoted to "full object status" Command cmd; public: Person(string n, Command c): cmd(c) { name = n; } void talk() { // "this" is the sender, cmd has the receiver cout << name << " is talking" << endl; cmd.execute(); // ask the "black box" to callback the receiver } void passOn() { cout << name << " is passing on" << endl; // 4. When the sender is ready to callback to the receiver, // it calls execute() cmd.execute(); } void gossip() { cout << name << " is gossiping" << endl; cmd.execute(); } void listen() { cout << name << " is listening" << endl; } };
int main() { // Fred will "execute" Barney which will result in a call to passOn() // Barney will "execute" Betty which will result in a call to gossip() // Betty will "execute" Wilma which will result in a call to listen() Person wilma("Wilma", Command()); // 2. Instantiate an object for each "callback" // 3. Pass each object to its future "sender" Person betty("Betty", Command(&wilma, &Person::listen)); Person barney("Barney", Command(&betty, &Person::gossip)); Person fred("Fred", Command(&barney, &Person::passOn)); fred.talk(); }
Command pattern in dynamic languages
In terms of dynamic languages, we consider PHP and Smalltalk.
PHP
In this example, a BookStarsOnCommand object is instantiated with an instance of the BookComandee class. The BookStarsOnCommand object will call that BookComandee object’s bookStarsOn() function when it’s execute() function is called.
<?php
class BookCommandee { private $author; private $title; function __construct($title_in, $author_in) { $this->setAuthor($author_in); $this->setTitle($title_in); } function getAuthor() { return $this->author; } function setAuthor($author_in) { $this->author = $author_in; } function getTitle() { return $this->title; } function setTitle($title_in) { $this->title = $title_in; } function setStarsOn() { $this->setAuthor(Str_replace(' ','*',$this->getAuthor())); $this->setTitle(Str_replace(' ','*',$this->getTitle())); } function setStarsOff() { $this->setAuthor(Str_replace('*',' ',$this->getAuthor())); $this->setTitle(Str_replace('*',' ',$this->getTitle())); } function getAuthorAndTitle() { return $this->getTitle().' by '.$this->getAuthor(); } }
abstract class BookCommand { protected $bookCommandee; function __construct($bookCommandee_in) { $this->bookCommandee = $bookCommandee_in; } abstract function execute(); }
class BookStarsOnCommand extends BookCommand { function execute() { $this->bookCommandee->setStarsOn(); } }
class BookStarsOffCommand extends BookCommand { function execute() { $this->bookCommandee->setStarsOff(); } }
writeln('BEGIN TESTING COMMAND PATTERN'); writeln(); $book = new BookCommandee('Design Patterns', 'Gamma, Helm, Johnson, and Vlissides'); writeln('book after creation: '); writeln($book->getAuthorAndTitle()); writeln(); $starsOn = new BookStarsOnCommand($book); callCommand($starsOn); writeln('book after stars on: '); writeln($book->getAuthorAndTitle()); writeln(); $starsOff = new BookStarsOffCommand($book); callCommand($starsOff); writeln('book after stars off: '); writeln($book->getAuthorAndTitle()); writeln(); writeln('END TESTING COMMAND PATTERN'); // the callCommand function demonstrates that a specified // function in BookCommandee can be executed with only // an instance of BookCommand. function callCommand(BookCommand $bookCommand_in) { $bookCommand_in->execute(); } function writeln($line_in) { echo $line_in."
"; } ?>
Smalltalk
In this example we configure the Switch with 2 commands: to turn the light on and to turn the light off.
Object subclass: #Switch instanceVariableNames: ' flipUpCommand flipDownCommand ' classVariableNames: poolDictionaries: Object subclass: #Light instanceVariableNames: classVariableNames: poolDictionaries: Object subclass: #PressSwitch instanceVariableNames: classVariableNames: poolDictionaries: !Switch class methods ! upMessage: flipUpMessage downMessge: flipDownMessage
^self new upMessage: flipUpMessage downMessge: flipDownMessage; yourself.! !
!Switch methods ! upMessage: flipUpMessage downMessge: flipDownMessage
flipUpCommand := flipUpMessage. flipDownCommand := flipDownMessage.!
flipDown
flipDownCommand perform.!
flipUp
flipUpCommand perform.! !
!Light methods ! turnOff
Transcript show: 'The light is off'; cr.!
turnOn
Transcript show: 'The light is on'; cr.! !
!PressSwitch class methods ! switch: state
" This is the test method | lamp switchUp switchDown switch | lamp := Light new. switchUp := Message receiver: lamp selector: #turnOn. switchDown := Message receiver: lamp selector: #turnOff. switch := Switch upMessage: switchUp downMessge: switchDown. state = #on ifTrue: [ ^switch flipUp ]. state = #off ifTrue: [ ^switch flipDown ] Transcript show: 'Argument #on or #off is required.'.
Comparison: static vs dynamic
Based on the above-mentioned examples, we present a short summary on comparison of command pattern in static language (such as Java) and dynamic language (such as ruby).
- Dynamic languages, such as ruby, can change their behavior conveniently at run time, such as reflection and metaprogramming. Therefore, the implementation of command pattern in dynamic languages is much easier and more convenient than that in static language.
- Command pattern is to issue requests to objects without knowing anything about the operation being requested or the receiver of the request.” The Command design pattern suggests encapsulating (“wrapping”) in an object all (or some) of the following: an object, a method name, and some arguments. Although most actions of static language have to be performed at compile time, static languages have their own solutions to implement command pattern. For example, C and C# use "pointers to methods". Java does not support “pointers to methods”, but its reflection capability will do nicely. The “command” is a black box to the “client”. All the client does is call “execute()” on the opaque object.
Summary
In this article we took a quick look at the Command Pattern in static and dynamic languages. Command pattern is a great pattern for disconnecting the command originator and the system receiving the commands. This should be one of the first patterns you consider when thinking about creating a distributed type of system.
Furthermore, the comparison between command pattern in static languages and dynamic languages is actually the comparison between the static languages and dynamic languages. That is, in dynamic language, we can implement command pattern at runtime.
References
[1] Wikipedia - Command_Pattern. [Online]. http://en.wikipedia.org/wiki/Command_pattern
[2] Command Design Pattern. [Online]. http://sourcemaking.com/design_patterns/command
[3] Wikipedia - Dynamic programming language. [Online]. http://en.wikipedia.org/wiki/Dynamic_programming_language
[4] Wikipedia - Design Pattern. [Online]. http://en.wikipedia.org/wiki/Design_pattern_(computer_science)
[5] http://www.patterndepot.com/put/8/command.pdf
[6] Erich,G., Richard,H., Ralph,J.,and John,M.V. 1997. Design Patterns: Elements of Reusable Object-Oriented Software
[7] Freeman,E., Robson,E., Bates,B.,and Sierra,K. 2004. Head First Design Patterns