10
Behavioral Pattern: Command Chapter 5 – Page 1 There are times when the need arises to issue a request to an object without knowing anything about the operation being requested or the object receiving the request. The Command Pattern encapsulates the request as an object, which allows clients to be parameterized with different requests, requests to be logged or queued, and undoable operations to be supported.

Behavioral Pattern: Command

  • Upload
    fauna

  • View
    30

  • Download
    0

Embed Size (px)

DESCRIPTION

Behavioral Pattern: Command. Chapter 5 – Page 139. There are times when the need arises to issue a request to an object without knowing anything about the operation being requested or the object receiving the request. - PowerPoint PPT Presentation

Citation preview

Page 1: Behavioral Pattern: Command

Behavioral Pattern: CommandChapter 5 – Page 1

There are times when the need arises to issue a request to an object without knowing anything about the operation being requested or the object receiving the request. The Command Pattern

encapsulates the request as an object, which allows clients to be parameterized with different requests, requests to be logged or queued, and undoable operations to be supported.

Page 2: Behavioral Pattern: Command

The Command PatternChapter 5 – Page 2

The Command declares an interface for executing an operation.The ConcreteCommand defines a binding between a Receiver object and an action, implementing Execute by invoking the corresponding operation on the Receiver.

The Client creates a ConcreteCommand object and sets its Receiver.

Client Invoker Command

execute()

ConcreteCommandstate

execute()

Receiver

action()

+receiver

execute(): receiver.action()

The Invoker asks the Command to carry out the request.The Receiver knows how to perform the operations associated with carrying out the request.

Page 3: Behavioral Pattern: Command

Chapter 5 – Page 3Non-Software Example: DinerThe Waitress (invoker) generates an Order (concrete command) based upon what the Customer (client) selects from the menu, placing the order on a Check (command).The Order is then queued for the Cook (receiver) and, after being executed, the Order is delivered to the Customer.

Check

executeOrder()

Waitress

placeOrder()

Customer

order()

Cook

cookOrder()

Ordersubmitted

executeOrder()

+cook

executeOrder(): cook.cookOrder()

Note that the pad of “checks” used by each waitress is not dependent on the menu, so they can support commands to cook many different items.

Page 4: Behavioral Pattern: Command

Chapter 5 – Page 4Software Example:Integer Conversion

This C++ code will convert an integer into binary, octal, or hexadecimal, but its design is rather rigid.

int num; char ch;char tryAgain = 'y';string output;

do{ cout << "Enter an integer value: "; cin >> num; cout << "Enter a conversion type " << " (b=binary, o=octal, h-hexadecimal): "; cin >> ch;

  switch( toupper(x) )   {   case 'O':  { output = to_oct(num); break; }     case 'H':  { output = to_hex(num); break; }     case 'B':  { output = to_bin(num); break; }     default:   { output = "invalid"; }   } cout << "Result: " << output << endl << endl;

cout << "Try again? (Y or N) "; cin >> tryAgain;}while ( (tryAgain == 'Y') || (tryAgain == 'y') );

Every time the operations change, the switch block needs to be modified.

At the moment, operations are just functions which exist completely independently of each another, even though they're all in fact fairly similar, and selection is solely based on different character values. (These character values acting like runtime identifiers for the operations.)A better solution would build in some kind of relationship between all the operations, and tag each one with their character value identifier.

Page 5: Behavioral Pattern: Command

Chapter 5 – Page 5Integer Conversion With The Command PatternBy separating the conversion command from the object that performs it (the convertor), the total amount of code will undoubtedly increase, but the revised design breaks bigger problems down into sub-problems, producing small, reusable modules in the process.

IntegerConversion

convertInteger()

User

Integer-BinaryConverter

convert()

IntegerConversionToBinary

convertInteger()

IntegerConversionToOctal

convertInteger()

Integer-OctalConverter

convert()

IntegerConversionToHexadecimal

convertInteger()

Integer-HexadecimalConverter

convert()

Page 6: Behavioral Pattern: Command

Chapter 5 – Page 6Command-Based Integer Conversion Code in C++

#include <iostream>#include <string>#include <bitset> // Enables the determination of bit lengths#include <sstream> // Enables the creation/loading of string-streams#include <map> // Enables the mapping of dictionary pairs

using namespace std;

class converter{ public: virtual std::string convert(int) = 0; virtual ~converter() {}};

class hex_converter : public converter{ public: std::string convert(int i) { std::stringstream ss; ss << std::hex << i; return ss.str(); }};

Page 7: Behavioral Pattern: Command

Chapter 5 – Page 7

class oct_converter : public converter{ public: std::string convert(int i) { std::stringstream ss; ss << std::oct << i; return ss.str(); }};

class bin_converter : public converter{ public: std::string convert(int i) { std::bitset< sizeof(i) * CHAR_BIT > bits(i); std::stringstream ss; ss << bits; return ss.str(); }};

Page 8: Behavioral Pattern: Command

Chapter 5 – Page 8class dictionary

{ public: // The dictionary is loaded with the three character/converter pairs dictionary() { dict.insert( std::make_pair( 'B', new bin_converter ) ); dict.insert( std::make_pair( 'O', new oct_converter ) ); dict.insert( std::make_pair( 'H', new hex_converter ) ); }

converter* lookup(char x) { std::map<char, converter*>::const_iterator iter; iter = dict.find( toupper(x) ); if ( iter != dict.end() ) return iter->second; else return NULL; }

~dictionary() { while( dict.begin() != dict.end() ) { delete dict.begin()->second; dict.erase( dict.begin() ); } } private: // The STL <map> library enables mapping between objects // between two types. In this case, the three user-entered // characters have corresponding converters, and this // mapping is used to create a dictionary between them. std::map<char, converter*> dict;};

Page 9: Behavioral Pattern: Command

Chapter 5 – Page 9

void main(){ int num; char ch; char tryAgain = 'y'; string output; dictionary dict;

do { cout << "Enter an integer value: "; cin >> num; cout << "Enter a conversion type (b=binary, o=octal, h-hexadecimal): "; cin >> ch;

converter* con = dict.lookup( ch ); if ( con != NULL ) output = con->convert( num ); else output = "Invalid"; cout << "Result: " << output << endl << endl;

cout << "Try again? (Y or N) "; cin >> tryAgain; } while ( (tryAgain == 'Y') || (tryAgain == 'y') );}

Page 10: Behavioral Pattern: Command

Command Pattern AdvantagesChapter 5 – Page 10• The object that creates a command (the Client) is

not the same object that executes it (the Invoker). This separation provides flexibility in the timing and sequencing of commands.

• Materializing commands as objects means they can be passed, staged, shared, loaded in a table, and otherwise instrumented or manipulated like any other object.• Command objects can be thought of as "tokens" that are created by one entity that knows what needs to be done, and passed to another entity that has the resources for doing it.• Another of the main reasons for using the Command Pattern is that it provides a convenient way to store and execute an Undo function. Each command object can remember what it just did and restore that state when requested to do so if the computational and memory requirements are not too overwhelming.