Design Patterns 2

You might also like

Download as ppt, pdf, or txt
Download as ppt, pdf, or txt
You are on page 1of 19

Design Patterns Part II

(TIC++V2:C10)

Yingcai Xiao 10/01/08

Outline
What For How Examples

The Command Pattern

The Command Pattern


Common task: wrapping a function in an object => object-oriented command. Use: commands can be passed around as objects with the functions encapsulated. Implementation example: class Command { public: virtual void execute() = 0; // can be any appropriate name }; class CommandList{// An object that holds commands: vector<Command*> commands; public: void add(Command* c) { commands.push_back(c); } void run() { vector<Command*>::iterator it = commands.begin(); while(it != commands.end()) (*it++)->execute(); } };

The Command Pattern


Example Usage (someone else is using your Command and CommandList): class Hello : public Command { public: void execute() { cout << "Hello "; } }; class World : public Command { public: void execute() { cout << "World! "; }}; class IAm : public Command { public: void execute() { cout << "I'm the command pattern!"; }}; int main() {
CommandList cl;

cl.add(new Hello); cl.add(new World); cl.add(new IAm); cl.run(); } ///:~

The Command Pattern


Applications:

1. queue a set of actions to be performed collectively 2. object-oriented callbacks (run can be executed at any desired time). 3. Callbacks are essential to even-driven programming. We can use Command to collect all event handlers and execute run when the related event occurs. 4. Undo: Each time the user performs an operation, the corresponding undo Command object is placed into a queue. Each Command object that is executed backs up the state of the program by one step.
Other Names of Command Pattern: function object functor

State Pattern

State Pattern
State Pattern: provides a surrogate class to its implementation classes and allows dynamic change of implementations. Applications: Like Proxy, State is created by having a front-end object that uses a back-end implementation object to fulfill its duties. However, the State pattern switches from one implementation to another during the lifetime of the front-end object, in order to produce different behavior for the same function call(s). Relationship between the surrogate class and the implementation classes: (Note a proxy class has only one implementation class as shown in the bottom figure).

An Example not Using State Pattern


class Creature { bool isFrog; // using a state variable makes code not scalable public: Creature() : isFrog(true) {} void greet() { if(isFrog) cout << Kiss me, please!" << endl; else cout << "Darling!" << endl; } void kiss() { isFrog = false; } }; int main() { Creature creature; creature.greet(); creature.kiss(); creature.greet(); } ///:~

State Pattern Example


class Creature { class State { public: virtual string response() = 0; }; class Frog : public State { public: string response() { return "Kiss me, please!"; } }; class Prince : public State { public: string response() { return "Darling!"; } }; State* state; public: Creature() : state(new Frog()) {} void greet() { cout << state->response() << endl; } void kiss() { delete state; state = new Prince(); } };

State Pattern Example


int main() { Creature creature; creature.greet(); creature.kiss(); creature.greet(); } ///:~ State change by dynamically switching the implementation objects. Use state pattern to make the code scalable and easy to maintain. Adding a new state has no impact no existing state classes and does not have to worry about the state variable and its possible states. If we had used a state variable, adding a new state means adding a new possible value for the state variable, therefore change of all code referencing the state variable. Hard to scale and easy to make mistakes. Example, adding a King2Be state in the above example. Colored code are new/modified code.

Scalability of State Pattern


#include <iostream> #include <string> using namespace std; class Creature { class State { public: virtual string response() = 0; }; class Frog : public State { public: string response() { return "Kiss me, please!"; } }; class Prince : public State { public: string response() { return "Darling!"; } }; class King2Be : public State { public: string response() { return "Hi, Dad, I am back!"; }}; State* state; public: Creature() : state(new Frog()) {} void greet() { cout << state->response() << endl; } void kiss() { delete state; state = new Prince(); } void return2palace() { delete state; state = new King2Be();} }; int main() { Creature creature; creature.greet(); creature.kiss(); creature.greet(); creature.return2palace(); creature.greet(); } ///:~

Scaling the Example not Using State Pattern


#include <iostream> using namespace std; class Creature { int isFrog; public: Creature() : isFrog(0) {} void greet() { if(isFrog == 0) cout << "Kiss me, please!" << endl; else if (isFrog == 1) cout << "Darling!" << endl; else if (isFrog == 2) cout << "Hi, Dad, I am back" << endl; } void kiss() { isFrog = 1; } void return2palace() { isFrog = 2;} };

int main() { Creature creature; creature.greet(); creature.kiss(); creature.greet(); creature.return2palace(); creature.greet(); } ///:~

Application Framework / Template Method (TIC++V2C10)

Application Framework / Template Method


An application framework allows you to inherit from a class or set of classes and create a new application, reusing most of the code in the existing classes and overriding one or more functions in order to customize the application to your needs. A fundamental concept in the application framework is the Template Method, which is typically hidden beneath the covers and drives the application by calling the various functions in the base class (some of which you have overridden in order to create the application).

An important characteristic of the Template Method is that it is defined in the base class and cannot be changed the Template Method is the thing that stays the same. It calls other base-class functions (the ones you override) in order to do its job, but the client programmer isnt necessarily able to call it directly. Note that the Template Method is the code that stays the same, and the functions that you override are the code that changes. However, this change is fixed at compile time via inheritance.

Application Framework / Template Method: Example


class ApplicationFramework { protected: virtual void customize1() = 0; virtual void customize2() = 0; public: void templateMethod() { for(int i = 0; i < 5; i++) { customize1(); customize2(); } } }; // Creatting a new "application by a application programmer class MyApp : public ApplicationFramework { protected: void customize1() { cout << "Hello "; } void customize2() { cout << "World!" << endl; } }; int main() { MyApp app; app.templateMethod(); } ///:~

Strategy: choosing the algorithm at runtime

Strategy: choosing the algorithm at runtime


Strategy Pattern: a problem can be solved in a number of ways, allow the user to chose a strategy to solve the problem at runtime. Following the maxim of prefer composition to inheritance, we can use composition to approach the problem of separating code that changes from code that stays the same, and produce the Strategy pattern. This approach has a distinct benefit: the code that changes can be plugged in at runtime. Strategy also adds a Context which can be a surrogate class that controls the selection and use of the particular strategy objectjust like State (which allows dynamic changes of state) !

Strategy : Example
class NameStrategy {public: virtual void greet() = 0; }; class SayHi : public NameStrategy { public: void greet() {cout << "Hi! How's it going?" << endl; }}; class Ignore : public NameStrategy { public: void greet() { cout << "(Pretend I don't see you)" << endl; }}; class Admission : public NameStrategy { public: void greet() { cout << "I'm sorry. I forgot your name." << endl; }}; class Context { NameStrategy& strategy; public: Context(NameStrategy& strat) : strategy(strat) {} void greet() { strategy.greet(); } }; int main() { SayHi sayhi; Ignore ignore; Admission admission; Context c1(sayhi), c2(ignore), c3(admission); c1.greet(); c2.greet(); c3.greet(); } ///:~

You might also like