Patterns in Software Development: Architectural Patterns, Design Patterns, and Idioms
Patterns in Software Development: Architectural Patterns, Design Patterns, and Idioms
Architectural Patterns,
Design Patterns,
and Idioms
Introduction
Intent
– Define an interface for creating an object, but let subclasses decide which
class to instantiate. Factory Method lets a class defer instantiation to
subclasses
Problem
– Frameworks use abstract classes to define and maintain relationships
between objects. A framework is also often responsible for creating these
objects
– Sometimes it is impossible at compile-time to determine which objects
need instantiated. The framework knows when an object is needed, but
not what kind
Applicability
– You have a certain family of objects that are all inter-related, but each
entity in the family performs a different task
– You cannot determine at compile-time which object to instantiate, and the
object that’s to be created may change at run-time
Factory Method
Participants
– Product
Defines the interface of objects the factory method creates
– ConcreteProduct
Implements the product interface, defines a concrete type
– Creator
Declares the factory method, which returns an object of type Product
– ConcreteCreator
Provides a concrete factory method that returns an instance of a
ConcreteProduct
Factory Method
Motivating Example
private:
std::map<Key, Creator> creators_;
};
Implementation
struct DocReaderCreator {
DocumentReader* operator ()();
};
class Factory<string, DocumentReader, DocReaderCreator*> factory;
Intent
– Compose objects into tree structures to represent part-whole hierarchies.
Composite lets clients treat individual objects and compositions of objects
uniformly
Problem
– You want to build more complex components out of simpler components,
combining them together at run-time to create complex objects
– However, you don’t want to treat the objects and their containers
differently; manipulation should be uniform across all related objects
Applicability
– Use when you want clients to be able to ignore the difference between
compositions of objects and individual objects. Clients will treat all objects
in the composite structure uniformly
Composite
Participants
– Component
Declares the interface for objects in the composition
Implements default behavior for the interface common to all classes,
as appropriate
– Leaf
Represents leaf objects in the composition
Defines primitives in the system
– Composite
Defines behavior for components having children
Stores children components
Composite
Composite
Consequences
– Defines class hierarchies consisting of primitives and compositions
uniformly; wherever a client expects a primitive object, it can also
take a composition
– Makes the client simple, as the client can treat composite
structures and single objects uniformly
– Makes it easy to add new kinds of components
– Disadvantage is that it can make your design overly general; it’s
harder to restrict the components of a composite, often have to
rely on run-time rather than compile-time checks to enforce
contraints
Motivating Example
struct Expression
{
virtual int evaluate() = 0;
};
struct IntLiteral {
public:
IntLiteral(int n);
int evaluate() {return val;}
};
Implementation
Intent
– Provide a means to access the elements of an aggregate type without
exposing its underlying implementation
Problem
– An aggregate object such as a list should give a means to iterate across all
elements in the list without exposing its internal structure
– But, you don’t want to clutter the list interface with messy details about
traversals, particularly if it precludes you from having multiple traversals
occurring at once
– You might also want to be able to swap the list container out with a
different container later on, but keep the code that uses the container the
same
Iterator
Applicability
– Use an iterator to access an aggregate object’s contents without
exposing its underlying implementation
– To support multiple traversals of a aggregate objects
– To decouple an aggregate object from the algorithms that act upon that
object
The last point is significant, and provides the motivation for our
example
Iterator
Participants
– Iterator
defines an interface for accessing and traversing elements
– ConcreteIterator
Particular iterator implementation
– Aggregate
defines an interface for creating an iterator object
– ConcreteAggregate
creates concrete iterators
Motivating Example
Intent
– Represent an operation to be performed on the elements of an object
structure. Visitor lets you define a new operation without changing the
classes of the elements on which it operates
Motivation
– Sub-classing allows us to easily add new class types, and these new
classes can define different behavior for a family of objects
– However, it is relatively hard to add new operations to a given set of
classes; the new operation must be added to each class in the hierarchy.
Distributing all of these new operations across all the classes also makes
the classes harder to understand and is frequently undersirable
Visitor
Applicability
Use visitor when:
– An object structure contains many classes of objects with differing
interfaces and you want to perform operations on these objects depending
on their concrete class types
– Many distinct, unrelated operations need to be performed on objects and
you want to avoid polluting the classes with these operations. Visitor lets
you keep related operations together
– The class hierarchy is relatively stable, that is, classes are rarely added or
removed from the existing class hierarchy, but you often want to add new
operations, Changing the hierarchy requires changing the interfaces to all
the visitors
Visitor
Participants
– Visitor
Declares a Visit method for each class in the object hierarchy that you
want to be visitable
– ConcreteVisitor
Defines each Visit method for a particular operation; a ConcreteVisitor
defines a single operation to be performed on the class hierarchy
– Element
Declares an Accept method that takes a visitor as an argument
– ConcreateElement
Implements the Accept method
– Object Structure
Can enumerate it’s elements, may provide a way to apply a visitor
over a range of elements, such as over a list
Visitor
Motivating Example
Intent
– Scope guard is a pattern that occurs in languages with deterministic
destruction designed to facilitate an undo operation, particularly in the case
of exceptions being thrown
Motivation
– Writing exception safe code is hard, particularly when you want to enforce
certain exception guarantees
– The usual exception handling techniques, namely try…catch doesn’t scale
particularly well, and clutters code with additional control flow statements
ScopeGuard
Example:
– Consider the following function
void CompoundOperation()
{
ComplexOperation1();
ComplexOperation2();
}
ScopeGuard’s Task
– Encapsulate an undo request in an object such that, should an exception
be thrown, the guard will perform the undo operation
void CompoundOperation()
{
ComplexOperation1();
ScopeGuard guard(&UndoOp1);
ComplexOperation2();
guard.dismiss();
}
This seems much nicer than our previous solution. We’ve added 2 lines of
code, yes, but they are less intrusive than the try catch blocks. What’s more,
this solution scales well, if we have 3 operations, we can simply add another
guard that performs the undo of the second operation.
Design Patterns
Questions?