Presentation is loading. Please wait.

Presentation is loading. Please wait.

Csci 490 / Engr 596 Special Topics / Special Projects Software Design and Scala Programming Spring Semester 2010 Lecture Notes.

Similar presentations


Presentation on theme: "Csci 490 / Engr 596 Special Topics / Special Projects Software Design and Scala Programming Spring Semester 2010 Lecture Notes."— Presentation transcript:

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).”


Download ppt "Csci 490 / Engr 596 Special Topics / Special Projects Software Design and Scala Programming Spring Semester 2010 Lecture Notes."

Similar presentations


Ads by Google