1 / 23

Introduction to Design Patterns

Introduction to Design Patterns. Design patterns were mentioned several times so far And the Singleton Pattern was discussed in detail Along with several C++ memory management idioms The lectures this week will focus on them in detail What they are and how they are structured

maverick
Télécharger la présentation

Introduction to Design Patterns

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Introduction to Design Patterns • Design patterns were mentioned several times so far • And the Singleton Pattern was discussed in detail • Along with several C++ memory management idioms • The lectures this week will focus on them in detail • What they are and how they are structured • Introduction to the main “Gang of Four” design patterns • The first set of design patterns should feel familiar • They use or help support C++ features we’ve discussed • We’ve seen examples of some of them already • The second set is useful for advanced design topics • Different ways to set up how a group of objects interacts • How to send an object from one machine to another

  2. What’s a Pattern? What’s an Idiom? • According to Alexander, a pattern: • Describes a recurring problem • Describes the core of a solution • Is capable of generating many distinct designs • An Idiom is more restricted • Still describes a recurring problem • Provides a more specific solution, with fewer variations • Applies only to a narrow context • e.g., the C++ language

  3. “Gang of Four” Pattern Structure • Gang of Four (GoF): Gamma, Johnson, Helm, Vlissides • Authors of the popular “Design Patterns” book • A pattern has a name • e.g., the Command pattern • A pattern documents a recurring problem • e.g., Issuing requests to objects without knowing in advance what’s to be requested or of what object • A pattern describes the core of a solution • e.g., class roles, relationships, and interactions • Important: this is different than describing a design • A pattern considers consequences of its use • Trade-offs, unresolved forces, other patterns to use

  4. Simple Pattern Form Example: “Singleton” • Problem • Want to ensure a single instance of a class, shared throughout a program • Context • Need to address initialization versus usage ordering • Solution • Provide a global access method (static in C++) • First use of the access method instantiates the class • Constructors for instance can be made private • Consequences • Object is never created if it’s never used • Object is shared efficiently among all uses

  5. Singleton Class Template • Parameterized by a concrete type • Notice constructor and variable are private • Initialization of static s_instance variable • Outside class declaration • Outside method definition • Done before any method in compilation unit is called • Instance accessor method can then check • For a 0 instance pointer • And create a new instance if so • Same object is always returned by accessor template <class T> class Singleton { public: static T *instance(); private: Singleton(); static T *instance_; }; // Initialize the static instance pointer template <class T> T *Singleton::instance_ = 0; // Global access point template <class T> T *Singleton::instance() { // check for existing instance if (Singleton<T>::instance_ == 0) { // may want a try/catch here Singleton<T>::instance_ = new Singleton<T>; } return Singleton<T>::instance_; };

  6. Using the Singleton Template • Need a single instance • E.g., a common buffer of text tokens from a file • Shared across multiple points in the code • Need to share buffer • Copying is wasteful • Need to refer to same object instance • What about deleting these instances? Foo *f1 = Singleton<Foo>::instance(); Foo *f2 = Singleton<Foo>::instance();

  7. A More Complete Pattern Form: “Command” • Problem • Want to issue requests to objects • Don’t know in advance which request(s) will be made • Don’t know in advance to what object(s) they will go • Solution core • Encapsulate function call parameters and target object reference inside an “execute” method • Consequences • Decouples invocation/execution • Commands are first-class objects (elevates functions) • Easy to compose, add new ones • Example we’ve seen already • STL function objects

  8. Structure Diagram Example: “Command” • Shows fixed class/interface rolesin the pattern • Shows fixed relationships between roles <<Client>> client role command role * <<Command>> <<Invoker>> execute ( ) inheritance <<ConcreteCommand>> <<Receiver>> execute ( ) action(args) state_

  9. Collaboration Diagram Example: “Command” • Shows dynamic interactions between pattern roles • Labels show what interaction does (here, labels show methods called) • Often used to diagram each of several key scenarios • “Happy path” when everything works, plus different error cases aClient aReceiver aCommand anInvoker new Command(aReceiver) StoreCommand(aCommand) time / / / / / / / / action() execute()

  10. Pattern Example: Command class OpenCommand : public Command { public: OpenCommand(Application*); virtual void Execute(); protected: virtual const char* AskUser(); private: Application* application; char* _response; }; OpenCommand::OpenCommand(Application* a){ _application = a; } void OpenCommand::Execute () { const char* name = AskUser(); if (name != NULL) { Document* doc = new Document(name); _application->Add(doc); doc->Open(); } } class Command { public: virtual ~Command(); virtual void Execute() = 0; protected: Command(); };

  11. Idiom Example: Guard • Problem • Want to tie key scoped behaviors to actual program scopes • e.g., program trace, resource acquisition/release, locking • However, tying functions to functions is error-prone • e.g., forgetting the release call, exceptional return paths • Solution • Design a special adapter class whose constructor and destructor call the key scope entry and exit behaviors • Create a guard object on the program call stack (in a scope) • Context limitations • Mainly limited to languages with constructor/destructor

  12. Part I: Familiar Design Patterns • First, a few more patterns related to course so far • Iterator: access elements sequentially no matter how stored • Adapter: converts interface you get into one you want • Factory method: creates a related type polymorphically • Next lecture we’ll examine other important patterns • Bridge: allow interface, implementation to vary separately • Chain of responsibility: give request to chain of handlers • Composite: common interface to composite/simple objects • Interpreter: build a representation for a simple language • Observer: tell registered observers when state changes • Strategy/template method: vary steps/all of an algorithm • Proxy: forward requests from placeholder to another object • Memento: package up object state without violating encapsulation • Visitor: allow various operations on fixed set of objects (2-way dispatch)

  13. Iterator Pattern • Problem • Want to access aggregated elements sequentially • E.g., traverse a list of names and print them out • Don’t want to know details of how they’re stored • E.g., in a linked list, or an array, or a balanced binary tree • Solution core • Provide a separate interface for iteration over each container • Consequences • Frees user from knowing details of how elements are stored • Decouples containers from algorithms (crucial in C++ STL) • Examples we’ve seen before • C++ pointers, C++ STL list<int>::iterator

  14. Iterator Pattern Structure Diagram • Each container may have a different iterator type • Iterator knows the internals of the container • Object-oriented form shown below (for user-defined types) • Slightly different with built-in types, templates • E.g., no inheritance relationship, may use traits, etc. <<Iterator>> first() next() is_done() current_item() <<Container>> create_iterator() creates <<ConcreteAggregate>> <<ConcreteIterator>> has-a

  15. class StringIterator { public: StringIterator (char * s) : s_ (s), current_ (s) {} void first () { current_ = s_; } void next () { ++current_; } bool is_done () { return *current_ == 0; } char * current_item () { return current_; } private: char *s_; char *current_; }; Object-oriented version of iterator is natural to implement as a class in C++ Constructor stores passed pointer to C-style string s, positions current_ at s first (re)positions iterator at the start of the string next moves iterator to the next position is_done tests whether iterator is at the end of the string current_item returns a pointer to the character at the current iterator position Object-Oriented Iterator Example

  16. unsigned int letter_count (StringIterator& si, char c) { unsigned int count = 0; for (si.first (); ! si.is_done ( ); si.next ()) if (*si.current_item () == c) ++count; return count; } Iterators naturally support use in looping constructs Here we show a for loop first is used to initialize is_done used for loop test next used to increment current_item is used in loop body’s value comparison When the loop completes, the function shown has Iterated through entire string Counted occurrences of the character value in the string Returned the occurrence count Using an Object-Oriented Iterator

  17. unsigned int letter_count (char * si, char * start, char c) { unsigned int count = 0; for (si = start; *si != 0; ++si) if (*si == c) ++count; return count; } This pattern is not limited to C++ or even to object-oriented languages For example, C code to the left Some changes are needed Need to pass in start of string (not encapsulated in iterator class) Syntax for pointer operators instead of method invocations Solution core clearly the same Program pretty much the same Result of function is the same Looks a lot like STL examples Using a Procedural Iterator.

  18. Adapter Pattern • Problem • Have an object with an interface that’s close to but not exactly what we need • Context • Want to re-use an existing class • Can’t change its interface • Impractical to extend class hierarchy more generally • Solution • Wrap a particular class or object with the interface needed (2 forms: class form and object forms) • Consequences • Implementation you’re given gets interface you want

  19. Abstract base class provides desired interface Concrete Impl class provides the implementation Adapter glues them together via inheritance Adapter Pattern Structure Diagram (Class Form) Interface Impl method () = 0; impl_method (); private public Adapter method ();

  20. Abstract base class provides desired interface Concrete Impl class provides the implementation Adapter class glues them together via delegation Adapter Pattern Structure Diagram (Object Form) Interface method () = 0; Adapter Impl method (); impl_method ();

  21. Problem You want a class to create a related class polymorphically Context Each class knows which version of the related class it should create Solution Declare abstract method that derived classes override Consequences Type created matches type(s) it’s used with Factory Method Pattern Iterator Container first()=0; next()=0; current()=0; is_done()=0; Iterator * make_iterator()=0; Array Array_Iterator Iterator * make_iterator(); first(); next(); current(); is_done(); Linked_List List_Iterator Iterator * make_iterator(); first();next(); current(); is_done();

  22. Use Factory Method pattern if you have inheritance hierarchies of classes (with abstract base classes) Use the Traits idiom when you want a generic programming version of this technique E.g., when you have unrelated classes capable of providing common interfaces (like STL vectors and linked lists) Traits version relies on templates and compile-time dispatching of calls Inheritance based version relies on run-time dynamic resolution of calls (virtual functions and overriding) Object-Oriented vs. Generic Variations

  23. Summary • We’ve looked at a number of important patterns so far • Singleton: share a class instance across multiple uses • Command: package up a function as an object • Iterator: access elements sequentially no matter how stored • Adapter: converts interface you get into one you want • Factory method: creates a related type polymorphically • Next time: Design Patterns II • Bridge: allow interface, implementation to vary separately • Chain of responsibility: give request to chain of handlers • Composite: common interface to composite/simple objects • Interpreter: build a representation for a simple language • Observer: tell registered observers when state changes • Strategy/template method: vary steps/all of an algorithm • Proxy: forward requests from placeholder to another object • Memento: package up object state without violating encapsulation • Visitor: allow various operations on fixed set of objects (2-way dispatch)

More Related