Download presentation
Presentation is loading. Please wait.
Published byRandolph Payne Modified over 9 years ago
1
Csci 490 / Engr 596 Special Topics / Special Projects Software Design and Scala Programming Spring Semester 2010 Lecture Notes
2
Designing with Patterns This is a set of slides to accompany chapter 2 of John Vlissides’s book Pattern Hatching : Design Patterns Applied (Addison Wesley, 1998) Created: 14 September 2004 Revised 20 April 2010
3
Introduction Best way to learn to use design patterns is to just use them Case study here is a Unix-like hierarchical file system 1
4
A Hierarchical File System Key elements Files Directories Common interface for key elements 2 / bin/ user/tmp/ istom/ harry/dick/junk
5
Class Hierarchy for File Structure class Node {public: // declare common interface here protected:Node(); Node(const Node &); } class File : public Node {public: File (); // redeclare common interface here } class Directory : public Node {public: Directory (); // redeclare common interface here private: list _nodes; } 3 Common abstract base class
6
Makeup of Common Interface 4 Operations should apply equally to files and directories Common interests name, size, protection, … Operations that have clear meaning for both files and directories are easy to treat uniformly
7
Makeup of Common Interface (cont.) 5 Operations that do not have clear meaning for both files and directories are difficult to treat uniformlyExample virtual Node* getChild (int n); Return the nth child Return value Node * define getChild in both Directory class and Node class call getChild without casting Let us define Directory operations recursively long Directory :: size() {long total = 0; Node * child; for (int I = 0; child = getChild(i); ++i) { total += child -> size();} return total; }
8
Composite Pattern 6 Compose objects into tree structure to represent part-whole hierarchies Give clients uniform way to deal with these objects whether internal nodes or leaves Component operation() getChild(int) Leaf operation() Composite operation() getChild(int) In File structure Component – Node Leaf– FileComposite – Directory
9
Composite Pattern (cont.) Use Composite when Represent part-whole hierarchies of objects Ignore difference between compositions of objects and individual objects clients treat all objects uniformly Consequences Positive Supports tree structures of arbitrary complexity Negative Leads to system in which classes of all objects look like each other 7
10
Design Status Check 8 Used Composite pattern to generate backbone of application Treated files and directories uniformly Now look at where children come from
11
Adoption Node created independently from directory Directory adopts child node virtual 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
12
Orphans Directory no longer child’s parent virtual void orphan (Node * child); 10
13
Nonuniformity Consider command mkdir to create subdiecrtory mkdir newsubdir mkdir subdirA/subdirB/newsubdir Examine consequences of not treating files and directories uniformly 11
14
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
15
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 (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
16
Surrogates Symbolic 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
17
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 design 15
18
Choose Pattern for Symbolic Links Apply approach No. 6 16 Adapter Vary interface to object Bridge Vary implementation of object Composite Vary object’s structure and composition Decorator Vary responsibilities without subclassing Facade Vary interface to subsystem Flyweight Vary storage costs of objects Proxy Vary how object is accessed and/or its location
19
Proxy Pattern Provide a surrogate or placeholder for another object to control access to it Use Proxy when Need a more versatile or sophisticated reference to object than simple pointer 17 Subject request() … RealSubject request() Proxy request() … realSubject … realSubject -> request(); …
20
Apply 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 i mplements Node interface Node * Link :: getChild (int n) { return _subject-> getChild(n); } RealSubject
21
Design Status Check 19 Applied Composite pattern structure the file system Applied Proxy pattern to support symbolic links Class Node is intersection of the two patterns Component in Composite Subject in Proxy
22
Class 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) children Link streamIn(istream) streamOut(ostream) getSubject() Proxy Pattern Composite Pattern subject
23
Visitor Pattern Consider operations that work differently on different kinds of nodes Unix cat command solution1: change existing classes solution2: use downcasts void Client :: cat (Node * node) { Link *l; if (dynamic_cast (node)) node -> streamOut (cout); node -> streamOut (cout); else if (dynamic_cast (node)) cerr << “can’t cat a directory.” <<endl else if (l = dynamic_cast (node)) cat(l -> getSubject()); } How to avoid downcasts? 21
24
Visitor Pattern (cont.) Represent operation to be performed on elements of object structure Define new operation without changing classes of elements on which it operates Example Compiler supports open-ended set of analyses without changing classes that implements abstract syntax tree 22
25
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()
26
Apply 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
27
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);
28
Apply Visitor Pattern (cont.) 26 Visitor is actually an abstract class class Visitor { public: virtual ~Visitor() { } virtual void visit (File *) = 0; virtual void visit (Directory *) = 0; virtual void visit (Link *) = 0; protected: Visitor (); Visitor (const Visitor &); }
29
Apply Visitor Pattern (cont.) 27 Unix command lists names Suffixed by / if node is a directory Suffixed by @ if node is a symbolic link, name Introduce SuffixPrintVisitor that prints suffix for node class SuffixPrintVisitor : public Visitor { public : SuffixPrinterVisitor () { } virtual ~SuffixPrinterVisitor () { } virtual void visit (File *) { } virtual void visit (Directory *) { cout << “/ ”; } virtual void visit (Link *) { cout << “@”;} } overload
30
Apply Visitor Pattern (cont.) 28 Use SuffixPrintVisitor to implement ls command void Client :: ls (Node * n) {SuffixPrinterVisitor suffixPrinter; Node * child; for (int i = 0; child = n -> getChild( i ); ++i ) { cout getName ( ); child -> accept (suffixPrinter); cout << endl; }
31
Apply Visitor Pattern (cont.) 29 Use different function names instead of overloading class 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
32
Apply Visitor Pattern (cont.) 30 When default behavior common to two or more types, put common functionality into visitNode operation for default call void 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);}
33
Visitor 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 *); };
34
Design 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
35
Single-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
36
Single-User Protection (cont.) 34 To prevent deletion of unwritable node Protect destructor make illegal for classes outside Node class hierarchy to delete node disallow local Node objects created on stack
37
Single-User Protection (cont.) 35 To delete node that its constructor is protected operation is defined in Class outside Node class hierarchy Global function Node class destroy () invariants check whether node is writable delete node if it is writable subclasses extend deletion criteria and change how deletion is carried out Template Method pattern
38
Template 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
39
Apply 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 getName( ) << “ cannot be deleted.” <<endl;} } Node destroy( ) isWritable( ) streamOut( ) …
40
Modified 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( ) …
41
Design Status Check 39 Used Template Method to implement single user protection Should extend protection to multiple users on shared file system
42
Multiuser Protection 40 Node in Unix file system is associated with user login in authentication User instantiation must be carefully controlled userlogin name 11 Abstract Factory Creates families of objects without specifying their concrete classes Factory Method Similar to Abstract Factory without emphasis on families Builder Creates complex objects Prototype Parameterizes kind of objects to instantiate Singleton Ensures a class has only one instance, provides global point of access to instance
43
Singleton Pattern 41 Normally allows exactly one instance Uses instance() method to control access to instance Can be extended to allow some controlled number of instances Singleton instance ( ) new ( ) static instance Return unique instance Ensure uniqueness
44
Apply 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 Properties accessed 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
45
Add 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 *);
46
Add User to Parameters (cont.) 44 extern const int maxTries; … for (int i = 0; i < maxTries; ++i) if (user = User :: logIn (loginName, password)) break; else cerr << :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); else doWarning (unreadbleWarning); }
47
Group 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
48
Mediator Pattern 46 Promotes object interaction to full object status Fosters loose coupling by keeping objects from referring to each other explicitly users groups Grouping
49
Apply 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 & ); };
50
Summary 48 Node Link FileDirectory User GroupGrouping CatVisitor NodeVisitor Visitor ConcreteVisitor Composite: Component Proxy: Subject TemplateMethod:AbstractClass Visitor:Element Composite Proxy: RealSubject TemplateMethod:ConcreteClass Leaf Proxy: RealSubject TemplateMethod:ConcreteClass Mediator: ConcreteColleague Singleton (variant) Mediator: ConcreteColleagueConcreteMediator Singleton
51
Acknowledgement 49 This work was supported by a grant from Acxiom Corporation titled “The Acxiom Laboratory for Software Architecture and Component Engineering (ALSACE).” This work was supported by a grant from Acxiom Corporation titled “The Acxiom Laboratory for Software Architecture and Component Engineering (ALSACE).”
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.