csci 490 / engr 596 special topics / special projects software design and scala programming spring...
TRANSCRIPT
Csci 490 / Engr 596Csci 490 / Engr 596Special Topics / Special ProjectsSpecial Topics / Special Projects
Software Design and Scala ProgrammingSoftware Design and Scala Programming
Spring Semester 2010Spring Semester 2010Lecture NotesLecture Notes
Designing with PatternsDesigning with Patterns
This is a set of slides to accompany chapter 2 of This is a set of slides to accompany chapter 2 of John Vlissides’s book John Vlissides’s book
Pattern Hatching : Design Patterns AppliedPattern Hatching : Design Patterns Applied(Addison Wesley, 1998)(Addison Wesley, 1998)
Created: 14 September 2004 Revised 20 April 2010
IntroductionIntroduction
Best way to learn to use design patterns is Best way to learn to use design patterns is to to justjust use them use them
Case study here is a Unix-like hierarchical Case study here is a Unix-like hierarchical file systemfile system
1
A Hierarchical File SystemA Hierarchical File System
Key elementsKey elementsFiles Files Directories Directories
Common interface for key elementsCommon interface for key elements2
/
bin/ user/ tmp/
is tom/ harry/dick/ junk
Class Hierarchy for File Structure Class Hierarchy for File Structure class Node class Node {{ public: // declare common interface herepublic: // declare common interface here
protected:protected:Node();Node();Node(const Node &);Node(const Node &);
}}class File : public Nodeclass File : public Node{{ public: public:
File ();File ();// redeclare common interface here// redeclare common interface here
}}class Directory : public Nodeclass Directory : public Node{{ public: public:
Directory ();Directory ();// redeclare common interface here// redeclare common interface here
private:private:list <Node *> _nodes;list <Node *> _nodes;
}} 3
Common abstract base class
Makeup of Common InterfaceMakeup of Common Interface
4
Operations should apply equally to Operations should apply equally to files files and and directoriesdirectories
Common interestsCommon interests name, size, protection, …
Operations that have clear meaning for both files and directories are easy to treat uniformly
Makeup of Common Interface (cont.)Makeup of Common Interface (cont.)
5
Operations that do not have clear meaning for both files and directories are difficult to treat uniformlyExampleExample
virtual Node* getChild (int n); Return the nth child Return value Node *
define getChild in both Directory class and Node classcall getChild without casting
Let us define Directory operations recursivelylong Directory :: size(){ long total = 0;
Node * child;for (int I = 0; child = getChild(i); ++i){ total += child -> size(); }return total;
}
Composite PatternComposite Pattern
6
Compose objects into tree structure to represent Compose objects into tree structure to represent part-whole hierarchiespart-whole hierarchiesGive clients uniform way to deal with these objects Give clients uniform way to deal with these objects whether internal nodes or leaveswhether internal nodes or leaves
Component
operation() getChild(int)
Leaf
operation()
Composite
operation() getChild(int)
In File structure
Component – Node Leaf– File Composite – Directory
Composite Pattern (cont.)Composite Pattern (cont.)Use Use CompositeComposite when whenRepresent part-whole hierarchies of objectsRepresent part-whole hierarchies of objectsIgnore difference between compositions of objects Ignore difference between compositions of objects
and individual objectsand individual objectsclients treat all objects uniformlyclients treat all objects uniformly
ConsequencesConsequencesPositivePositive
Supports tree structures of arbitrary complexitySupports tree structures of arbitrary complexity
NegativeNegativeLeads to system in which classes of all objects look like each other
7
Design Status CheckDesign Status Check
8
Used Composite pattern to generate Used Composite pattern to generate backbone of application backbone of application
Treated files and directories uniformlyTreated files and directories uniformly
Now look at where children come fromNow look at where children come from
AdoptionAdoption
Node created independently from directory
Directory adopts child nodevirtual void adopt (Node * child);Child’s responsibility is handed over to directory
when directory gets deleted, so does child
9
Node
operation() getChild(int)
File
operation()
Directory
operation() getChild(int)
Aggregation
OrphansOrphans
Directory no longer child’s parent
virtual void orphan (Node * child);
10
NonuniformityNonuniformity
Consider command mkdir to create subdiecrtorymkdir newsubdir
mkdir subdirA/subdirB/newsubdir
Examine consequences of not treating files and directories uniformly
11
Nonuniformity (cont.)Nonuniformity (cont.)What happens without uniformity?
12
void Client :: mkdir (Directory * current, const String & path){ string subpath = subpath (path);
if (subpath.empty()) { current->adopt (new Directory(path)); } else { string name = head (path); Node * child = find (name, current); if (child) { mkdir (child, subpath); } else { cerr << name
<< “ nonexistent.”<<endl; } }}
Node * Client :: find (const String & name, Directory * current){ Node * child = 0;
for (int i=0; child = current->getChild(i); ++i) { if (name == child->getName()) { return child; } }
return 0;}
needs Directory * not Node *
! mkdir won’t compile
Nonuniformity (cont.)Nonuniformity (cont.)
13
void Client :: mkdir (Directory * current, const String & path){ string subpath = subpath (path); if (subpath.empty()) { current->adopt (new Directory (path)); } else { string name = head (path); Node * node = find (name, current); if (node) { Directory * child = dynamic_cast<Directory *> (node); if (child) { mkdir (child, subpath); }
else { cerr << getName() << “ is not a directory.” <<endl; }} else { cerr << getName() << “ nonexistent.” <<endl; }
}}
Another alternative – using downcast
Conclusion: nonuniformity makes clients more complicated
SurrogatesSurrogatesSymbolic link (shortcut, alias)Reference to another node in file system
surrogate for the node
no effect on node if its symbolic link deleted
own access rights
How can we add these to design? Use design patterns to guide us
14
Choose Design Pattern Approaches Choose Design Pattern Approaches 1. Consider how design patterns solve design
problems
2. Scan Intent sections for something that sounds right
3. Study how patterns interrelate
4. Look at patterns whose purpose corresponds to what to do
5. Examine a relevant cause of redesign, apply patterns that help to avoid it
6. Consider what should be variable in design15
Choose Pattern for Symbolic LinksChoose Pattern for Symbolic LinksApply approach No. 6
16
AdapterAdapter Vary interface to objectVary interface to object
BridgeBridge Vary implementation of objectVary implementation of object
CompositeComposite Vary object’s structure and compositionVary object’s structure and composition
DecoratorDecorator Vary responsibilities without subclassingVary responsibilities without subclassing
FacadeFacade Vary interface to subsystemVary interface to subsystem
FlyweightFlyweight Vary storage costs of objectsVary storage costs of objects
ProxyProxy Vary how object is accessed and/or its Vary how object is accessed and/or its locationlocation
Proxy PatternProxy Pattern
17
Subject
request() …
RealSubject
request()
Proxy
request() …
realSubject… realSubject -> request(); …
Apply Proxy PatternApply Proxy Pattern
class Link : public Node
{ public :
Link (Node *);
// redeclare common Node interface here
private:
Node * _subject;
}
18
Node
…
RealSubject
…
Link
…
realSubject… realSubject -> request(); …
// Link implements Node interface
Node * Link :: getChild (int n)
{
return _subject-> getChild(n);
} RealSubject
Design Status CheckDesign Status Check
19
Applied Composite pattern structure the file Applied Composite pattern structure the file system system
Applied Proxy pattern to support symbolic Applied Proxy pattern to support symbolic linkslinks
Class Class NodeNode is intersection of the two patterns is intersection of the two patternsComponent in CompositeComponent in CompositeSubject in ProxySubject in Proxy
Class StructureClass Structure
20
Node
getName() getProtection() streamIn(istream) streamOut(ostream) getChild(int) adopt(Node) orphan(Node)
File
streamIn(istream) streamOut(ostream)
Directory
streamIn(istream) streamOut(ostream) getChild(int) adopt(Node) orphan(Node)
childrenLink
streamIn(istream) streamOut(ostream) getSubject()
Proxy Pattern Composite Pattern
subject
Visitor PatternVisitor PatternConsider operations that work differently on Consider operations that work differently on different kinds of nodesdifferent kinds of nodes UnixUnix cat cat commandcommand
solution1: change existing classessolution1: change existing classessolution2: use downcastssolution2: use downcasts
void Client :: cat (Node * node)void Client :: cat (Node * node){{ Link *l; Link *l;
if (if (dynamic_castdynamic_cast <File*> (node)) <File*> (node)) node -> streamOut (cout); node -> streamOut (cout);
else if (else if (dynamic_castdynamic_cast <Directory*> (node)) <Directory*> (node))cerr << “can’t cat a directory.” cerr << “can’t cat a directory.”
<<endl<<endlelse if (l = else if (l = dynamic_castdynamic_cast <Link *> (node)) <Link *> (node))
cat(l -> getSubject());cat(l -> getSubject());}} How to avoid How to avoid downcastsdowncasts?? 21
Visitor Pattern (cont.)Visitor Pattern (cont.)
Represent operation to be performed on Represent operation to be performed on elements of object structureelements of object structure
Define new operation without changing Define new operation without changing classes of elements on which it operatesclasses of elements on which it operatesExampleExample
Compiler supports open-ended set of analyses without Compiler supports open-ended set of analyses without changing classes that implements abstract syntax treechanging classes that implements abstract syntax tree
22
Visitor Pattern (cont.)Visitor Pattern (cont.)
23
client
Visitor
visitConcreteElemA(concrElemA a) visitConcreteElemB(concrElemB b)
Concrete Visitor1
visitConcreteElemA(concrElemA a) visitConcreteElemB(concrElemB b)
Concrete Visitor2
visitConcreteElemA(concrElemA a) visitConcreteElemB(concrElemB b)
Object structure
Element
accept(visitor v)
Concrete ElementA
accept(Visitor v) operationA()
Concrete ElementB
accept(visitor v) operationB()
Apply Visitor PatternApply Visitor Pattern
24
Node
… virtual void accept(Visitor &) =0 …
Element
Directory
… accept(Visitor & v) {v.visit(this);} …
File
… accept(Visitor & v) {v.visit(this);} …
Link
… accept(Visitor & v) {v.visit(this);} …
Concrete Elements
Apply Visitor Pattern (cont.)Apply Visitor Pattern (cont.)
25
class Visitor { public: visitor(); void visit (File *); void visit (Directory *); void visit (Link *);
}
void Visitor :: visit (File * f) { f -> streamOut(cout); }
void Visitor :: visit (Directory * d) { cerr -> “can’t cat a directory.” << endl; }
void Visitor :: visit (Link * l) { l -> getSubject() -> accept( * this); }
Visitor cat;
Node -> accept (cat);
Apply Visitor Pattern (cont.)Apply Visitor Pattern (cont.)
26
Visitor is actually an abstract classclass Visitor
{ public:
virtual ~Visitor() { }
virtual void visit (File *) = 0;
virtual void visit (Directory *) = 0;
virtual void visit (Link *) = 0;
protected:
Visitor ();
Visitor (const Visitor &);
}
Apply Visitor Pattern (cont.)Apply Visitor Pattern (cont.)
27
Unix command lists namesSuffixed by / if node is a directorySuffixed by @ if node is a symbolic link, name
Introduce SuffixPrintVisitor that prints suffix for nodeclass SuffixPrintVisitor : public Visitor
{ public :
SuffixPrinterVisitor () { }
virtual ~SuffixPrinterVisitor () { }
virtual void visit (File *) { }
virtual void visit (Directory *) { cout << “/ ”; }
virtual void visit (Link *) { cout << “@”;}
}
overload
Apply Visitor Pattern (cont.)Apply Visitor Pattern (cont.)
28
Use SuffixPrintVisitor to implement ls commandvoid Client :: ls (Node * n)
{ SuffixPrinterVisitor suffixPrinter;
Node * child;
for (int i = 0; child = n -> getChild( i ); ++i )
{
cout << child -> getName ( );
child -> accept (suffixPrinter);
cout << endl;
}
}
Apply Visitor Pattern (cont.)Apply Visitor Pattern (cont.)
29
Use different function names instead of overloadingclass Visitor{ public:
virtual ~Visitor() { } virtual void visitFile (File *) = 0; virtual void visitDirectory (Directory *) = 0; virtual void visitLink (Link *) = 0;
protected: Visitor (); Visitor (const Visitor &);
}
Void File :: accept (Visitor & v) { v.visitFile (this); }Void Directory :: accept (Visitor & v) { v.visitDirectory (this); }Void Link :: accept (Visitor & v) { v.visitLink (this); }
Clearer names
Apply Visitor Pattern (cont.)Apply Visitor Pattern (cont.)
30
When default behavior common to two or more types, put common functionality into visitNode operation for default callvoid Visitor :: visitNode (Node * n)
{ // common default behavior }
void Visitor :: visitFile (File * f)
{ Visitor :: visitNode (f); }
void Visitor :: visitDirectory (Directory * d)
{ Visitor :: visitNode (d); }
void Visitor :: visitLink (Link * l)
{ Visitor :: visitNode (l); }
Visitor CaveatsVisitor Caveats
31
Is class hierarchy stable? Adding a new kind of Node may cause all classes in
Visitor hierarchy to change
Visitor creates a circular dependency between Visitor and Node class hierarchies Change to either base class interface is likely to prompt
recompilation of both hierarchies class NewVisitor : public Visitor{ public:
using Visitor :: visit;virtual void visit (Node *);
};
Design Status CheckDesign Status Check
32
Applied Composite and Proxy patterns to define file system
Applied Visitor pattern to allow introduction of new capabilities noninvasively By adding instead of changing code
Now look at how to address security issues
Single-User ProtectionSingle-User Protection
33
Protect file system objects from accidental change or deletion A node be readable or unreadable, writable or unwritable
const string & getName( ); const Protection & getProtection( ); void setName(const string &); //neutralized for unwritable node
void setProtection(const Protection &); void streamIn(istream& ); //neutralized for unwritable node
void streamOut(ostream& ); Node * getChild (int); // inoperative for unreadable node
void adopt(Node *); //neutralized for unwritable node
void orphan(Node *); //neutralized for unwritable node
Single-User Protection (cont.)Single-User Protection (cont.)
34
To prevent deletion of unwritable nodeProtect destructor
make illegal for classes outside Node class hierarchy to delete node
disallow local Node objects created on stack
Single-User Protection (cont.)Single-User Protection (cont.)
35
To delete node that its constructor is protected operation is defined inClass outside Node class hierarchyGlobal function Node class
destroy () invariants
check whether node is writable
delete node if it is writablesubclasses extend deletion criteria and change how deletion is
carried out Template Method pattern
Template Method PatternTemplate Method Pattern
36
Define skeleton of algorithm in operation, deferring some steps to subclasses
Let subclasses redefine certain steps of algorithm without changing algorithm’s structure
AbstractClass
TemplateMethod() PrimativeOperation1( ) PrimativeOperation2( )
ConcreteClass1
PrimativeOperation1( ) PrimativeOperation2( )
ConcreteClass2
PrimativeOperation1( ) PrimativeOperation2( )
Hook methods
Apply Template Method PatternApply Template Method Pattern
37
class Node{ public:
static void destroy (Node * );…
protected:virtual ~Node( );virtual bool isWritable ( ) = 0;
};void Node :: destroy (Node * node){ if (node -> isWritable( )) { delete node; } else { cerr << node -> getName( ) << “ cannot be deleted.” <<endl;}}
Node
destroy( ) isWritable( ) streamOut( )
…
Modified CodeModified Code
38
void Node :: destroy (Node * node)
{ if (node -> isWritable( ))
delete node;
else
node -> doWarning (undeletableWarning);
}
void Node :: streamOut (ostream & out)
{ if (isReadble ( ))
doStreamOut (out);
else
doWarning (unreadableWarning);
}
Node
destroy( ) isWritable( ) streamOut( ) doWarning( )
…
Design Status CheckDesign Status Check
39
Used Template Method to implement single user protection
Should extend protection to multiple users on shared file system
Multiuser ProtectionMultiuser Protection
40
Node in Unix file system is associated with user
login in authentication
User instantiation must be carefully controlled
user login name1 1
Abstract Abstract FactoryFactory
Creates families of objects without specifying their concrete classes
Factory Factory MethodMethod
Similar to Abstract Factory without emphasis on families
BuilderBuilder Creates complex objects
PrototypePrototype Parameterizes kind of objects to instantiate
SingletonSingleton Ensures a class has only one instance, provides global point of access to instance
Singleton PatternSingleton Pattern
41
Normally allows exactly one instanceUses instance() method to control access to instanceCan be extended to allow some controlled number of instances
Singleton
instance ( ) new ( )
static instance
Return unique instance
Ensure uniqueness
Apply Singleton PatternApply Singleton Pattern
42
static const User * User :: logIn (const string & loginName,
const string & password );
logIn ensures only one instance is created per login time Looks up loginName parameter in hash table
returns entry if finds User entry Otherwise
creates new User object, authenticating against password registers User object in hash table returns User object
Propertiesaccessed globally
prevents instantiation of more than one User object per login name
return 0 if either the login name it password in invalid
application cannot change logIn by subclassing User
Add User to ParametersAdd User to Parameters
43
const User * user = 0;
static const User * User :: getUser ( ); static void User :: setUser (const User *);
const string & getName (const User * ); const Protection & getProtection (const User *);
void setName ( const string &, const User *); void setProtection (const Protection &, const User * );
void streamIn (istream &, const User *); void streamOut (ostream &, const User *);
Node * getChild (int, const User *);
void adopt (Node *, const User *); void orphan (Node *, const User *);
Add User to Parameters (cont.)Add User to Parameters (cont.)
44
extern const int maxTries; …for (int i = 0; i < maxTries; ++i)
if (user = User :: logIn (loginName, password))break;
elsecerr << :Log-in invalid!” <<endl;
if (user)User :: setUser( user);
else// lock login name
void Node :: streamOut (ostream & out, const User * u) { User * user = u ? u : User :: getUser( );
if (isReadableBy (user))doStreamOut (out);
elsedoWarning (unreadbleWarning);
}
GroupGroup
45
Group is named set of login names Groups have zero or more users User a member of zero or more groups
deleting group does not delete its constituent users
users groups
Mediator PatternMediator Pattern
46
Promotes object interaction to full object status
Fosters loose coupling by keeping objects from referring to each other explicitly
users groups
Grouping
Apply Mediator PatternApply Mediator Pattern
47
class Grouping{ public:
virtual void ~Grouping ( );static const Grouping * getGrouping ( );static void setGrouping (const Grouping *, const User * = 0);virtual void register (const User*, const Group*,
const User*=0)=0; virtual void unregister (const User*, const Group*,
const User*=0) =0 ;virtual const Group * getGroup (const string & loginName,
int index = 0) =0;virtual const string * getUser (const Group*, int index =0) =0;
protected:Grouping ( );Grouping (const Grouping & );
};
SummarySummary
48
Node
Link File Directory
User GroupGrouping
CatVisitor
NodeVisitor Visitor
ConcreteVisitor
Composite: Component Proxy: Subject TemplateMethod:AbstractClass Visitor:Element
Composite Proxy: RealSubject TemplateMethod:ConcreteClass
Leaf Proxy: RealSubject TemplateMethod:ConcreteClass
Leaf Proxy: RealSubject TemplateMethod:
ConcreteClass
Mediator: ConcreteColleague Singleton (variant)
Mediator: ConcreteColleagueConcreteMediator Singleton
AcknowledgementAcknowledgement
49
This work was supported by a grant from This work was supported by a grant from Acxiom Corporation titled “The Acxiom Acxiom Corporation titled “The Acxiom Laboratory for Software Architecture and Laboratory for Software Architecture and Component Engineering (ALSACE).”Component Engineering (ALSACE).”