Download presentation
Presentation is loading. Please wait.
Published byMariah Byrd Modified over 10 years ago
1
Design Patterns Part III (TIC++VII:C10) Yingcai Xiao 07/08/08
2
Outline What For How Examples
3
Simplifying Idioms Patterns to keep code simple and straightforward.
4
The Messenger Pattern Common task: to package information into an object. Use: to pass information around in one piece instead of in separate pieces. Implementation example: C10:MessengerDemo.cpp class Point { // A messenger public: int x, y, z; // public for easier coding Point(int xi, int yi, int zi) : x(xi), y(yi), z(zi) {} // init Point(const Point& p) : x(p.x), y(p.y), z(p.z) {} // init Point& operator=(const Point& rhs) { // assignment x = rhs.x; y = rhs.y; z = rhs.z; return *this; } friend ostream& operator<<(ostream& os, const Point& p) { return os << "x=" << p.x << " y=" << p.y << " z=" << p.z; // display } };
5
The Messenger Pattern class Space { public: static Point translate(Point p, int dx, int dy, int dz) { p.x += dx; p.y += dy; p.z += dz; return p; } }; int main() { Point p1(1, 2, 3); // a static method can be used without creating an object Point p2 = Space::translate(p1, 3, 2, 1)); cout << "p1: " << p1 << " p2: " << p2 << endl; }
6
The Collecting Parameter Pattern Common task: collecting parameter is messenger’s big brother, it captures information from the function to which it is passed. Use: to collect information from multiple functions. Implementation example: C10:CollectingParameterDemo.cpp
7
The Collecting Parameter Pattern class CollectingParameter : public vector {}; // the class class Filler { public: // the functions from which to collect information from void f(CollectingParameter& cp) { cp.push_back("accumulating"); } void g(CollectingParameter& cp) { cp.push_back("items"); } void h(CollectingParameter& cp) { cp.push_back("as we go"); } }; int main() { Filler filler; CollectingParameter cp; filler.f(cp); filler.g(cp); filler.h(cp); vector ::iterator it = cp.begin(); while(it != cp.end()) cout << *it++ << " "; cout << endl; } ///:~
8
The Singleton Pattern Common task: to allow one and only one instance of a class Use: to make sure there is no way to create more than one instance of a special class for each application (e.g. the eAF - Application Framework class in PA4. ) Implementation example: C10:SingletonPattern.cpp class Singleton { static Singleton s; // static: one copy per class, self declaration int i; // a single data member for demonstration Singleton(int x) : i(x) { } Singleton& operator=(Singleton&); // Disallowed Singleton(const Singleton&); // Disallowed public: static Singleton& instance() { return s; } // return the reference to the sole instance int getValue() { return i; } // access its void setValue(int x) { i = x; } };
9
The Singleton Pattern C10:SingletonPattern.cpp // declaring s so that it can be referenced Singleton Singleton::s(47); int main() { Singleton& s = Singleton::instance(); cout << s.getValue() << endl; Singleton& s2 = Singleton::instance(); s2.setValue(9); cout << s.getValue() << endl; // the following will not compile // Singleton *sp = new Singleton(); // no appropriate default constructor available // Singleton *sp = new Singleton(8); //cannot access private member // Singleton *sp = new Singleton(Singleton::instance()); // cannot access private member } ///:~
10
The Singleton Pattern The key to creating a Singleton is to prevent the client programmer from having any control over the lifetime of the object. declare all constructors private (note: a private constructor can’t be used to create new objects, but can be used to declare an static object). prevent the compiler from implicitly generating any constructors (compiler will not create a default construct if there is any kind of constructors there). The copy constructor and assignment operator (which intentionally have no implementations, since they will never be called) are declared private to prevent any sort of copies being made. You must also decide how to create the single instance of the class. Here, it’s created statically. Lazy initialization waits until the client programmer asks for one and create it on demand. Tt only makes sense if it is expensive to create your object, and if you don’t always need it. If you return a pointer instead of a reference, the user could inadvertently delete the pointer, so the implementation above is considered safest (the destructor can also be declared private or protected to alleviate that problem).
11
The Singleton Pattern The object ( static Singleton s) should be stored privately. You provide access through public member functions. Here, instance( ) produces a reference to the Singleton object. The rest of the interface (getValue( ) and setValue( )) is the regular class interface.
12
Decoupling Event Handling with Command Common task: to separate event handling from “normal” computation thread of the application. Use: to make sure event handling and the normal computation task are separated so that they can each reside in a separate execution thread for concurrent processing. Implementation example: C10:MulticastCommand.cpp To avoid coupling of code using the Command pattern. Each “normal” operation must periodically call a function to check the state of the events, but with the Command pattern these normal operations don’t need to know anything about what they are checking, and thus are decoupled from the event-handling code.
13
Decoupling Event Handling with Command int main() { // Randomize for firing button click event randomly. srand(time(0)); // Declare event generators Button b1("Button 1"), b2("Button 2"), b3("Button 3"); // Declare event handlers CheckButton cb1(b1), cb2(b2), cb3(b3); // Add event handlers to the TaskRunner’s list. TaskRunner::add(cb1); TaskRunner::add(cb2); TaskRunner::add(cb3); // start the loop to perform “normal” computational work. cout << "Control-C to exit" << endl; while(true) { procedure1(); procedure2(); procedure3(); // events will be checked and processed within the procedures // while they perform the “normal” computational work. } } ///:~
14
Decoupling Event Handling with Command // The procedures to perform the “normal” computational work. // These need to be occasionally "interrupted" in order to // check the state of the buttons or other events: void procedure1() { // Perform procedure1 operations here. //... TaskRunner::run(); // Check all events } void procedure2() { // Perform procedure2 operations here. //... TaskRunner::run(); // Check all events } void procedure3() { // Perform procedure3 operations here. //... TaskRunner::run(); // Check all events }
15
Decoupling Event Handling with Command The event generators are Button b1, b2, b3; The event handlers are CheckButton cb1, cb2, cb3; All event handlers are added to TaskRunner’s list. The main loop starts the procedures to perform the “normal” computational work. The procedures will run the TaskRunner patriotically to check and process events. The procedures knows how to perform “normal” computational work, but does not know anything about how the events are generated, checked or processed, therefore decoupled from event handling.
16
Review: Command Pattern Common task: wrapping a function in an object => object-oriented command. AKS: functor, an object representing a function (event handler) Use: commands can be created, saved and passed around as objects with the functions (handlers) encapsulated. Implementation example: class Command { public: virtual void execute() = 0; // can be any appropriate name }; class CommandList {// An object that holds commands: vector commands; public: void add(Command* c) { commands.push_back(c); } void run() { vector ::iterator it = commands.begin(); while(it != commands.end()) (*it++)->execute(); } };
17
Decoupling Event Handling with Command class Task { public: virtual void operation() = 0; }; class TaskRunner { static vector tasks; TaskRunner() {} // Make it a Singleton by privatized the constructors TaskRunner& operator=(TaskRunner&); // Disallowed TaskRunner(const TaskRunner&); // Disallowed static TaskRunner tr; // the single instance of the class public: static void add(Task& t) { tasks.push_back(&t); } static void run() { vector ::iterator it = tasks.begin(); while(it != tasks.end()) (*it++)->operation(); } };
18
Decoupling Event Handling with Command // Declare all static objects for later reference TaskRunner TaskRunner::tr; vector TaskRunner::tasks; class EventSimulator { clock_t creation; clock_t delay; public: EventSimulator() : creation(clock()) { delay = CLOCKS_PER_SEC/4 * (rand() % 20 + 1); cout << "delay = " << delay << endl; } bool fired() { return clock() > creation + delay; } };
19
Decoupling Event Handling with Command // Something that can produce asynchronous events: class Button { bool pressed; string id; EventSimulator e; // For demonstration public: Button(string name) : pressed(false), id(name) {} void press() { pressed = true; } bool isPressed() { if(e.fired()) press(); // Simulate the event return pressed; } friend ostream& operator<<(ostream& os, const Button& b) { return os << b.id; } };
20
Decoupling Event Handling with Command // The Command object class CheckButton : public Task { Button& button; bool handled; public: CheckButton(Button & b) : button(b), handled(false) {} void operation() { if(button.isPressed() && !handled) { cout << button << " pressed" << endl; handled = true; } };
21
Decoupling Event Handling with Command The event handlers are defined as Command objects (functors) following the Command pattern. This is an object-oriented representation of event handlers. The Command class is Task here. CheckButtons are event handlers implementing the Task/Command. The CommandList class is TaskRunner here. All event handlers are added to TaskRunner’s list. TaskRunner is a Signleton, we only need one TaskRunner. When TaskRunner runs, it invokes “operation” for all CheckButtons (i.e. the event handlers.)
22
Decoupling Event Handling with Command EventSimulator creates a random delay time, and changes fired() from returning false to true when the delay time has passed (“event has been fired”). EventSimulator objects are used inside Buttons to simulate the act of a user event occurring at some unpredictable time. TaskRunner is run periodically by all the “normal” code in the program (procedure1( ), procedure2( ) and procedure3( )).
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.