How Do We Ensure This?
How Do We Ensure This?
Compatibility
• What do they mean?
• Suppose you are writing a web browser. Should the
browser be able to handle future versions of HTML?
– How do we ensure this?
• Suppose we are writing a new web browser. Should it be
able to read in old versions of HTML?
– How to ensure?
• Forward compatibility: The ability of an application to
accept future versions of input.
• Backward compatibility: The ability of an application to
accept previous versions of input.
ABI (Application Binary Interface)
vs. API
• Suppose you are designing and implementing
module that fits within an application.
– You design the interfaces carefully.
– In version 2, you change the implementation, but the
interfaces do not change.
• Does the rest of the application need to be modified? Does it
need to be recompiled?
• Can you make your changes such that the application does
not even need to be recompiled? Why does this matter? Isn’t
recompiling simple?
• Can you make your changes such that the application does
not even need to be relinked?
• Is Node v2 backwards binary compatible with
v1?
– struct Node_v1 {
double x;
int i;
int p1, p2, p3; // For extensibility.
};
struct Node_v2 {
double x;
int i;
double p1;
int p3; // For extensibility.
};
• How to achieve binary compatibility in C/C++/Fortran,
etc.?
– // A.hpp
class Aimpl;
class A {
public:
void method1();
private:
Aimpl *impl;
};
– // A.cpp
void A::method1() { impl->method1(); }
– // Aimpl.hpp
class Aimpl { … };
– // What does a client need to include?
#include “A.hpp”
#include “Aimpl.hpp” // Needed?
– When you modify the implementation by changing A.cpp is a
recompile needed? What about Aimpl.hpp, is a recompile
needed?
• Binary compatibility is important in these
situations:
– Large app. Want to ship bug fixes to
customers.
• With no binary compatibility, what happens? With
binary compatibility?
– Designing an OS. When you upgrade the OS,
what happens to apps?
– Libs and dlls. What happens if there is a bug
in the C library on Windows or Linux?
Design Patterns
Introduction
• Designing reusable object-oriented software is hard.
– Find the right objects/classes.
– Find the right factorization.
– Define the interfaces.
– Define the inheritance hierarchies.
• Solves the problem at hand
– But also general enough to address future problems and
requirements.
• Very hard to get right the first time.
– An iterative process.
• Design it, try reuse, redesign/refactor, try reuse again.
• Do not solve every problem starting from first
principles.
– When building a house, each house does not have to
be designed starting from each nail or screw.
– There are well-known “templates” or “patterns” for
building a house that can then be customized, like
kitchen plans, etc.
– “Each pattern describes a problem which occurs over
and over again in our environment, and then
describes the core of the solution to that problem, in
such a way that you can use this solution a million
times over, without ever doing it the same way twice.”
What is a Design Pattern
• Description of communicating objects and
classes that are then customized to solve
a general design problem in a particular
context.
– Pattern name
• Are names important?
– Problem: when to apply the pattern
– Solution: the elements that make up the
design
– Consequences: results and trade-offs
Organizing Design Patterns
• Purpose:
– Creational
• Dealing with object creation: Factory Method
– Structural
• Dealing with composition: Adapter
– Behavioral
• Dealing with behavior: Visitor
• Scope:
– Class
• Applies primarily to classes
– Object
• Applies primarily to objects
Interface vs. Class
• In common OOP usage:
– Interface: What the methods are named, the
parameters they take, return types, and the
semantics.
– Class: The implementation of the interface, the actual
data members, method implementation code, etc.
• In C++, there is greater ability to mix and match
the features of interfaces and classes, so
interface is somewhat implicit.
– Virtual functions and abstract base classes can be
used to make them more explicit.
– A class with just pure virtual functions and no data
members is essentially an interface.
• What is the interface of this class?
– struct A {
void f(int);
};
• Solution?
• You could push the code into the object:
– // Original version.
if (typeid(*a) == typeid(B1)) {
// Some code to do XYZ.
} else if (typeid(*a) == typeid(B2)) {
// Some code to do ABC.
}
– // Code moved into a virtual function in the
// object.
virtual void B1::doit() {
// Code to do XYZ.
}
virtual void B2::doit() {
// Code to do ABC.
}
…
a->doit(); // Outside of the object.
• Disadvantage?
– Intrusive, however.
• The problem is to efficiently map from the
type to the code that should be executed
for that type.
– struct Code {
virtual void operator()(A *) const = 0;
};
struct Code_B1 : public Code {
virtual void operator()(A *) const;
};
map<type_info *, Code, Cmp> type_map;
Code &code = type_map.lookup(&typeid(*a));
code(a);
• In order to efficiently store type_info in
a map, what operation do we need?
– The type_info object has before() method.
Class Diagrams
• Diagram the relationships between
classes.
Abstract class or
interace (Italicized)
Class name
Methods
Member variables
(Only if concrete)
Client Client
Participant Non-participant
Inheritance
Instantiation Acquaintance
• Class relationships
• Pseudo-code annotation
Object Diagrams
Object name
(aSomeclass)
Interaction Diagrams
• Describe how objects interact.
– How do you do this?
• Function F invokes function G, in two different call
sites.
• In the second invocation, G invokes F.
Object name
Not yet
Instantiation
created
Request name
Object
lifetime
Object
active
Example
• class A {
public:
A() : paren(0), a1(new A(this)),
a2(new A(this)) {}
void start() {
a1->foo();
a2->foo();
}
private:
A(A *p)
: paren(p), a1(0), a2(0) {}
void foo() { paren->goo(); }
void goo();
private:
A *a1, *a2, *paren;
};
Most Common Patterns
Abstract Factory
(Object Creational)
• Intent
– Provide an interface for creating families of related
objects without specifying concrete factories.
• Motivation
– Consider an application with a GUI that needs to run
on both Windows and Linux. You might need to
create a Window with a containing Scrollbar. You
want the Windows version to create Windows
interface objects, and the Linux version to create
Linux interface objects.
• Interface has create methods.
– Concrete derived class creates various concrete
classes.
• Example:
– struct MazeFactory {
virtual Maze *MakeMaze() const = 0;
virtual Wall *MakeWall() const = 0;
};
struct MagicMazeFactory : public MazeFactory {
virtual Maze *MakeMaze() const {
return new MagicMaze; }
virtual Wall *MakeWall() const {
return new MagicWall; }
};
struct BombedMazeFactory
: public MazeFactory { … };
Maze *BuildMaze(MazeFactory *) { … }
// Make a magic maze.
MazeFactory *mmf = new MagicMazeFactory;
Maze *m = BuildMaze(mmf);
// Make a bombed maze.
MazeFactory *bmf = new BombedMazeFactory;
Maze *m = BuildMaze(bmf);
Factory Method
(Class Creational)
• Intent
– Define an interface for creating an object, but
let subclasses decide which class to
instantiate.
• Motivation
– At the point at which an object is created, it is
unknown which actual implementation to use.
• Factory method subclasses the thing creating the
objects.
• Can have a factory method without an abstract factory.
• Suppose you have different kinds of String
implementations, optimized for different
cases:
– Short strings
– Strings that don’t change
– UTF-8 strings
– Etc.
• You want to completely isolate these
optimizations. The rest of the application
should not know that there are different
kinds of string types.
• So, can you put this in application code?
– // Dealing with Unicode, so create
Unicode optimized string.
String *str = new UnicodeString;
• Use a virtual constructor approach.
– class String {
public:
enum LengthType { FIXED, DYNAMIC };
enum Encoding { UTF-8, ASCII };
static String *MakeString(int len,
LengthType,
Encoding);
};
class UTFString : public String {… };
String *String::MakeString(…) {
// Decide which version to make…
if (…) {
return new UTFString(…);
} else if (…) {
return new ASCIIString(…);
} …
}
• Optimized implementations can now be
added completely independently, without
affecting the rest of the application at all.
• Application just says:
– String *s = String::MakeString(…);
Adapter
(Class/Object Structural)
• Intent
– Convert interface of a class into one that is
expected.
• Commonly called a wrapper.
• Can use inheritance or composition.
• Within a single class.
• As two separate objects (probably
preferred).
• Example: Adapt a TextView object to a Shape interface.
– struct Shape {
virtual void BoundingBox(Point &bottom_left,
Point &top_right) const;
};
– class TextView {
void GetOrigin(Coord &x, Coord &y) const;
void GetExtent(Coord &width,
Coord&height) const;
}
• Using inheritance
– class TextShape
: public Shape, private TextView {
BoundingBox(Point &bl, Point &tr) const {
GetOrigin(…);
GetExtent(…);
…
}
}
• Example: Adapt a TextView object to a Shape interface.
– struct Shape {
virtual void BoundingBox(Point &bottom_left,
Point &top_right) const;
};
– class TextView {
void GetOrigin(Coord &x, Coord &y) const;
void GetExtent(Coord &width,
Coord &height) const;
}
• Using composition
– class TextShape : public Shape {
public:
BoundingBox(Point &bl, Point &tr) const {
tv.GetOrigin(…);
tv.GetExtent(…);
…
}
private: TextView tv;
}
Composite
(Object Structural)
• Applicability
– Part-whole hierarchies
– Compositions of objects and individual objects
treated the same.
• Example
– An expression is:
• A number by itself
• A variable by itself
• An operator
• A composite of things has the same interface as
the contained things.
• Example:
– class Expression { … };
class Operator : public Expression {
…
private:
Expression *left, *right;
};
class Leaf : public Expression {
…
};
Decorator
(Object Structural)
• Intent
– Adding additional responsibilities/functionality
to an object dynamically.
– Alternative to subclassing.
– Differs from Adapter in that Adapter changes
the interface, while Decorator only modifies
the features, not the interface.
• Example:
– class VisualComponent {
public:
virtual void Draw() const;
};
class Decorator : public VisualComponent {
public:
Decorator(VisualComponent *);
virtual void Draw() const;
private:
VisualComponent *comp;
};
void Decorator::Draw() {
comp->Draw();
}
class Border : public Decorator { … };
void Border::Draw() {
Decorator::Draw();
// Do stuff to draw border.
}
• Discussion
– Can be dynamic, unlike subclassing.
– Avoids creating all possible combinations of
subclasses. For example, if there are two
possible decorations, D1, and D2, and two
possible elements, E1, and E2, you would
have to have ? different subclasses.
Observer
• Intent
– Define a dependency (subscription) relationship between
objects, so that when the observed object changes state, all
observers are notified and updated automatically.
– It is the View, in the MVC pattern.
• Motivation
– Suppose you have a set of objects which needs to be notified
when object A changes.
• Applicability
– When a change to one object requires changing others, and you
don’t know how many others need to be changed.
– When an object should be able to notify other objects, and you
don’t know the other objects ahead of time.
• Notify() is called by the subject itself when setstate() is called.
• Example:
– class Observer {
public:
virtual void Update();
};
class Subject {
public:
void Notify() {
for (it = list.begin();
it != list.end(); ++it) {
it->Update();
}
};
class MyObserver : public Observer {
void Update() {
subject->getState();
…
}
};
class MySubject : public Subject {
void SetState(…) {
…
Notify();
}
}
• Who should call Notify(), the subject itself, or the object changing the state?
• Suppose an observer itself need to set the state in Update.
• Suppose a derived class operation is composed of several base class
operations.
Strategy
(Object Behavioral)
• Intent: Define a family of algorithms, encapsulate each
one, make them interchangeable.
• Motivation: Many algorithms exist for breaking a stream
of text into lines.
– Do not want to put the code in the clients that need linebreaking.
– Want to support multiple algorithms.
– Want to support easily adding them.
• Applicability:
– You need variants of an algorithm.
– Many related classes differ only in their behavior.
– An algorithm uses data that clients shouldn’t know about.
– A class defines many different behaviors, and these show up as
multiple conditionals in the ops. Move branches into strategy
class.
• Strategy and context collaborate to
provide the behavior.
• Consequences:
– Alternative to subclassing.
• You could subclass the Context object.
– Eliminate conditional statements.
• void Composition::Repair() {
switch (line_breaking_strategy) {
case SIMPLE:
BreakWithSimple();
break;
case TEX:
BreakWithTex();
break;
…
}
• void Composition::Repair() {
compositor->BreakLines():
…
}
– A choice of implementations. Makes it easy to compare two
different ones.
• Implementation Issues:
– How do the Strategy and Context
communicate? What is passed?
– Use a template. What are the benefits?
• Example:
– class LineBreaker {
public:
virtual vector<string>
break(const vector<string> &) const;
};
class Paragraph {
public:
Paragraph(const LineBreaker *);
void addLine(const string &);
private:
const LineBreaker *const breaker;
};
void Paragraph::addLine(const string &l) {
// Do stuff to add the line.
lines = line_break->break(lines);
}
Template Method
(Class Behavioral)
• Intent
– Define the overall steps of an operation in a super-
class method, but deferring the exact implementation
of the steps to methods in derived classes.
• Motivation
– You have some code that is similar, but not identical
in multiple classes.
• Factor out the similarity into a template method.
• Use derived class methods (or function pointers) to handle
the similar parts.
• Suppose you have an application
framework with App and Doc objects.
Specific applications, such as a drawing
application or a spreadsheet application
will subclass these.
– One thing in common is the need to open a
document. The overall sequence of steps is
identical for all apps, but the specific details of
how each step is carried out varies.
– So, put the overall sequence in the base
class, and let derived classes define exactly
what to do.
• class App {
void OpenDoc(const char *name);
};
class MyApp {
void CanOpenDoc(const char *name);
Doc *DoCreateDoc(const char *name);
void AboutToOpenDoc(Doc *);
};
void App::OpenDoc(const char *name) {
if (!CanOpenDoc(name)) { return; }
Doc *d = DoCreateDoc(name);
if (d) {
AboutToOpenDoc(d);
d->Open();
d->DoRead();
}
}
void MyApp::CanOpenDoc(const char *name) {
// Do stuff here specific to this app, such as maybe
// check the format of the file to make sure it is of
// the right type.
// …
}
void MyApp::DoCreateDoc(const char *name) { … }
void MyApp::AboutToOpenDoc(Doc *) { … }
• Applicability:
– Implement the invariant parts of an algorithm
once.
• Sort, pick some good candidates using a metric,
run a computation with those candidates, then do
an update based on those results, but also
dependent on the metric.
– When common behavior should be factored
and localized to a superclass.
– Control and define subclass extensions.
• Example: A class called View that supports
drawing on the screen.
– Before drawing can begin, the focus must first be set.
Base class does that, then calls derived class.
– void View::Display() {
SetFocus();
DoDisplay();
ResetFocus();
}
void MyView::DoDisplay() {
// App-defined stuff.
}
– So user derives from base class.
• Tip: Use a naming convention to make clear
what should be implemented by derived class.
• Basic idea here is that you want to put common
code in the base class.
• Is there another way to do the same thing?
– class Base {
void op();
};
class Derived: public Base {
virtual void op() {
…
Base::op();
}
};
– Advantages, disadvantages?
Patterns Catalog
Related Factory Patterns
• Abstract Factory
– An object with factory methods.
• Factory Method
– A method within an object that is subclassed.
• Builder
– An object to which you feed a sequence of things to it,
then get the result in the end.
• Prototype
– Another way of creating objects via a clone() method.
Builder (Object Creational)
• Intent: Separate the construction of a complex object
from its representation so that the same construction
process can create different representations.
• Motivation: Suppose you have an object to read RTF.
You want it to be able to convert to multiple different
representation types, such as ASCII, or HTML.
Solutions?
– // In reader
while (t = get the next token) {
switch (t.type) {
case CHAR: builder->ConvertChar(t.char);
case FONT: builder->ConvertFont(t.font);
case PARA: builder->ConvertPara(t.para);
• Applicability:
– Algorithm for creating a complex object should
be independent of the parts that make up the
object.
– The construction process must allow for
different representations.
• Builder similar to AF.
– Builder focuses on creating a complex object step-by-step.
– AF focus is on families of product objects.
– Builder often returns product as final step. AF often returns
immediately.
– Where is the product stored?
Prototype
(Object Creational)
• Intent
– Specify the kinds of objects to create using a prototypical
instance, and create new objects by copying this protoype.
• Motivation
– Suppose you have a GUI element called a GraphicTool that
will let you create and place a graphic in a music editor. You
want to create and place a number of different ones, like
WholeNote and HalfNote.
– The GraphicTool needs to instantiate the graphic, such as
WholeNote or HalfNote. You could create one GraphicTool
for each graphic.
– Better is to initialize each GraphicTool with an instance of a
Graphic abstract class, that it then clones.
– Other solutions?
• Could use a factory of some kind.
• class Client {
void Op() {
p = prototype->Clone();
}
}
class MyProto1 : public Proto {
virtual MyProto1 *Clone() { … }
};
class MyProto2 : public Proto {
virtual MyProto2 *Clone() { … }
};
• Do we need to use a separate class for whole
notes and half notes?
• Applicability:
– When class to instantiate are specified at run-
time.
– To avoid building a hierarchy of factories that
parallel the products.
– When instances of a class can have only a
few combinations of state.
• Easier than instantiating manually, especially if
many parameters, but few combinations.
• Example
– class MazePrototypeFactory {
public:
MazePrototypeFactory(Maze*, Wall*, Room *);
virtual Maze *MakeMaze() const;
virtual Room *MakeRoom() const;
virtual Wall *MakeWall() const;
private:
Maze *protoMaze;
Room *protoRoom;
Wall *protoWall;
};
Wall *MazePrototypeFactory::MakeWall() const {
return protoWall->clone();
}
MazePrototypeFactory bombedMF(new Maze, new
BombedWall, new BombedRoom);
• Consequences
– Requires implementing clone(), so is intrusive.
– Does not require a whole set of parallel classes, as
using factories might.
• A factory for each type, like bombed wall, etc.
• The concept is like merging the factory with the object that
it creates.
– Supports user-templates. For example, say you
have a graphic that you want to put into a palette.
– Can be very dynamic, and have prototypes with
different values (but same class). (Factories can do
this to, but not quite as elegant.)
• // Five bombs per wall.
MazeProtoFactory mf5(new BombedWall(5));
// Ten bombs per wall.
MazeProtoFactory mf10(new BombedWall(10));
• Implentation:
– If lots of prototypes, and there is a need to
search for them based on attributes, use a
prototype manager/registry.
Discussion of Factory Patterns
• Two ways of parameterizing a system by the classes of
objects it creates:
– Subclass the class that creates the objects. (FactoryMethod)
– Define an object that is responsible for knowing what to create
(AbstractFactory, Builder, Prototype)
• Consider the GraphicTool.
– FactoryMethod would say subclass it, one to create HalfNote,
the other to create WholeNote.
– AbstractFactory would say pass in a factory object to create
HalfNotes or WholeNotes.
– Prototype would say pass in a prototype object that is cloned.
– Note that the line between these is somewhat blurry sometimes.
Traversing Containers
• Iterator
– Essentially an object that functions as a pointer,or
cursor. It has a current element that it points to, and
methods to advance to the next element.
• Visitor
– For data structures that are heterogeneous. Factor
out the traversal code, and as you traverse the
structure, make callbacks to a visitor object that is
carried along the traversal.
Iterator (Object Behavioral)
• Intent
– Provide a way to access the elements of an
aggregate object sequentially without exposing the
underlying implementation.
• Motivation
– How do you traverse a linked list? How did you learn
how to do it?
– A container like a linked list should have a way of
traversing the elements without exposing its structure.
• Suppose you have a variety of containers, such as a list and
binary tree. You want to do something to every thing in the
container using the same code.
• Or suppose you want to traverse it in different ways, like
forwards and backwards, or using different orderings.
– void foo(List *li) {
ListNode *ptr = li->head;
while (ptr != NULL) {
// Do something with it.
ptr = ptr->next;
}
}
– Suppose it was changed to an array? Or a balanced binary tree?
– Suppose this is traverse monsters. You decide that you want to
filter out invisible ones.
– void foo(Container *li) {
Iterator *it = il->getIterator(no_invisible);
while (it->more()) {
// Do something with it.
it->next();
}
}
– Key Idea: Take the responsibility for traversal and
access out of the container itself, and put it into an
iterator object. The iterator object functions as a
pointer, or cursor, or placeholder, that you can then
advance, etc.
• Using a single interface, different classes of iterator objects
can traverse in different directions, or different ways. For
example, you could have an OddIterator to skip all even
elements.
– class Iterator {
void next();
int get();
};
class OddIterator : public Iterator {…};
IntList l;
OddIterator it(l);
it.get();
it.next();
it.get();
– How does the iterator know how to advance to
the next element? Does it require
implementation knowledge about the
container.
• Yes, so should use a pattern.
– What class should be created? Should it be public?
– class SomeList;
Iterator it = sl.createIterator(); // What
is this pattern?
– SomeListIterator it(sl);
– How can this code be rewritten to make it
more flexible/maintainable/adaptable?
• void foo(List *) {
ListIterator lit(l);
for (; !lit.end(); lit.next()) {
// Do something with lit.get().
}
}
• void foo(Iterator *) {
for (; !lit.end(); lit.next()) {
// Do something with lit.get().
}
}
• Known as polymorphic iteration.
• Applicability: when to use?
– To access a container’s contents without
exposing the internal representation.
– Support different ways of traversing the same
container. For example, in-order or pre-order
traversal of a tree. Or backwards or forwards.
Or filtering out certain elements.
– Provide a uniform interface for traversing
different kinds of containers (polymorphic
iteration).
• Implementation issues:
– External iterators vs. internal iterators.
• Where is the code for traversal defined? It could be in the container, or it
could be in the iterator.
aSaveDialog
handler anApplication
aPrintButton
handler
handler aPrintDialog
handler
anOKButton
handler
aSaveDialog
handler anApplication
aPrintButton
handler
handler aPrintDialog
handler
anOKButton
handler