Design Patterns by Eric Gamma... ** Design Patterns - ~ capture solutions that have developed and evolved over time - ~ makes it easier to use successful designs and architectures - "Pattern" describe a problem that occurs over and over again in our environment - ~ are description of communication objects and classes that are customized to solve a general design problem in a particular context. - Creational design patterns abstract the instantiation process. They help make a system independent of how its objects are created, composed and represented. Class creation pattern use inheritance where as object creational pattern uses delegation. - Structural patterns are concerned with how classes and objects are composed to form larger structures. It describes ways to compose objects to realize new functionality. It has the ability to change composition at runtime. - Behavioral Pattern: are concerned with algorithm and assignments of responsibilities between objects. Patterns of communication, complex control flow. Behavioral class pattern: Use inheritance to distribute behavior between classes. Behavioral object pattern: Use object composition. ** Creational Pattern: Abstract Factory - Provides an interface for creating families of related or dependant object without specifying their concrete classes - Used to create multiple objects - Applicability: Use ~ when: - a system should be independent of how its products are creates, composed, and represented - a system should be constructed with one of multiple families of products - a family of related product objects is designed to be used together and you need to enforce this constraint - you want to provide a class library of products, and u want to reveal just their interfaces, not their implementation. - ~ defers creation of product objects to its concrete sub class(es) - Consequences: ~ has the following benefits and liabilities: - It isolates clients from implementation classes. Clients manipulate instances through their abstract interfaces. - It makes exchanging product families easy. It can use different product configuration just by changing the concrete factory. - Promotes consistency among products - Supporting new kind of products is difficult, as interface is fixed - Implemented as a singleton - Abstract factory and concrete factory maybe the same class for small modules ** Creational Pattern: Builder - Separate the construction of a complex object from its representation, so that the same construction process can create different representations. - Used to create a single object - The class constructing the object different from the object's class and is contained in the object's class. Actual construction work is delegated to a sub class of the constructing class. - Applicability: Use the ~ pattern when, - the algorithm creating a complex object should be independent of the parts that make up an object and how they are assembled. - the construction object must allow different representations of the object that's constructed - Consequences: - lets you vary the product's internal representation - isolates code for construction and representation - gives you finer control over the construction process ** Creational Pattern: Factory Method - Define an interface to create an object but let the subclass decide with class to instanciate. ~ lets a class defer instantiation to its subclass. - Used to create a single object - Applicability: Use ~ when - a class can't anticipate the class of objects it must create - a class wants its sub classes to specify the object it creates - classes delegate responsibility to one of several helper sub classes, and you want to localize the knowledge of which helper subclass is the delegate - Consequences: - Provides hooks for subclasses - Connects parallel class hierarchies: Used to create and implement frameworks - Clients may have to sub classes just for creation purposes ! ** Creational Pattern: Prototype - Specify the kinds of objects to create using a prototypical instance and create new objects by copying this prototype - To be used as a prototype, an object must support a copy constructor and a "clone" operation - Applicability: Use the ~ pattern when a system should be independent of how its products are created, composed, and represented; and - when the classes to instantiate are specified at run time, eg. dynamic loading - to avoid building a class hierarchy of factories that parallels the class hierarchy of products - when instances of a class can have one of only a few different combinations of state. - Consequences - Adding and removing products at run time - Specifying new objects by varying values - Specifying new objects by varying structure - Reduced subclassing - Configuring an application with classes dynamically ** Creational Pattern: Singleton - Ensure a class has only one instance and provide a global point of access to it - Applicability: Use a ~ pattern when - there must be exactly one instance of a class and it must be accessible to clients from a well known access point - when the sole instance should be extensible by subclassing, and clients should be able to use an extended instance without modifying their code - Consequences - Controlled access to sole instance - Reduced name space: ~ is an improvement over global variables - Permits refinement of operations and representation - Permits a variable number of instances: can be used in pooling - More flexible than class operations. ** Structural Pattern: Adapter (Also known as wrapper) - Convert the interface of a class into an interface clients expects. - Applicability: Use Adapter pattern when: - You want to use an existing class and its interface does not match the one you need. - You want to create a usable class that co-operates with unrelated or unforeseen classes. - You need to use several existing subclasses, but its impractical to adapt their interface by sub-classing every one. An object adapter can adapt the interface of its parent class. - Adaption can be done by inheritance (class adaption) or composition (object adaption). - Consequences: A class adapter - won't work when we want to adapt a class and all its subclasses. - lets adapter override some of adaptee's behavior - introduces only one object and no additional pointer indirection is needed. An object adapter - supports lots of single adapter work with many adaptee's, i.e. complete hierarchy. - makes it harder to override adaptee's behavior. - Other issues: - how much adaption does adapter do ? - plug-able adapter: interface adaption lets us incorporate our class into existing system that might expect different interface to the class i.e. we build of class(es) such that user might be able to plugin their classes / objects into our class(es). - 2 way adapters: an adapted *object* no longer confirms to an adaptee's interface, so it cannot be used as is wherever an adaptee's object can. 2 way adapters can provide this transparency. Specifically, they are useful when 2 different clients need to view an object differently. Usually implemented through multiple inheritance. - In C++, adapter derives publicly from the target and privately from the adaptee. If using object adapter, derive publicly from the target and contain the adaptee. ** Structural Pattern: Bridge - De-couple an abstraction from its implementation so that both can be implemented independently. - Inheritance binds an implementation to the abstraction permanently, which makes it harder to modify, extend, reuse abstraction and implementation independently. - Applicability: Use bridge pattern when: - You want to avoid permanent binding between abstraction and its implementation. This might be the case when implementation has to be selected / switched at runtime. - Both abstraction and implementation should be extensible via sub-classing. The bridge pattern lets you combine different abstraction and implementation. - changes in implementation of an abstraction should have no effect on the client, i.e. no re-compiling. - (C++ only) You want to hide the implementation of an abstraction completely from the client. - You want to share an implementation among multiple objects, and this fact should be hidden from the client. - Consequences - Implementation is not bound permanently to its interface. - Improved extensibility - Hiding implementation details from the client. ** Structural Pattern: Composite - Compose objects into tree structures (class / object hierarchies) to represent part-whole hierarchies. Composite pattern lets clients treat individual objects and composition of objects uniformly. - Applicability: Use composite pattern when: - You want to represent part-whole hierarchies. - You want the clients to be able to ignore the differences between compositions of objects and individual objects. - Consequences - recursively defines class hierarchies consisting of primitive and composite objects. - makes clients simple: clients can treat all types of objects in a similar manner - makes it easier to add new types of components - can make your design overly general ** Structural Pattern: Decorator - Attach additional responsibilities to an object dynamically. - Provides a flexible alternative to subclassing. - Useful for adding responsibilities to objects rather than class (all objects) - Achieved by enclosing the object in a decorator. - Decorators can be nested recursively. - Applicability: Use decorator pattern: - add responsibilities to individual objects dynamically and transparently. - for responsibilities that can be withdrawn. - when extension by sub classing is impractical. - A decorator is itself a component and also contains a component to act upon. - Consequences: - more flexibility than static inheritance. - avoid feature laden classes high up in the hierarchy - a decorator and its component aren't identical - lots of little objects: can be heard to learn, debug, maintain ** Structural Pattern: Facade - Provides a unified interface to a set of interfaces in a sub system. - Facade defines a higher level interface that makes the sub system easier to use. - Applicability: Use facade pattern when: - you want to provide a simple interface to a complex sub system - you need to de-couple the sub system from clients and other sub systems, promoting sub system's independence and portability. - you want to layer your sub systems - Consequences - Shields clients from sub system components, there by reducing the number of objects the client deals with, making the sub system easier to use. - promotes weak coupling between sub system and client: you can vary components without affecting its client. - simplifies porting sub systems to other platforms ** Structural Pattern: Flyweight - Use sharing to support large number of fine grained objects efficiently - Used to reduce the total number of objects in a system - A flyweight is a shared object that be used in multiple contexts simultaneously. The flyweight acts as an independent object in each context. It is indistinguishable from an object that is not shared. - Flyweight cannot make assumptions about the context in which they operate - The key concept here is the distinction between intrinsic and extrinsic state. Intrinsic state is stored in the flyweight and is independent of the flyweight's context, thereby making it shareable. Extrinsic state depends on and varies with the flyweight's context and therefore can't be shared. Client objects are responsible for passing the extrinsic state to the flyweight as an when needed. - Intrinsic state is stored in the flyweight, extrinsic state is stored or computed by client objects and is passed to the flyweight as and when needed. (i.e. when a client invokes a flyweight operation needing the extrinsic state). - The flyweight interface enables sharing, not enforces it: A flyweight subclass need not be shareable. - The flyweight pattern is often combined with composite pattern to represent a hierarchic structure with a graph, with shared leaf nodes. - Applicability: Use flyweight pattern when all the following are true: - an application uses a large no. of objects - storage costs are high because of the sheer number of objects - most object state can be made extrinsic - many groups of objects can be replaced with relatively few shared objects once extrinsic state is removed. - the application doesn't need to depend upon object identity: Since flyweight objects can be shared, identity tests will return true for conceptually distinct objects. - Consequences: - Trade off between speed and storage. - Storage saving are a function of several factors: - The reduction in total number of objects that come from sharing - The amount of intrinsic state per object - whether extrinsic state is computed or stored. ** Structured pattern: Proxy (also known as Surrogate) - Provide a surrogate or place holder for another object to control access to it. - One reason for controlling access to an object is to defer full cost of its creation and its initialization until we actually need to use it: create expensive objects on demand. - The proxy and the "real subject" derive from the same class - Copy on write: copying a large or complicated object can be an expensive operation. If the copy is never modified, there is no need to incur this cost. By using a proxy to postpone the copying process, we ensure that we pay the price of copying only if it is modified. To make this work, the subject must be reference counted. - Overloading "->" operation in C++, can be used in the proxy object to dynamically create the target. - Applicability: A proxy is applicable whenever there is a need for more versatile or sophisticated reference to an object than a simple pointer. - A remote proxy provides a local representation for an object in a different address space - A virtual proxy creates expensive objects on demand - A protection proxy controls access to the original object. Useful when objects should have different access rights. - A smart reference is a replacement for a bare pointer that performs additional actions when an object is accessed. ** Behavioral Pattern: 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. - Implemented by inheritance: message keeps getting passed to parent until it is handled. Can also be implemented by composition. - Applicability: Use ~ pattern when: - more than one object may handle a request and the handler is located dynamically - u want to issue a request to several objects without specifying the receiver explicitly. - the set of objects that can handle a request should be specified dynamically. - Consequences: - reduced coupling - added flexibility in assigning responsibility to objects - receipt isn't guaranteed: the request can fall off the end of the chain, without ever being handled ** Behavioral Pattern: Command (also known as Action, Transaction) - Encapsulate the request as an object, thereby letting, you parameterize clients with different requests, queues or log requests and support within "undo"able operation. - Applicability: Use ~ pattern when you want to: - parameterize object by an action to perform - specify queue and execute requests at different times. - support undo - support logging changes so that they can be reapplied in case of a system crash - structure a system around high level operation built on primary operation - Consequences: - Command de-couples the object that invokes the operation from the one that knows how to perform it. - Commands are first class objects - Commands can be assembled into a composite command - It is easy to add new commands as you don't have to change existing classes ** Behavioral Pattern: Interpreter - Given a language, define a representation for its grammar, along with an interpreter that uses the representation to interpret sentences in a language - Applicability: Use ~ pattern when there is a language to interpret and you can represent statement in a language as abstract syntax trees. The interpreter pattern works best when: - the grammar is simple. For complex grammar, the class hierarchy for the grammar becomes large and unmanageable. Tools, such as parser generation, are better in such cases. - efficiency is not a critical concern - Consequences: - It is easy to change and extend the grammar - Implementing the grammar is easy too - Complex grammar is hard to maintain - Easier to evaluate an expression in a new way ** Behavioral Pattern: Iterator (Also known as cursor) - Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation. - The object being traversed has to have the knowledge of which iterator would be best suited for itself. Hence the method to create an iterator for an object is contained in the object itself. The factory pattern could be used for such an implementation. - Applicability: Use iterator pattern: - to access an aggregate object's contents without exposing its internal representation - to support multiple traversal of aggregate objects - to provide a uniform interface for traversing different aggregate structures (polymorphic iteration) - Consequences: - Support variation in traversal of an aggregate - iterator simplifies the aggregate interface - more than one traversal can be pending on an aggregate ** Behavioral Pattern: Mediator - Define an object that encapsulates how a set of object interact - ~ promotes loose coupling by keeping objects from referring each other explicitly, and it lets you vary their interaction independently. - ~ serves as an intermediary and reduces the number of interconnection between objects - The behavior is located in one class. It can be changed or replaced by extending or replacing that class - Facade differs from ~ in that it abstracts a sub system of objects to provide a more convenient interface. Its protocol is unidirectional. - Applicability: Use ~ when: - a set of objects communicate in well defined but complex ways. The resulting independencies are unstructured and difficult to understand - reusing an object is difficult because it refers to and communicates with many other objects. - a behavior that is distributed between several classes should be customizable without a lot of sub-classing - Consequences: - reduced sub classing - de-couples colleagues - objects that interact - simplifies object protocol - abstracts how objects cooperate - centralizes control: increases complexities of mediator, which can become hard to maintain. ** Behavioral Pattern: Memento (also known as token) - Without violating encapsulation, capture & externalize an object's internal state, so that the object can restore to this state later. - A ~ is an object that stores a snapshot of the internal state of another object (the memento's originator) - The originator initializes the memento with information that characterizes its current state. - Only the originator can store and retrieve information from the memento: the memento is opaque to other objects - Applicability: use ~ when - a snapshot of (or some portion of) an object's state must be saved so that it can be restored to that state later, and - a direct interface to obtaining the state would expose implementation details and break the objects encapsulation. - Consequences: - Preserving encapsulation boundaries - simplifies originator - using ~ might be expensive: copy of large objects - defining narrow and wide interfaces - hidden costs of caring for mementos: caretaker objects ** Object Behavioral Pattern: Observer (Also known as dependents, 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. - By encapsulating complex update semantics, the change manager can act as mediator between subjects and observer. - Applicability: use ~ in any of the following situations - when an abstraction has 2 aspects: one dependent on the other. Encapsulating these objects into separate objects lets you vary and use them independently. - when a change to one object requires changing others and you don't know how many objects need to be changed. - when an object should be able to notify other objects without making assumption about who those objects are. In other words, you don't want these objects tightly coupled - Consequences: - Abstract coupling between subject and observer - support for broadcast communication - unexpected updates ** Object Behavioral Pattern: State (Also known as objects for state) - Allows an object to alter it behavior, when its internal state changes. The object will appear to change its class. - An object maintains a state variable that changes its instance (class) when the state of the object changes. - All requests are delegated to the state object. - The state object is itself responsible for changing its state, usually through call backs. - Applicability: Use ~ in either of the following cases: - An object's behaviors depends on its state and it must change its behavior at run-time depending on its state. - Operations have large multipart conditional statements, that depend on the object's state. - Consequences: - ~ localizes state specific behavior and partitions behavior from different states. - makes state transition explicit - state objects can be shared, if state objects have no instance variables: similar to flyweight ** Object Behavioral Pattern: Strategy (Also known as Policy) - Define a family of algorithms, encapsulate each one, and make them interchangeable. ~ lets the algorithm vary independently from the clients that use them. - Applicability: Use ~ when - many related classes differ only in behavior. Strategy provides a way to configure a class with one of many behaviors. - u need different variants of an algorithm - an algorithm uses data that a client should not know about. - a class defines many behaviors and these appear as multiple conditional statements in its operation. - Consequences: - Families of related algorithms: inheritance can help factor out common functionalities of the algorithms. - An alternate to subclassing - Strategies eliminate conditional statements. - Choice of implementation: can provide different implementation of the same behavior. - clients must be aware of different strategies - communication overhead between strategy and context - Increased number of objects ** Class Behavioral Pattern: Template Method - Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template method lets subclasses re-define certain steps of an algorithm without changing the algorithm's structure. - ~ is a fundamental technique for code re-use. - Control is in the parent class. Parent class calls the operations of a subclass, not the other way around. - Applicability: Use ~, - to implement parts of an algorithm once and leave it upto subclasses to implement the behavior that can vary. - when common behavior among subclasses should be factored and localized in a common class to avoid code duplication. - to control sub class extensions. You can define a template method that calls "hook" operation at specific points and there by permitting extensions only to those points. - Consequences: template methods call the following kinds of operations: - concrete operations: either on concrete class or on subclasses - concrete abstract class operations - primitive operations (abstract operations) - factory methods - hook operations ** Object Behavioral Pattern: Visitor - Represents an operation to be performed on the elements of an object structure. - ~ lets you define a new operation without changing the classes of the elements on which it operates. - With ~, you define 2 class hierarchies: one for the elements being operated and one for the visitors that define operations on these elements. You can create a new operation by adding a new subclass to the visitor class hierarchy. - Visitor will need to define a "visit" operation for each class of element in the object structure. Visitor will invoke a method that will call back the corresponding visit operation for itself. - Applicability: Use ~: - when, an object structure contains many classes of objects with differing interfaces and you want to perform operations on these objects that depend on their concrete classes. - when, many distinct and unrelated operations need to be performed on objects in an object structure, and u want to avoid polluting their classes with these operations. - when, the classes defining the object structure rarely changes, but you often want to define new operations over the structure. Changing the object structure classes require redefining the interface to all visitors, which is potentially costly. - to do interpretation. - Consequences: - Visitors make adding new operation easy. - A visitor gathers related operation and separates unrelated ones. - Adding new concrete element subclass is hard: requires modification to a large number of visitors. - Visitors can visit across class hierarchies vs iterator that work with only one type of class. - visitors can accumulate state - Breaking encapsulation: Visitors often force you to provide public operations that access element's internal state, which may compromise its encapsulation. ** TODO - Read Chapter 2 again after completing book - Reread Structure and participants for each pattern - Give the author feedback: Prototype pattern.