340 likes | 578 Views
Don’t reinvent the wheel. Design Patterns. The Design Patterns Book. Design Patterns. “simple and elegant solutions to specific problems in object-oriented software design”
E N D
Don’t reinvent the wheel Design Patterns
Design Patterns • “simple and elegant solutions to specific problems in object-oriented software design” • If there are “tried and true” approaches that have worked on similar problems in the past, you can benefit by adopting them. • Design patterns can enhance the vocabulary of design • The people you are talking to have to know the names and meanings of the patterns
Patterns are Reusable Solutions • Each pattern has • A pattern name • Naming the pattern provides powerful short-hand for design solutions • A problem description • The problem in general terms • A solution • Describes the design • A set of consequences • Trade-offs involved in applying the pattern
Singleton • Intent: • Ensure a class only has one instance, and provide a global point of access to it. • Motivation • It’s important for some classes to have exactly one instance. • We often need global access to just objects • Applicability • Exactly one instance of a class must be accessible to clients from a well-known access point • The sole instance should be extensible by subclassing
Implementation in C++ • Static Singleton* Instance(); • Instance does one of the following • Creates an instance of the class and returns it, storing the value as a static private data member • Returns a previously created instance • Protected Singleton constructor • External code cannot create an instance of the singleton class
Singleton Example • SpriteLand • Design Patterns uses Instance() • We use getSpriteLand() • Look at SpriteLand implementation
Inheritance versus Composition • Object Inheritance (is a) • Whitebox reuse • Internals of parent class often visible • Breaks encapsulation • Can’t change at runtime • Non-abstract base classes define part of physical representation • Directly supported by language (polymorphism) • Easy to modify an existing implementation • Object Composition (has a) • Blackbox reuse • Internals of delegate class not visible • Can change at runtime (limited by base class data type) • Fewer implementation dependencies • Smaller hierarchies
Prefer Composition to Inheritance • Delegation • A object contains a delegate object • Requests to the object are explicitly referred to the delegate, similarly to how a subclass implicitly refers functions that have not been overridden to its parent class. • Disadvantage • Dynamic, parameterized software is harder to understand
Strategy (or Policy) Pattern • Intent – Define a family of algorithms, encapsulate each one, and make them interchangeable. • class Tower • { • public: • void setShootingStrategy(ShootingStrategy* newStrategy) { shootingStrategy = newStrategy;} • Target* findTarget() { shootingStrategy_->findTarget; }; • private: ShootingStrategy* shootingStrategy_; • };
Creational Patterns • Factory • Abstract Factory • Builder • Prototype • Singleton
Factory Method (Virtual Constructor) • Define an interface for creating an object, but let subclasses decide which class to instantiate. • class TowerDefenseGame • { • // Factory methods • virtual Path* makePath(); • virtual Tower* makeMonkeyTower(); • } • // The TowerDefenseGame can be subclassed to //EasyTowerDefenseGame and DifficultTowerDefenseGame • makePath() and makeMonkeyTower() are overridden so that EasyTowerDefenseGame::makePath() returns an instance of class EasyPath, and DifficultTowerDefenseGame::makePath() return an instance of class DifficultPath.
Abstract Factory (or Kit)(Factory that uses delegation rather than inheritance) • Provide an interface for creating families of related or dependent objects without specifying their concrete classes. • class ComponentFactory • { • Path* makePath(); • Tower* makeMonkeyTower(); • }; • class TowerDefense • { • public: • Path* makePath() { componentFactory->makePath(); } • private: • ComponentFactory* componentFactory_; • };
Prototype • Specify the kinds of objects to create using a prototypical instance, and create new objects by copying the prototype. • class TowerDefenseGame • { • public: • TowerDefenseGame(Path* prototypePath, • Tower* prototypeMonkeyTower) : prototypePath_(prototypePath), prototypeTower_(prototypeTower) {} • Path* makePath() { return prototypePath_.clone(); } • Tower* makeMonkeyTower() { return prototypeTower_.clone(); } • private: • Path* prototypePath_; • Tower* prototypeTower_; • }
Structural Design Patterns • How classes and objects are composed to form larger structures • Structural class patterns use inheritance • Structural object patterns use composition
Adapter (aka Wrapper) • Convert the interface of a class into another interface clients expect. • Can use multiple inheritance • Structural class pattern • Can use composition • Structural object pattern
Bridge (aka Handle/Body aka PIMPL) • Decouple an abstraction from its implementation so that the two can vary independently. • C++ • Pointer to implementation • class myClass • { • public: • // Define public interface private: myClassImpl* implementation; • };
Uses for Bridge • Completely divorce public interface from implementation. (C++ headers) • Allow different implementations to be assigned at runtime. • You want to share an implementation between multiple objects
Some Sample Patterns (from the Design Patterns Book) • Singleton • Ensure a class only has one instance, and provide a global point of access to it. • Factory Method • Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses. • Bridge • Decouple an abstraction from its implementation so that the two can vary independently • Façade • Provide a unified interface to a set of interfaces in a subsystem. Façade defines a higher-level interface that makes the subsystem easier to use.
Composite • Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly. • Problem – We want to build composite objects from primitive objects, and then use the composites as though they are primitives. • Abstract base class represents both primitives and their containers. • Windows Forms use this pattern. • Windows have components that can also be windows • Messages sent to windows are forwarded to their components.
Decorator • Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality. • Decorators are implemented as classes that contain an instance (component) of the thing that they are decorating • Example • Window contained in a ScrollingDecorator which in turn is contained in a BorderDecorator. Window requests are routed first to the outer-most decorator and forward to inner decorators until finally reaching the window.
Decorator Implementation • Decorator must conform to interface of decorated component. • Decorators should be light-weight (focus on interface, not data storage). • Change the skin of an object rather than change the guts.
Facade • Provide a unified interface to a set of interfaces in a subsystem • Provide a single-point of contact between subsystems • Provide a task-oriented interface to a set of components (compile, rather than scan, parse, generate code, optimize, etc). • Decouple dependencies between components and component clients.
Flyweight • Use sharing to support large numbers of fine-grained objects efficiently • Flyweight is a shared object that can be used in multiple contexts simultaneously • Intrinsic state • What is stored inside the flyweight • Independent of flyweight’s context • Extrinsic state • Client objects track the extrinsic state and pass it to member functions that need it. • Example • Flyweight for each letter in a text document • Position and typographic style are extrinsic state
Flyweight Applicability • Application uses a large number of objects • Most object state can be extrinsic • Many instances can make use of a shared object.
Proxy • Provide a surrogate or placeholder for another object to control access to it. • Uses • An expensive class can be replaced by a proxy until it must be created • Control access based on a set of access rights • Smart pointers!
Behavioral Patterns • Behavioral class patterns • Use inheritance • Behavioral object patterns • Use composition
Chain of Responsibility • Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it. • Each object, starting with the first in in a chain of objects, get a chance to handle the request, or pass it along the chain. • Example • Windows
Command • Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue, or log requests, and support undoable operations. • Use when you want to build an object that invokes a command (like a UI button), and you don’t want to tie a button press to a hard-coded function. • Delegates in C# come to mind • Execute() can store state to provide an Unexecute() function.
Iterator (aka Cursor) • Provide a way to access the elements of an aggregate (collection) object sequentially without exposing its underlying representation. • Support variation in traversal method • Preorder, postorder, inorder tree traversal • Simplify the interface • Use of multiple iterators allows us to keep track of multiple places in the aggregate.
Mediator • Define an object that encapsulates how a set of objects interact. Mediator promotes loose coupling by keeping objects from referring to each other explicitly, and it lets you vary their interaction independently. • Each object knows only of the Mediator • Reduce coupling between components • Mediator must know objects and interactions • Centralizes control • Simplifies object protocols
Memento (aka Token) • Without violating encapsulation, capture and externalize an object’s internal state. • Often used to restore state via undo operations • Originator (the class whose state will be saved as a memento) supports the methods • CreateMemento() // Create a memento with state • SetMemento(Memento m) // Restore state • Mementos are opaque objects that are used only for setMemento operations
Observer (aka publish-subscribe) • Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically. • Example: • Spreadsheet data used to generate a grid-view of the data and several different types of data • When the underlying data changes, all presentations must be updated. • The “Subject” knows its observers • Subject notifies each observer of change • Upon notification, observers request data from subject • In Model/View/Controller pattern, model is subject, views are observers.
State • Allow an object to alter its behavior when its internal state changes. The object will appear to change its class. • Abstract “State” class contains a pointer to concrete state classes that to whom all requests are delegated. • Example: • Person class might be implemented as an abstract state where • Sleeping • Working • Playing provide concrete implementations of “answerPhone()”