Sei sulla pagina 1di 37

OO Pattern Examples Douglas C.

Schmidt
Case Studies Using Patterns
The following slides describe several case studies using C++ & patterns
Object-Oriented Design Case Studies with


to build highly extensible software


Patterns & C++  The examples include

1. Expression Tree
Douglas C. Schmidt { e.g., Adapter, Factory, Bridge
Professor Department of EECS 2. System Sort
{ e.g., Facade, Adapter, Iterator, Singleton, Factory Method, Strategy,
d.schmidt@vanderbilt.edu Vanderbilt University Bridge
www.dre.vanderbilt.edu/schmidt/ (615) 343-8197 3. Sort Veri er
{ e.g., Strategy, Factory Method, Facade, Iterator, Singleton

Vanderbilt University 1

OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt


Case Study: Expression Tree Evaluator Expression Tree Diagram
*
BINARY
 The following inheritance & dynamic binding example constructs NODES
expression trees
{ Expression trees consist of nodes containing operators & operands
 Operators have di erent precedence levels, di erent associativities,
_ +
& di erent arities, e.g.,
 Multiplication takes precedence over addition
UNARY
 The multiplication operator has two arguments, whereas unary

minus operator has only one


NODE
5 3 44
 Operands are integers, doubles, variables, etc.

 We'll just handle integers in this example . . .

INTEGER
NODES

Vanderbilt University 2 Vanderbilt University 3





op_
use_
tag_

num_

unary_

binary_

LAYOUT
MEMORY

Vanderbilt University
Vanderbilt University

OO Pattern Examples
OO Pattern Examples

Expression trees

 Generate code

Tree
Node
1
1|2

CLASS
RELATIONSHIPS
 Perform semantic analysis

Tree
Node
 Traverse & print the expression tree

Here's the memory layout of a struct Tree


 Return the \value" of the expression tree

Node
 e.g., in-order, post-order, pre-order, level-order
Expression Tree Behavior

{ Trees may be \evaluated" via di erent traversals

object
Memory Layout of Algorithmic Version
{ The evaluation step may perform various operations, e.g.,

6
Douglas C. Schmidt
4
Douglas C. Schmidt

OO Pattern Examples Do OO Pattern Examples D


Print Tree Function Algorithmic Version
 A typical algorithmic implementation use a switch  A typical algorithmic method for implementing
statement & a recursive function to build & evaluate expression trees involves using a struct/union to
a tree, e.g., represent data structure, e.g.,
void print_tree (Tree_Node *root) {
typedef struct Tree_Node Tree_Node;
switch (root->tag_) {
struct Tree_Node {
case NUM: printf ("%d", root->num_);
enum { NUM, UNARY, BINARY } tag_;
break;
short use_; /* reference count */
case UNARY:
union {
printf ("(%s", root->op_[0]);
char op_[2];
print_tree (root->unary_);
int num_;
printf (")"); break;
} o;
case BINARY:
#define num_ o.num_
printf ("(");
#define op_ o.op_
print_tree (root->binary_.l_);
union {
printf ("%s", root->op_[0]);
Tree_Node *unary_;
print_tree (root->binary_.r_);
struct { Tree_Node *l_, *r_; } binary_;
printf (")"); break;
} c;
default:
#define unary_ c.unary_
printf ("error, unknown type\n");
#define binary_ c.binary_
}
};
}

Vanderbilt University Vanderbilt University


OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt
Limitations with Algorithmic Approach More Limitations with Algorithmic Approach
 Problems or limitations with the typical algorithmic approach include  The program organization makes it dicult to extend, e.g.,
{ Little or no use of encapsulation { Any small changes will ripple through the entire design &
implementation
 Incomplete modeling of the application domain, which results in
 e.g., see the \ternary" extension below

1. Tight coupling between nodes & edges in union representation { Easy to make mistakes switching on type tags . . .
2. Complexity being in algorithms rather than the data structures Solution wastes space by making worst-case assumptions wrt structs &
{ e.g., switch statements are used to select between various types of


unions
nodes in the expression trees
{ Compare with binary search! { This is not essential, but typically occurs
3. Data structures are \passive" & functions do most processing work { Note that this problem becomes worse the bigger the size of the
explicitly largest item becomes!

Vanderbilt University 8 Vanderbilt University 9

OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt


OO Alternative Expression Tree Diagram
Contrast previous algorithmic approach with an object-oriented
*

BINARY

decomposition for the same problem: NODES

{ Start with OO modeling of the \expression tree" application domain,


e.g., go back to original picture
_ +
{ Discover several classes involved:
 class Node: base class that describes expression tree vertices:

 class Int Node: used for implicitly converting int to Tree node
UNARY

 class Unary Node: handles unary operators, e.g., -10, +10, !a


NODE
5 3 44
 class Binary Node: handles binary operators, e.g., a + b, 10 - 30

 class Tree: \glue" code that describes expression-tree edges, i.e.,

relations between Nodes INTEGER


{ Note, these classes model entities in the application domain NODES
 i.e., nodes & edges (vertices & arcs)

Vanderbilt University 10 Vanderbilt University 11


OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt
Relationships Between Tree & Node Classes Design Patterns in the Expression Tree Program
Binary Unary Int  Factory
Node Node Node
{ Centralize the assembly of resources necessary to create an object
1 1  e.g., decouple Node subclass initialization from use

 Bridge
{ Decouple an abstraction from its implementation so that the two can
vary independently
Ternary Node  e.g., printing contents of a subtree and managing memory
Node 1
2  Adapter
-a
1 has
3 Tree { Convert the interface of a class into another interface clients expect
 e.g., make Tree conform C++ iostreams

Vanderbilt University 12 Vanderbilt University 13

OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt


C++ Node Interface C++ Tree Interface
class Tree; // Forward declaration #include "Node.h"
// Bridge class that describes the Tree edges and
// Describes the Tree vertices // acts as a Factory.
class Node { class Tree {
friend class Tree; public:
protected: // Only visible to derived classes // Factory operations
Node (): use_ (1) {} Tree (int);
Tree (const string &, Tree &);
/* pure */ virtual void print (std::ostream &) const = 0; Tree (const string &, Tree &, Tree &);
Tree (const Tree &t);
// Important to make destructor virtual! void operator= (const Tree &t);
virtual ~Node (); ~Tree ();
private: void print (std::ostream &) const;
int use_; // Reference counter. private:
}; Node *node_; // pointer to a rooted subtree

Vanderbilt University 14 Vanderbilt University 15


OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt
C++ Int Node Interface C++ Unary Node Interface
#include "Node.h" #include "Node.h"

class Int_Node : public Node { class Unary_Node : public Node {


public: public:
Int_Node (int k); Unary_Node (const string &op, const Tree &t);
virtual void print (std::ostream &stream) const; virtual void print (std::ostream &stream) const;
private: private:
int num_; // operand value. string operation_;
}; Tree operand_;
};

Vanderbilt University 16 Vanderbilt University 17

OO Pattern Examples Douglas C. Schmidt Do


C++ Binary Node Interface
#include "Node.h"
op

tag
op

tag
PART))
PART))
PART))

left
right

left_

PART
middle
right_

Node
PART
Node
operator
middle_

class Binary_Node : public Node {


operator_
(TreePART
(TreePART
(TreePART

(Tree
(Tree
(Tree

public:
Binary_Node (const string &op,
Node

PART))
PART))
Ternary

left
right

left_

PART
Node
PART
right_
Node

Node
operator
Binary

const Tree &t1,


(TreePART

operator_
(TreePART

(Tree
(Tree

const Tree &t2);


virtual void print (std::ostream &s) const;
PART))

PART
Node
PART
operand

Node
operator

private:
operand_

operator_
(TreePART
(Tree
Unary Node

const string operation_;


Tree left_;
ptr
use

vptr
num

vptr
use_
PART

Tree
num_

Node

node_
PART

Node
Node

Tree right_;
Int_Node

};
Memory Layout for C++ Version
Memory layouts for di erent subclasses of Node


OO Pattern Examples
Vanderbilt University

Vanderbilt University 18
OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt
C++ Int Node Implementations C++ Unary Node Implementations
#include "Int_Node.h" #include "Unary_Node.h"

Int_Node::Int_Node (int k): num_ (k) { } Unary_Node::Unary_Node (const string &op, const Tree &t1)
: operation_ (op), operand_ (t1) { }
void Int_Node::print (std::ostream &stream) const {
stream << this->num_; void Unary_Node::print (std::ostream &stream) const {
} stream << "(" << this->operation_ <<
<< this->operand_ // recursive call!
<< ")";
}

Vanderbilt University 20 Vanderbilt University 21

OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt


C++ Binary Node Implementation Initializing the Node Subclasses
#include "Binary_Node.h"
 Problem
Binary_Node::Binary_Node (const string &op, { How to ensure the Node subclasses are initialized properly
const Tree &t1,
 Forces
const Tree &t2):
operation_ (op), left_ (t1), right_ (t2) {} { There are di erent types of Node subclasses
 e.g., take di erent number & type of arguments
void Binary_Node::print (std::ostream &stream) const { { We want to centralize initialization in one place because it is likely to
stream << "(" << this->left_ // recursive call change . . .
<< " " << this->operation_
<< " " << this->right_ // recursive call  Solution
<< ")";
{ Use a Factory pattern to initialize the Node subclasses
}

Vanderbilt University 22 Vanderbilt University 23


OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt
The Factory Pattern Structure of the Factory Pattern
 Intent Factory
{ Centralize the assembly of resources necessary to create an object make_product()
 Decouple object creation from object use by localizing creation

knowledge
creates
 This pattern resolves the following forces:
{ Decouple initialization of the Node subclasses from their subsequent Product product = ...
return product
use
{ Makes it easier to change or add new Node subclasses later on
 e.g., Ternary nodes . . .
Product
 A generalization of the GoF Factory Method pattern

Vanderbilt University 24 Vanderbilt University 25

OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt


Using the Factory Pattern Printing Subtrees
 The Factory pattern is used by the Tree class to initialize Node subclasses:  Problem
{ How do we print subtrees without revealing their types?
Tree::Tree (int num)
: node_ (new Int_Node (num)) {}  Forces
Tree::Tree (const string &op, const Tree &t)
{ The Node subclass should be hidden within the Tree instances
: node_ (new Unary_Node (op, t)) {}
{ We don't want to become dependent on the use of Nodes, inheritance,
& dynamic binding, etc.
Tree::Tree (const string &op,
{ We don't want to expose dynamic memory management details to
const Tree &t1,
application developers
const Tree &t2)  Solution
: node_ (new Binary_Node (op, t1, t2)) {}
{ Use the Bridge pattern to shield the use of inheritance & dynamic
binding

Vanderbilt University 26 Vanderbilt University 27


OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt
The Bridge Pattern Structure of the Bridge Pattern
1: method_impl()
 Intent
Abstraction Implementor
{ Decouple an abstraction from its implementation so that the two can
vary independently method() method_impl()

 This pattern resolves the following forces that arise when building

extensible software with C++


1. How to provide a stable, uniform interface that is both closed & open, Concrete
i.e., ImplementorA
{ interface is closed to prevent direct code changes method_impl() Concrete
{ Implementation is open to allow extensibility ImplementorB
2. How to manage dynamic memory more transparently & robustly method_impl()
3. How to simplify the implementation of operator<<

Vanderbilt University 28 Vanderbilt University 29

OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt


Using the Bridge Pattern Illustrating the Bridge Pattern in C++
1: print()
 The Bridge pattern is used for printing expression trees:
Tree Node void Tree::print (std::ostream &os) const {
print() print() this->node_->print (os);
}

 Note how this pattern decouples the Tree interface for printing from the
Node subclass implementation
Int Node { i.e., the Tree interface is xed, whereas the Node implementation
Ternary
print() Binary Node varies
Node Unary print() { However, clients need not be concerned about the variation . . .
print() Node
print()

Vanderbilt University 30 Vanderbilt University 31


OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt
Integrating with C++ I/O Streams The Adapter Pattern
 Problem  Intent
{ Our Tree interface uses a print method, but most C++ programmers { Convert the interface of a class into another interface client expects
expect to use I/O Streams  Adapter lets classes work together that couldn't otherwise because

of incompatible interfaces
 Forces
 This pattern resolves the following force:
{ Want to integrate our existing C++ Tree class into the I/O Stream
paradigm without modifying our class or C++ I/O 1. How to transparently integrate the Tree with the C++ istd::ostream
operators
 Solution
{ Use the Adapter pattern to integrate Tree with I/O Streams

Vanderbilt University 32 Vanderbilt University 33

OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt


Structure of the Adapter Pattern Using the Adapter Pattern
1: request () 1: operator<<

Target client Target


client
request() operator<<

Adapter Adaptee iostream Tree


operator<< print()
request() 2: specific_request() specific_request() 2: print()

Vanderbilt University 34 Vanderbilt University 35


OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt
Using the Adapter Pattern C++ Tree Implementation
 Reference counting via the \counted body" idiom
 The Adapter pattern is used to integrate with C++ I/O Streams
Tree::Tree (const Tree &t): node_ (t.node_) {
std::ostream &operator<< (std::ostream &s, const Tree &tree) {
++this->node_->use_; // Sharing, ref-counting.
tree.print (s);
}
// This triggers Node * virtual call via
// tree.node_->print (s), which is
void Tree::operator= (const Tree &t) {
// implemented as the following:
if (this == &t) return;
// (*tree.node_->vptr[1]) (tree.node_, s);
// order important here!
return s;
++t.node_->use_;
}
--this->node_->use_;
 Note how the C++ code shown above uses I/O streams to \adapt" the if (this->node_->use_ == 0)
Tree interface . . . delete this->node_;
this->node_ = t.node_;
}

Vanderbilt University 36 Vanderbilt University 37

OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt


C++ Tree Implementation (cont'd) C++ Main Program
#include <istd::ostream.h>
Tree::~Tree () {
#include "Tree.h"
// Ref-counting, garbage collection
--this->node_->use_;
int main (int, char *[]) {
if (this->node_->use_<= 0)
const Tree t1 = Tree ("*", Tree ("-", 5),
delete this->node_;
Tree ("+", 3, 4));
}
cout << t1 << endl; // prints ((-5) * (3 + 4))
const Tree t2 = Tree ("*", t1, t1);

// prints (((-5) * (3 + 4)) * ((-5) * (3 + 4))).


cout << t2 << endl;

return 0;
// Destructors of t1 \& t2 recursively
} // delete entire tree when leaving scope.

Vanderbilt University 38 Vanderbilt University 39


Do

Do
Binary

Unary

Node
Node
Node

Int

Binary

Unary

Node
Node
Node

Int
Expression Tree Diagram 1

Expression Tree Diagram 2


Expression tree for t1 = ((-5) * (3 + 4))

4
4

Expression tree for t2 = (t1 * t1)


+
+

)
print(
)
print(

3
3

*
t2

*
t1
*
t1

5
OO Pattern Examples

OO Pattern Examples
Vanderbilt University

Vanderbilt University
-
5
-


OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt
Adding Ternary Nodes C++ Ternary Node Implementation
#include "Ternary_Node.h"
 Extending the existing program to support ternary nodes is Ternary_Node::Ternary_Node (const string &op,
straightforward const Tree &a,
{ i.e., just derive new class Ternary Node to handle ternary operators, const Tree &b,
e.g., a == b ? c : d, etc. const Tree &c)
: operation_ (op), left_ (a), middle_ (b),
#include "Node.h" right_ (c) {}
class Ternary_Node : public Node {
public: void Ternary_Node::print (std::ostream &stream) const {
Ternary_Node (const string &, const Tree &, stream << this->operation_ << "("
const Tree &, const Tree &); << this->left_ // recursive call
virtual void print (std::ostream &) const; << "," << this->middle_ // recursive call
private: << "," << this->right_ // recursive call
const string operation_; << ")";
Tree left_, middle_, right_; }; }
Vanderbilt University 42 Vanderbilt University 43
OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt
C++ Ternary Node Implementation (cont'd) Di erences from Algorithmic Implementation
// Modified class Tree Factory  On the other hand, modifying the original algorithmic approach requires
class Tree { changing (1) the original data structures, e.g.,
// add 1 class constructor
struct Tree_Node {
public:
enum {
Tree (const string &, const Tree &,
NUM, UNARY, BINARY, TERNARY
const Tree &, const Tree &)
} tag_; // same as before
: node_ (new Ternary_Node (op, l, m, r)) {}
union {
// Same as before . . .
// same as before. But, add this:
struct {
Tree_Node *l_, *m_, *r_;
} ternary_;
} c;
#define ternary_ c.ternary_
};

Vanderbilt University 44 Vanderbilt University 45

OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt


Di erences from Algorithmic Implementation (cont'd) Summary of Expression Tree Example
 & (2) many parts of the code, e.g.,  OO version represents a more complete modeling of the application
domain
void print_tree (Tree_Node *root) {
// same as before { e.g., splits data structures into modules that correspond to \objects"
case TERNARY: // must be TERNARY. & relations in expression trees
printf ("(");
 Use of C++ language features simpli es the design and facilitates
print_tree (root->ternary_.l_);
printf ("%c", root->op_[0]);
extensibility
print_tree (root->ternary_.m_); { e.g., implementation follows directly from design
printf ("%c", root->op_[1]);
 Use of patterns helps to motivate, justify, & generalize design choices
print_tree (root->ternary_.r_);
printf (")"); break;
// same as before
}

Vanderbilt University 46 Vanderbilt University 47


OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt
Potential Problems with OO Design Case Study: System Sort
 Solution is very \data structure rich"  Develop a general-purpose system sort
{ e.g., requires con guration management to handle many headers & { It sorts lines of text from standard input and writes the result to
.cpp les! standard output
{ e.g., the UNIX system sort
 May be somewhat less ecient than original algorithmic approach
 In the following, we'll examine the primary forces that shape the design
{ e.g., due to virtual function overhead of this application
 In general, however, virtual functions may be no less inecient than large  For each force, we'll examine patterns that resolve it
switch statements or if/else chains . . .
 As a rule, be careful of micro vs. macro optimizations
{ i.e., always pro le your code!

Vanderbilt University 48 Vanderbilt University 49

OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt


External Behavior of System Sort High-level Forces
 A \line" is a sequence of characters terminated by a newline  Solution should be both time & space ecient
 Default ordering is lexicographic by bytes in machine collating sequence { e.g., must use appropriate algorithms and data structures
(e.g., ASCII) { Ecient I/O & memory management are particularly important
{ Our solution uses minimal dynamic binding (to avoid unnecessary
 The ordering is a ected globally by the following options: overhead)
{ Ignore case (-f)  Solution should leverage reusable components
{ Sort numerically (-n)
{ Sort in reverse (-r) { e.g., istd::ostreams, Array & Stack classes, etc.
{ Begin sorting at a speci ed eld (-k)
 Solution should yield reusable components
{ Begin sorting at a speci ed column (-c)
 Your program need not sort les larger than main memory { e.g., ecient input classes, generic sort routines, etc.

Vanderbilt University 50 Vanderbilt University 51


OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt
Top-level Algorithmic View of the Solution Top-level Algorithmic View of the Solution (cont'd)
 Note the use of existing C++ mechanisms like I/O streams  Avoid the grand mistake of using top-level algorithmic view to structure
the design . . .
// Reusable function:
// template <typename ARRAY> void sort (ARRAY &a); { Structure the design to resolve the forces!
{ Don't focus on algorithms or data, but instead look at the problem,
int main (int argc, char *argv[]) its participants, & their interactions!
{
parse_args (argc, argv);
Input input;

cin >> input;


sort (input);
cout << input;
}

Vanderbilt University 52 Vanderbilt University 53

OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt


General OOD Solution Approach C++ Class Model
 Identify the classes in the application/problem space & solution space System
Options
{ e.g., stack, array, input class, options, access table, sorts, etc. Sort
GLOBAL
Sort_AT
 Recognize & apply common design patterns Adapter

STRATEGIC
Sort
{ e.g., Singleton, Factory, Adapter, Iterator COMPONENTS
Line_Ptrs
Sort_AT
 Implement a framework to coordinate components Adapter
ARRAY

{ e.g., use C++ classes & parameterized types Sort


TYPE
TACTICAL Access
COMPONENTS Table
TYPE TYPE
Input
Stack Array

Vanderbilt University 54 Vanderbilt University 55


OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt
C++ Class Components C++ Class Components
 Tactical components  Strategic components
{ Stack { System Sort
 Used by non-recursive quick sort  Facade that integrates everything . . .

{ Array { Sort AT Adapter


 Stores/sorts pointers to lines & elds  Integrates Array & Access Table

{ Access Table { Options


 Used to store input  Manages globally visible options

{ Input { Sort
 Eciently reads arbitrary sized input using only 1 dynamic allocation  e.g., both quicksort & insertion sort

& 1 copy

Vanderbilt University 56 Vanderbilt University 57

OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt


Detailed Format for Solution Reading Input Eciently
 Note the separation of concerns  Problem
// Prototypes { The input to the system sort can be arbitrarily large (e.g., up to 1/2
template <typename ARRAY> void sort (ARRAY &a); size of main memory)
void operator>> (std::istream &, Sort_AT_Adapter &);
 Forces
void operator<< (std::ostream &, const Sort_AT_Adapter &);
{ To improve performance solution must minimize:
int main (int argc, char *argv[]) 1. Data copying & data manipulation
{ 2. Dynamic memory allocation
Options::instance ()->parse_args (argc, argv);
 Solution
cin >> System_Sort::instance ()->access_table ();
sort (System_Sort::instance ()->access_table ()); { Create an Input class that reads arbitrary input eciently
cout << System_Sort::instance ()->access_table ();
}

Vanderbilt University 58 Vanderbilt University 59


OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt
Access Table Format The Input Class
 Eciently reads arbitrary-sized input using only 1 dynamic allocation
ACCESS BUFFER
class Input {
public:
// Reads from <input> up to <terminator>, replacing <search>
// with <replace>. Returns dynamically allocated buffer.
char *read (std::istream &input, int terminator = EOF,
ACCESS ARRAY

int search = '\n', int replace = '\0');


// Number of bytes replaced.
size_t replaced () const;
// Size of buffer.
size_t size () const;
private:
// Recursive helper method.
char *recursive_read ();
// . . .
};

Vanderbilt University 60 Vanderbilt University 61

OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt


The Input Class (cont'd) Design Patterns in the System Sort
char *Input::read (std::istream &i, int t, int s, int r)
{  Facade
// Initialize all the data members...
return recursive_read (); { Provide a uni ed interface to a set of interfaces in a subsystem
}
 Facade de nes a higher-level interface that makes the subsystem

char *Input::recursive_read () { easier to use


char buffer[BUFSIZ]; { e.g., sort() function provides a facade for the complex internal details
// 1. Read input one character at a time, performing
// search/replace until EOF is reached or buffer
of ecient sorting
// is full.
// 1.a If buffer is full, invoke recursive_read()
 Adapter
// recursively.
// 1.b If EOF is reached, dynamically allocate chunk
{ Convert the interface of a class into another interface clients expect
// large enough to hold entire input  Adapter lets classes work together that couldn't otherwise because

// 2. On way out of recursion, copy buffer into chunk of incompatible interfaces


} { e.g., make Access Table conform to interfaces expected by sort &
istd::ostreams

Vanderbilt University 62 Vanderbilt University 63


OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt
Design Patterns in System Sort (cont'd) Design Patterns in System Sort (cont'd)
 Factory
 Singleton
{ Centralize assembly of resources needed to create objects
{ e.g., decouple initialization of Line Ptrs used by Access Table from { Ensure a class has only one instance, & provide a global point of
their subsequent use access to it
{ e.g., provides a single point of access for the system sort facade & for
 Bridge program options
{ Decouple an abstraction from its implementation so that the two can
 Iterator
vary independently
{ e.g., comparing two lines to determine ordering { Provide a way to access the elements of an aggregate object
 Strategy sequentially without exposing its underlying representation
{ e.g., provides a way to print out the sorted lines without exposing
{ De ne a family of algorithms, encapsulate each one, & make them representation or initialization
interchangeable
{ e.g., allow exible pivot selection

Vanderbilt University 64 Vanderbilt University 65

OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt


Sort Algorithm Quicksort Optimizations
 For eciency, two types of sorting algorithms are used: 1. Non-recursive
1. Quicksort  Uses an explicit stack to reduce function call overhead

{ Highly time & space ecient sorting arbitrary data


{ O(n log n) average-case time complexity 2. Median of 3 pivot selection
{ O(n2) worst-case time complexity  Reduces probability of worse-case time complexity
{ O(log n) space complexity
{ Optimizations are used to avoid worst-case behavior 3. Guaranteed (log n) space complexity
2. Insertion sort  Always \pushes" larger partition
{ Highly time & space ecient for sorting \almost ordered" data
{ O(n2) average- & worst-case time complexity 4. Insertion sort for small partitions
{ O(1) space complexity  Insertion sort runs fast on almost sorted data

Vanderbilt University 66 Vanderbilt University 67


OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt
Selecting a Pivot Value The Strategy Pattern
 Problem  Intent
{ There are various algorithms for selecting a pivot value { De ne a family of algorithms, encapsulate each one, & make them
 e.g., randomization, median of three, etc. interchangeable
 Strategy lets the algorithm vary independently from clients that use
 Forces it
{ Di erent input may sort more eciently using di erent pivot  This pattern resolves the following forces
selection algorithms
1. How to extend the policies for selecting a pivot value without modifying
 Solution the main quicksort algorithm
{ Use the Strategy pattern to select the pivot selection algorithm 2. Provide a one size ts all interface without forcing a one size ts all
implementation

Vanderbilt University 68 Vanderbilt University 69

OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt


Structure of the Strategy Pattern Using the Strategy Pattern
STRATEGY
Context Strategy
quick_sort Pi vot
context_interface() algorithm_interface() Strategy
get_pivot()

Concrete Concrete pivot_strat->get_pivot (array, lo, hi)


Strategy A Strategy C
algorithm_interface() algorithm_interface()
Median
of
Concrete Select Three
Strategy B First Random
algorithm_interface()

Vanderbilt University 70 Vanderbilt University 71


OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt
Implementing the Strategy Pattern Implementing the Strategy Pattern
 ARRAY is the particular \context" template <typename ARRAY, class PIVOT_STRAT>
template <typename ARRAY> quick_sort (ARRAY &array,
void sort (ARRAY &array) { PIVOT_STRAT *pivot_strat) {
Pivot_Strategy<ARRAY> *pivot_strat = for (;;) {
Pivot_Factory<ARRAY>::make_pivot
typename ARRAY::TYPE pivot =
(Options::instance ()->pivot_strat ());
std::auto_ptr <Pivot_Strategy<ARRAY> > // Note 'lo' & 'hi' should be passed by reference
holder (pivot_strat); // so get_pivot() can reorder the values & update
// 'lo' & 'hi' accordingly...
// Ensure exception safety. pivot_strat->get_pivot (array, lo, hi);
ARRAY temp = array;
quick_sort (temp, pivot_strat);
// Partition array[lo, hi] relative to pivot . . .
// Destructor of <holder> deletes <pivot_strat>.
array = temp; }
} }

Vanderbilt University 72 Vanderbilt University 73

OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt


Fixed-size Stack Devising a Simple Sort Interface
 De nes a xed size stack for use with non-recursive quicksort  Problem
{ Although the implementation of the sort function is complex, the
template <typename T, size_t SIZE>
class Fixed_Stack
interface should be simple to use
{  Key forces
public:
{ Complex interface are hard to use, error prone, and discourage
bool push (const T &new_item);
extensibility & reuse
bool pop (T &item);
{ Conceptually, sorting only makes a few assumptions about the \array"
bool is_empty ();
it sorts
// . . .
 e.g., supports operator[] methods, size, & trait TYPE

{ We don't want to arbitrarily limit types of arrays we can sort


private:
T stack_[SIZE];  Solution
size_t top_; { Use the Facade & Adapter patterns to simplify the sort program
};

Vanderbilt University 74 Vanderbilt University 75


OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt
Facade Pattern Structure of the Facade Pattern
EXTERNALLY
 Intent VISIBLE
{ Provide a uni ed interface to a set of interfaces in a subsystem Facade
 Facade de nes a higher-level interface that makes the subsystem
HIDDEN
easier to use
 This pattern resolves the following forces:

1. Simpli es the sort interface


{ e.g., only need to support operator[] & size methods, & element
TYPE
2. Allows the implementation to be ecient and arbitrarily complex
without a ecting clients

Vanderbilt University 76 Vanderbilt University 77

OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt


Using the Facade Pattern Centralizing Option Processing
EXTERNALLY
VISIBLE
ARRAY  Problem
Sort { Command-line options must be global to many parts of the sort
program
HIDDEN
 Key forces
ARRAY
Quick { Unrestricted use of global variables increases system coupling & can
Sort violate encapsulation
{ Initialization of static objects in C++ can be problematic
TYPE

Stack ARRAY  Solution


Insert { Use the Singleton pattern to centralize option processing
Sort

Vanderbilt University 78 Vanderbilt University 79


OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt
Singleton Pattern Structure of the Singleton Pattern
 Intent
if (unique_instance_ == 0)
{ Ensure a class has only one instance, & provide a global point of unique_instance_ = new Singleton;
access to it return unique_instance_;
 This pattern resolves the following forces:

1. Localizes the creation & use of \global" variables to well-de ned


objects Singleton
2. Preserves encapsulation
3. Ensures initialization is done after program has started & only on rst static instance()
use singleton_operation()
4. Allow transparent subclassing of Singleton implementation get_singleton_data()
static unique_instance_
singleton_data_

Vanderbilt University 80 Vanderbilt University 81

OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt


Using the Singleton Pattern Options Class
 This manages globally visible options
if (unique_instance_ == 0)
unique_instance_ = new Options; class Options
return unique_instance_; {
public:
static Options *instance ();
bool parse_args (int argc, char *argv[]);
Options
// These options are stored in octal order
static instance() // so that we can use them as bitmasks!
bool enabled() enum Option { FOLD = 01, NUMERIC = 02,
field_offset() REVERSE = 04, NORMAL = 010 };
static unique_instance_ enum Pivot_Strategy { MEDIAN, RANDOM, FIRST };
options_

Vanderbilt University 82 Vanderbilt University 83


OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt
Options Class (cont'd) Options Class (cont'd)
#define SET_BIT(WORD, OPTION) (WORD |= OPTION)
bool enabled (Option o);
#define CLR_BIT(WORD, OPTION) (WORD &= ~OPTION)
int field_offset (); // Offset from BOL.
bool Options::parse_args (int argc, char *argv[]) {
Pivot_Strategy pivot_strat ();
for (int c;
int (*compare) (const char *l, const char *r);
(c = getopt (argc, argv, ``nrfs:k:c:t:'')) != EOF; ) {
switch (c) {
protected:
case 'n': {
Options (); // Ensure Singleton.
CLR_BIT (options_, Options::FOLD);
CLR_BIT (options_, Options::NORMAL);
u_long options_; // Maintains options bitmask . . .
SET_BIT (options_, Options::NUMERIC);
int field_offset_;
break;
static Options *instance_; // Singleton.
}
};
// . . .
}

Vanderbilt University 84 Vanderbilt University 85

OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt


Using the Options Class Simplifying Comparisons
 One way to implement sort() comparison operator:
 Problem
int Line_Ptrs::operator< (const Line_Ptrs &rhs) const {
Options *options = Options::instance (); { The comparison operator shown above is somewhat complex
 Forces
if (options->enabled (Options::NORMAL))
return strcmp (this->bof_, rhs.bof_) < 0;
{ It's better to determine the type of comparison operation during the
else if (options->enabled (Options::NUMERIC)); initialization phase
return numcmp (this->bof_, rhs.bof_) < 0; { But the interface shouldn't change
else // if (options->enabled (Options::FOLD))  Solution
return strcasecmp (this->bof_, rhs.bof_) < 0;
}
{ Use the Bridge pattern to separate interface from implementation

 We'll see another approach later on using Bridge

Vanderbilt University 86 Vanderbilt University 87


OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt
The Bridge Pattern Structure of the Bridge Pattern
 Intent
1: method_impl()
{ Decouple an abstraction from its implementation so that the two can
vary independently Abstraction Implementor
method_impl()
 This pattern resolves the following forces that arise when building method()
extensible software
1. How to provide a stable, uniform interface that is both closed & open,
i.e., Concrete
{ Closed to prevent direct code changes ImplementorA
{ Open to allow extensibility method_impl()
2. How to simplify the Line_Ptrs::operator< implementation & Concrete
reference counting for Access Table bu er ImplementorB
method_impl()

Vanderbilt University 88 Vanderbilt University 89

OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt


Using the Bridge Pattern Using the Bridge Pattern
 The following is the comparison operator used by sort
1: compare()
Line_Ptrs Options int Line_Ptrs::operator<(const Line_Ptrs &rhs) const {
return (*Options::instance ()->compare)
operator< compare() (bof_, rhs.bof_) < 0;
}

 This solution is much more concise


 However, there's an extra level of function call indirection . . .
{ Which is equivalent to a virtual function call
strcmp()
strcasecmp()
numcmp()

Vanderbilt University 90 Vanderbilt University 91


OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt
Initializing the Comparison Operator The Adapter Pattern
 Problem  Intent
{ How does the compare pointer-to-method get assigned? { Convert the interface of a class into another interface clients expect
 Adapter lets classes work together that couldn't otherwise because
int (*compare) (const char *left, const char *right);

 Forces of incompatible interfaces


 This pattern resolves the following forces:
{ There are many di erent choices for compare, depending on which
options are enabled 1. How to transparently integrate the Access Table with the sort
{ We only want to worry about initialization details in one place routine
{ Initialization details may change over time 2. How to transparently integrate the Access Table with the C++
{ We'd like to do as much work up front to reduce overhead later on istd::ostream operators
 Solution
{ Use a Factory pattern to initialize the comparison operator

Vanderbilt University 92 Vanderbilt University 93

OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt


Structure of the Adapter Pattern Using the Adapter Pattern

1: request () "conforms to"


ARRAY
ARRAY
client Target sort ARRAY::
ARRAY::TYPE
TYPE
request() operator[]
size()

1: ARRAY::TYPE t
= array[i]
"conforms to"
Adapter Adaptee
Line_Ptrs
request() 2: specific_request() specific_request()
Sort_AT_Adapter TYPE
typedef Line_Ptrs TYPE Access_Table
make_table()
operator[] make_table()
size() length()
element()

Vanderbilt University 94 Vanderbilt University 95


OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt
Dynamic Array The Access Table Class
 De nes a variable-sized array for use by the Access Table  Eciently maps indices onto elements in the data bu er
template <typename T> template <typename T>
class Array { class Access_Table {
public: public:
Array (size_t size = 0); // Factory Method for initializing Access_Table.
int init (size_t size); virtual int make_table (size_t lines, char *buffer) = 0;
T &operator[](size_t index); // Release buffer memory.
size_t size () const; virtual ~Access_Table ();
T *begin () const; // STL iterator methods. T &element (size_t index); // Reference to <indexth> element.
T *end () const; size_t length () const; // Length of the access_array.
// . . . Array<T> &array (void) const; // Return reference to array.
private: protected:
T *array_; Array<T> access_array_; // Access table is array of T.
size_t size_; Access_Table_Impl *access_table_impl_; // Ref counted buffer.
}; };

Vanderbilt University 96 Vanderbilt University 97

OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt


The Access Table Impl Class The Sort AT Adapter Class
class Access_Table_Impl { // Part of the Bridge pattern
public:  Adapts the Access Table to conform to the ARRAY interface expected by
Access_Table_Impl (void); //Default constructor sort
Access_Table_Impl (char *buffer); // Constructor
// Virtual destructor ensures subclasses are virtual struct Line_Ptrs {
virtual ~Access_Table_Impl (void); // Comparison operator used by sort().
int operator< (const Line_Ptrs &) const;
void add_ref (void); // Increment reference count
void remove_ref (void); // Decrement reference count
// Beginning of line & field/column.
char *get_buffer(void); // Get buffer from the class
void set_buffer(char *); // Set buffer
char *bol_, *bof_;
};
private:
char *buffer_; // Underlying buffer
size_t ref_count_; // Refcount tracks deletion.
};

Vanderbilt University 98 Vanderbilt University 99


OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt
The Sort AT Adapter Class The Factory Pattern
class Sort_AT_Adapter : // Note class form of the Adapter
private Access_Table<Line_Ptrs> {  Intent
{ Centralize the assembly of resources necessary to create an object
public:
virtual int make_table (size_t num_lines, char *buffer);
 Decouple object creation from object use by localizing creation

typedef Line_Ptrs TYPE; // Type trait. knowledge


// These methods adapt Access_Table methods . . .
 This pattern resolves the following forces:
Line_Ptrs &operator[] (size_t index); { Decouple initialization of the compare operator from its subsequent
};
size_t size () const;
use
{ Makes it easier to change comparison policies later on
 e.g., adding new command-line options
// Put these into separate file.
Line_Ptrs &Sort_AT_Adapter::operator[] (size_t i)
{ return element (i); }
size_t Sort_AT_Adapter::size () const { return length (); }

Vanderbilt University 100 Vanderbilt University 101

OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt


Structure of the Factory Pattern Using the Factory Pattern for Comparisons

Factory Options
make_product() parse_args()

creates
creates

Product product = ... initialize compare


return product

Compare
Product Function

Vanderbilt University 102 Vanderbilt University 103


OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt
Code for Using the Factory Pattern Code for Using the Factory Pattern (cont'd)
 The following initialization is done after command-line options are parsed  We need to write a numcmp() adapter function to conform to the API
used by the compare pointer-to-function
bool Options::parse_args (int argc, char *argv[])
{
int numcmp (const char *s1, const char * s2) {
// . . .
double d1 = strtod (s1, 0), d2 = strtod (s2, 0);
if (this->enabled (Options::NORMAL))
this->compare = &strcmp;
if (d1 < d2) return -1;
else if (this->enabled (Options::NUMERIC))
else if (d1 > d2) return 1;
this->compare = &numcmp;
else // if (d1 == d2)
else if (this->enabled (Options::FOLD))
return 0;
this->compare = &strcasecmp;
}
// . . .

Vanderbilt University 104 Vanderbilt University 105

OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt


Initializing the Access Table Factory Method Pattern
 Problem  Intent
{ One of the nastiest parts of the whole system sort program is initializing { De ne an interface for creating an object, but let subclasses decide
the Access Table which class to instantiate
 Factory Method lets a class defer instantiation to subclasses
 Key forces
 This pattern resolves the following forces:
{ We don't want initialization details to a ect subsequent processing
{ Makes it easier to change initialization policies later on { Decouple initialization of the Access Table from its subsequent use
 e.g., using the Access Table in non-sort applications { Improves subsequent performance by pre-caching beginning of each
eld & line
 Solution { Makes it easier to change initialization policies later on
{ Use the Factory Method pattern to initialize the Access Table  e.g., adding new command-line options

Vanderbilt University 106 Vanderbilt University 107


OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt
Structure of the Factory Method Pattern Using the Factory Method Pattern for Access Table
Initialization
Creator
TYPE
factory_method() = 0
make_product() Access Table
make_table() = 0
Product Product *product = factory_method()
return product

Line Ptrs
Concrete Sort AT
Creator Adapter
factory_method() make_table()

Concrete
CREATES
Product return new Concrete_Product // initialize the table

Vanderbilt University 108 Vanderbilt University 109

OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt


Using the Factory Method Pattern for the Implementing the Factory Method Pattern
Sort AT Adapter
 The Access Table Factory class has a Factory Method that initializes
 The following istd::ostream Adapter initializes the Sort AT Adapter Sort AT Adapter
access table // Factory Method initializes Access_Table.
int Sort_AT_Adapter::make_table (size_t num_lines,
void operator>> (std::istream &is, Sort_AT_Adapter &at)
char *buffer)
{
{
Input input;
// Array assignment op.
// Read entire stdin into buffer.
this->access_array_.resize (num_lines);
char *buffer = input.read (is);
this->buffer_ = buffer; // Obtain ownership.
size_t num_lines = input.replaced ();

size_t count = 0;
// Factory Method initializes Access_Table<>.
at.make_table (num_lines, buffer);
}

Vanderbilt University 110 Vanderbilt University 111


OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt
Implementing the Factory Method Pattern (cont'd) Initializing the Access Table with Input Bu er
// Iterate through the buffer & determine
 Problem
// where the beginning of lines & fields
// must go. { We'd like to initialize the Access Table without having to know the
for (Line_Ptrs_Iter iter (buffer, num_lines); input bu er is represented
iter.is_done () == 0;
iter.next ())  Key force
{
{ Representation details can often be decoupled from accessing each
Line_Ptrs line_ptr = iter.current_element ();
item in a container or collection
this->access_array_[count++] = line_ptr;
}  Solution
}
{ Use the Iterator pattern to scan through the bu er

Vanderbilt University 112 Vanderbilt University 113

OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt


Iterator Pattern Iterator Pattern (cont'd)
 Intent  The Iterator pattern provides a way to initialize the Access Table without
knowing how the bu er is represented
{ Provide a way to access the elements of an aggregate object
sequentially without exposing its underlying representation Line_Ptrs_Iter::Line_Ptrs_Iter (char *buffer,
 The C++ Standard Library (STL) is heavily based on the iterator pattern, size_t num_lines);
e.g.,
int main (int argc, char *argv[]) {
Line_Ptrs Line_Ptrs_Iter::current_element () {
std::vector <std::string> args; Line_Ptrs lp;
for (int i = 1; i < argc; ++i) {
args.push_back (std::string (argv [i])); // Determine beginning of next line \& next field . . .
} lp.bol_ = // . . .
for (std::vector<std::string>::iterator j = args.begin (); lp.bof_ = // . . .
j != args.end (); ++j)
cout << (*j)_ << endl;
return lp;
}
}

Vanderbilt University 114 Vanderbilt University 115


OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt
Iterator Pattern (cont'd) Summary of System Sort Case Study
 Iterator provides a way to print out sorted lines  This case study illustrates using OO techniques to structure a modular,
reusable, & highly ecient system
void operator<< (std::ostream &os, const Line_Ptrs lp) {
os << lp.bol_;  Design patterns help to resolve many key forces
}
 Performance of our system sort is comparable to existing UNIX system
void operator<< (std::ostream &os, const Sort_AT_Adapter &at) {
if (Options::instance ()->enabled (Options::REVERSE)) sort
std::reverse_copy (
at.array ().begin (), { Use of C++ features like parameterized types and inlining minimizes
at.array ().end (),
std::::ostream_iterator<System_Sort::Line_Ptrs> (os, "\n")); penalty from increased modularity, abstraction, & extensibility
else
std::copy (
at.array ().begin (),
at.array ().end (),
std::::ostream_iterator<System_Sort::Line_Ptrs> (os, "\n"));
}

Vanderbilt University 116 Vanderbilt University 117

OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt


Case Study: Sort Veri er General Form of Solution
 Verify whether a sort routine works correctly  The following is a general use-case for this routine:
{ i.e., output of the sort routine must be an ordered permutation of the template <typename ARRAY> void sort (ARRAY &a);
original input
 This is useful for checking our system sort routine! template <typename ARRAY> int
check_sort (const ARRAY &o, const ARRAY &p);
{ The solution is harder than it looks at rst glance . . .
int main (int argc, char *argv[])
 As before, we'll examine the key forces & discuss design patterns that
{
resolve the forces Options::instance ()->parse_args (argc, argv);

Input original;
Input potentially_sorted;

Vanderbilt University 118 Vanderbilt University 119


OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt
General Form of Solution (cont'd) Common Problems
unsorted 7 13 4 15 18 13 8 4
cin >> input;
sorted, but 0 0 0 0 0 0 0 0
not permuted
permuted, but 8 13 18 15 4 13 4 7
std::copy (original.begin (), not sorted
original.end (), sorted and 4 4 7 8 13 13 15 18
permuted
potentially_sorted.begin ());
sort (potentially_sorted);
 Several common problems:
if (check_sort (original, potentially_sorted) == -1)
{ Sort routine may zero out data
cerr << "sort failed" << endl;
 though it will appear sorted . . . ;-)
else
{ Sort routine may fail to sort data
cout << "sort worked" << endl;
{ Sort routine may erroneously add new values
}

Vanderbilt University 120 Vanderbilt University 121

OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt


Forces Forces (cont'd)
 Solution should be both time & space ecient  Multiple implementations will be necessary, depending on properties of
{ e.g., it should not take more time to check than to sort in the rst
the data being examined, e.g.,
place! 1. if data values are small (in relation to number of items) & integrals
{ Also, this routine may be run many times consecutively, which may use . . .
facilitate certain space optimizations 2. if data has no duplicate values use . . .
 We cannot assume the existence of a \correct" sorting algorithm . . . 3. if data has duplicate values use . . .
 This problem illustrates a simple example of \program families"
{ Therefore, to improve the chance that our solution is correct, it must
be simpler than writing a correct sorting routine { i.e., we want to reuse as much code and/or design across multiple
 Quis costodiet ipsos custodes? solutions as possible
 (Who shall guard the guardians?)

Vanderbilt University 122 Vanderbilt University 123


OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt
Strategies General OOD Solution Approach
 Implementations of search structure vary according to data, e.g.,  Identify the \objects" in the application & solution space
1. Range Vector { e.g., use a search structure ADT organization with member function
{ O(N) time complexity & space ecient for sorting \small" ranges such as insert & remove
of integral values
 Recognize common design patterns
2. Binary Search (version 1)
{ O(n log n) time complexity & space ecient but does not handle { e.g., Strategy & Factory Method
duplicates
 Implement a framework to coordinate multiple implementations
3. Binary Search (version 2)
{ O(n log n) time complexity, but handles duplicates { e.g., use classes, parameterized types, inheritance & dynamic binding
4. Hashing
{ O(n) best/average case, but O(n2) worst case, handles duplicates,
but potentially not as space ecient

Vanderbilt University 124 Vanderbilt University 125

OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt


General OOD solution approach (cont'd) High-level Algorithm
 C++ framework should be amenable to:  e.g., pseudo code
{ Extension & Contraction template <typename ARRAY>
 May discover better implementations int check_sort (const ARRAY &original,
 May need to conform to resource constraints const ARRAY &potential_sort)
 May need to work on multiple types of data {
{ Performance Enhancement Perform basic sanity check to see if the
 May discover better ways to allocate & cache memory potential_sort is actually in order
 Note, improvements should be transparent to existing code . . . (can also detect duplicates here)
{ Portability
 May need to run on multiple platforms

Vanderbilt University 126 Vanderbilt University 127


OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt
High-level Algorithm (cont'd) UML Class Diagram for C++ Solution
if (basic sanity check succeeds) then
Initialize search structure, srchstrct TYPE

for i < 0 to size - 1 loop


Search
insert (potential_sort[i])
Struct
into srchstrct
LONG TYPE
for i < 0 to size - 1 loop
if remove (original[i]) from Range Hash
srchstrct fails then Vector Table
return ERROR
TYPE
return SUCCESS TYPE
else Binary Binary
return ERROR Search Search
end if Nodups Dups
}

Vanderbilt University 128 Vanderbilt University 129

OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt


C++ Class Interfaces C++ Class interfaces (cont'd)
 Search structure base class.  Strategy Factory class
template <typename T> template <typename ARRAY>
class Search_Strategy Search_Struct
{ {
public: public:
virtual bool insert (const T &new_item) = 0; // Singleton method.
virtual bool remove (const T &existing_item) = 0; static Search_Struct<ARRAY> *instance ();
virtual ~Search_Strategy () = 0;
}; // Factory Method
virtual Search_Strategy<typename ARRAY::TYPE> *
make_strategy (const ARRAY &);
};

Vanderbilt University 130 Vanderbilt University 131


OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt
C++ Class interfaces (cont'd) C++ Class interfaces (cont'd)
template <typename ARRAY> class Binary_Search_Dups :
 Strategy subclasses public Search_Strategy<typename ARRAY::TYPE>
{
// Note the template specialization
typedef typename ARRAY::TYPE TYPE; /* . . . */
class Range_Vector :
};
public Search_Strategy<long>
{ typedef long TYPE; /* . . . */ };
template <typename T>
class Hash_Table :
template <typename ARRAY>
class Binary_Search_Nodups : public Search_Strategy<T>
{
public Search_Strategy<typename ARRAY::TYPE>
typedef typename ARRAY::TYPE TYPE; /* . . . */
{
};
typedef typename ARRAY::TYPE TYPE; /* . . . */
};
Vanderbilt University 132 Vanderbilt University 133

Do
OO Pattern Examples Douglas C. Schmidt
Design Patterns in Sort Veri er

This pattern extends the strategies for checking if


Factory Method

TYPE
Using the Strategy Pattern


Table
Hash

an array is sorted without modifying the


{ De ne an interface for creating an object, but let subclasses decide
which class to instantiate
 Factory Method lets a class defer instantiation to subclasses

TYPE
TYPE

Strategy
Search

Nodups
Struct

Binary
Search
 In addition, the Facade, Iterator, Singleton, & Strategy patterns are used

check sort algorithm


TYPE
Binary
Search
Dups
OO Pattern Examples

Vanderbilt University
check_sort

long

Vector
Range
Vanderbilt University 134


OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt
The Factory Method Pattern Structure of the Factory Method Pattern
 Intent
{ De ne an interface for creating an object, but let subclasses decide Creator
which class to instantiate factory_method() = 0

 Factory Method lets a class defer instantiation to subclasses


make_product()

 This pattern resolves the following force:


Product Product *product = factory_method()
return product

1. How to extend the initialization strategy in the sort veri er Concrete


transparently Creator
factory_method()

Concrete
CREATES
Product return new Concrete_Product

Vanderbilt University 136 Vanderbilt University 137

OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt


Using the Factory Method Pattern Implementing the check sort Function
 e.g., C++ code for the sort veri cation strategy
Search template <typename ARRAY> int
Strategy
make_strategy()
check_sort (const ARRAY &orig,
Search
const ARRAY &p_sort) {
Struct
if (orig.size () != p_sort.size ())
return -1;
New Search
Strategy
auto_ptr < Search_Strategy<typename ARRAY::TYPE> > ss =
make_strategy()
New Search Search_Struct<ARRAY>::instance ()->make_strategy
Struct CREATES (p_sort);
return new New_Search_Struct

Vanderbilt University 138 Vanderbilt University 139


OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt
Implementing the check sort Function (cont'd) Initializing the Search Structure
for (int i = 0; i < p_sort.size (); ++i)
 Factory Method
if (ss->insert (p_sort[i]) == false)
return -1; template <typename ARRAY>
Search_Strategy<typename ARRAY::TYPE> *
for (int i = 0; i < orig.size (); ++i) Search_Struct<ARRAY>::make_strategy
if (ss->remove (orig[i]) == false) (const ARRAY &potential_sort) {
return -1; int duplicates = 0;

return 0; for (size_t i = 1; i < potential_sort.size (); ++i)


// auto_ptr's destructor deletes the memory . . . if (potential_sort[i] < potential_sort[i - 1])
} return 0;
else if (potential_sort[i] == potential_sort[i - 1])
++duplicates;

Vanderbilt University 140 Vanderbilt University 141

OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt


Initializing the Search Structure (cont'd) Specializing the Search Structure for Range Vectors
if (typeid (potential_sort[0]) == typeid (long) template <Array<long> > Search_Strategy<long> *
&& range <= size) Search_Struct<Array<long> >::make_strategy
return new Range_Vector (potential_sort[0], (const Array<long> &potential_sort)
potential_sort[size - 1]) {
else if (duplicates == 0) int duplicates = 0;
return new Binary_Search_Nodups<ARRAY>
(potential_sort); for (size_t i = 1; i < size; ++i)
else if (size % 2) if (potential_sort[i] < potential_sort[i - 1])
return new Binary_Search_Dups<ARRAY> return 0;
(potential_sort, duplicates) else if (potential_sort[i] == potential_sort[i - 1])
else return new Hash_Table<typename ARRAY::TYPE> ++duplicates;
(size, &hash_function);
} long range = potential_sort[size - 1] -
potential_sort[0];

Vanderbilt University 142 Vanderbilt University 143


OO Pattern Examples Douglas C. Schmidt OO Pattern Examples Douglas C. Schmidt
Specializing the Search Structure for Range Vectors Summary of Sort Veri er Case Study
if (range <= size)
return new Range_Vector (potential_sort[0],  The sort veri er illustrates how to use OO techniques to structure a
potential_sort[size - 1]) modular, extensible, & ecient solution
else if (duplicates == 0) { The main processing algorithm is simpli ed
return new Binary_Search_Nodups<long> { The complexity is pushed into the strategy objects & the strategy
(potential_sort); selection factory
else if (size % 2) { Adding new solutions does not a ect existing code
return new Binary_Search_Dups<long> { The appropriate ADT search structure is selected at run-time based
(potential_sort, duplicates) on the Strategy pattern
else return new Hash_Table<long>
(size, &hash_function);
}

Vanderbilt University 144 Vanderbilt University 145

Potrebbero piacerti anche