CS5103 Software Engineering Lecture 09 Software Design and Design Patterns Last class Sequence diagrams Software architecture 2 Why? Higher level design of larger software projects Software architectural styles Pipe and Filter Layered Repository Today’s class 3 Software Design Process Factors Measures Design Patterns Composite pattern Factory pattern Visitor pattern Software Design Process Software design is usually created in two stages Architecture design Detail design (component-level design) 4 Design patterns Design classes Design review Other design issues Refactoring UI Design Software Design Factors Fundamental software design factors 5 Modularity Abstraction Information hiding Component independence Fault prevention and fault tolerance Modularity and Abstraction When we consider modular solutions to any problems, many levels of abstraction can be posed At the lower levels of abstraction, a more detailed description of the solution is provided: class diagrams Modularity hides details and facilitates evolvement 6 At the highest level of abstraction, a solution is stated in broad terms of problem domain: architecture Each component hides a design decision from the others Component Independent 7 We strive in most designs to make the components independent of one another, why? We measure the degree of component independence using two concepts Low coupling High cohesion Coupling and Cohesion Coupling Two components are loosely coupled when they have some dependence, but the interconnections among them are weak Two components are uncoupled when they have no interconnections at all Cohesion 8 Two components are highly coupled when there is a great deal of dependence between them A component is cohesive if the internal parts of the component are related to each other and to its overall purpose Decoupling Most difficult part in design: Need tradeoff Consider the following case: Online and offline bookstore components Both need shopping cart checkout 9 Should we have a shopping cart checkout module for both of them? Or have one, and let the two components to call the module? Consider potential changes on overall book discount policy and specific book discount policy? Solution for the dilemma: break the checkout module, still not perfect Fault defense & tolerance Defensive design anticipates situations the might lead to problems Network Failure Data corruption Invalid user inputs Tolerate runtime errors 10 Exception handling Redundant components (distributed system or critical software) Timely error reporting Criteria for Good Software Design 11 High-quality designs should have characteristics that lead to quality products Correct translation from the requirements specification Ease of understanding Ease of implementation Ease of testing Ease of modification Criteria for Good Software Design 12 Architecture Using suitable architectural styles or patterns Loose-coupled components Can be implemented in an evolutionary fashion Classes at a suitable abstract level Interfaces are clear and minimize the data transfer Design using a effective notation Software Design Evaluation and Validation We check a design in two different ways 13 Validation: the design satisfies all requirements specified by the customer Verification: the characteristics (quality) of a good design are incorporated We use some techniques for helping us to perform verification and validation Measuring design quality Design reviews Measuring Software Design Quality 14 We check a design using a set of measures Coupling Cohesion Complexity Basic metrics: size Depths of relations Cyclomatic complexity Design reviews 15 We check a design Designers Customers Analysts prospective users Developers Moderator leading the discussions Secretary recording the issues Design Patterns Become popular due to a book 16 Design Patterns: Elements of Reusable ObjectOriented Software Gang of Four: Gamma, Erich; Richard Helm, Ralph Johnson, and John Vlissides Provide solutions for common problems in micro-design Design Patterns Structure Name Problem solved When to apply the pattern Solution 17 Important to know for easier communication between designers Usually a class diagram segment (sometimes attributes and operations) Design Patterns Structure Patterns Creation Patterns Factory Behavioral Patterns 18 Composite Visitor Running Example 19 A Document Editor Text and graphics can be freely mixed Graphical user interface Support different look-and-feel GUI styles Traversal operations: spell checking Composite Pattern: Example A document is represented by structure Primitive glyphs 20 Characters, circles, pictures Lines: A sequence of glyphs Columns: A sequence of Lines Pages: A sequence of Columns Documents: A sequence of Pages Example of Hierarchical Document 21 Possible Designs? Document 1 * Page Classes of Character, Circle, Pictures, lines, Columns, pages, document Composition relationship between classes Not so good, why? A lot of duplication: 1* Column 1* Line 1 * Glyph all classes has onClick, draw behavior Difficult to add or remove levels in hierarchy Character 22 Picture Traverse/display operations need to change for any change of the hierarchy Alternate Designs One class of Glyph 23 All elements are subclasses of Glyph All elements uniformly present the same interface How to draw Computing bounds Mouse hit detection … Make extending the class easier Logic Object Structure 24 Logic Object Structure 25 Composite Pattern Applies to any hierarchy structure Leaves and internal nodes have similar functionality Pros: Easy to add, remove new types Clean interface Cons: 26 Add corner cases Enforce certain type of elements only have certain types of children Problem: Supporting look-and-feel settings Different look-and-feel standards Appearance of scrollbars, menus, windows, etc., set by the user We want to support them all 27 For example: having classes MotifScrollBar and WindowsScrollBar Both are subclasses of ScrollBars How should we create a new scrollbar in a window? Possible designs? Terrible ScrollBar sc = new WindowScrollBar(); Better, problems? If (style==MOTIF){ sc = new MotifScrollBar(); }else{ sc = new WindowScrollBar(); } 28 Conditions for other UI elements: menus, etc. Hard to add new styles Factory Pattern 29 One method to create a look-and-feel dependent object Define a GUIFactory Class One GUIFactory object for each look and feel Create itself using conditions set by the user Factory Pattern 30 Factory Pattern: factory design abstract class GUIFactory{ abstract ScrollBar CreateScrollBar(); abstract Menu CreateMenu(); ... } 31 class MotifFactory extends GuiFactory{ ScrollBar CreateScrollBar(){ return new MotifScrollBar() } Menu createMenu(){ return new MotifMenu(); } ... } Factory Pattern: GUI code GuiFactory factory; if(style==MOTIF){ factory = new MotifFactory(); }else if(style==WINDOW){ factory = new WindowFactory(); }else{ ... } ScrollBar bar = factory.createScrollBar(); Menu mn = factory.createMenu(); 32 Factory Pattern Applies to the object creation of a family of classes The factory can be changed at runtime Pros & Cons 33 Lift the conditional creation of objects to the creation of factories Flexible for add new type of objects Sometimes make the code more difficult to understand Spell Checking Problem Considerations Requires to traverse the document Need to see every glyph in order There maybe other analyses that require traverse the document Counting 34 words, Grammar checking Possible design class Glyph{… void spellCheck(char[] curWord): for(int i = 0; i<this.children.length; i++): this.children[i].spellCheck(curWord); if(this isa character): if(this == space): Util.SpellCheck(curWord); curWord.clear(); else: curWord.append(this); …} 35 Other Actions 36 Document Stats counting words, counting pictures, counting lines, columns, pages) Find a word Change line-break rules … Now we have A large number of similar methods Code duplication always causes problems 37 All need to traverse the document change the data structure of “children” from array to list (better insertion and deletion) Skip full-picture page to enhance performance for word-related tasks Solution Visitor pattern Decouple traverse process and action Write traverse code only once 38 Each element in the hierarchy under traverse has an “action” code Visitor pattern basic design class Glyph{… void scan(Visitor v): for(int i = 0; i<d.children.length; i++): d.children[i].scan(v); …} We have different visitors: spellcheckVisitor, documentStatVisitor, etc. 39 Solution 40 Code of SpellCheckVisitor class SpellCheckVisitor extends Visitor{ char[] curWord = {}; void visitChar(Character c){ if(c==SPACE): spellCheck(curWord); curWord.clear(); else: curWord.add(c); } } 41 Still have problems 42 How about counting all glyphs (including picture, character, circle)? Need to change visitPicture, visitCharacter, and visitCircle? Add visitGlyph Add VisitGlyph class Glyph{ … scan(Visitor){ for each c in children: v.visitGlyph(c) c.Scan(v) } … } 43 Code of CountGlyphVisitor class CountGlyphVisitor extends Visitor{ int count = 0; void visitGlyph(Glyph g){ if(g.children.length==0): count++; } } } 44 Summary of design patterns Major aim: decoupling pros: more flexibility less changes when modification is required Cons: 45 not intuitive at the beginning, harder to read code, but get easier when you are familiar with the pattern Trouble for verifications and analysis (often not scalable without a design pattern) So it is rather popular! Other useful design patterns Observer Facade Factory method Decorator Bridge See them in wiki pages or read the book 46 Design Patterns: Elements of Reusable Object-Oriented Software Today’s class 47 Software Design Design process Design factors Design evaluation Design Patterns Composite pattern Factory pattern Visitor pattern Next Class 48 Software Implementation: Versioning Why versioning? Versioning tools CVS SVN GIT Thanks! 49 A controversial pattern: singleton pattern Problem 50 How to make sure a class only has one object File system Windows manager … The class itself is made responsible for keeping track of its instance. It can thus ensure that no more than one instance is created. This is the singleton pattern. Singleton Pattern Singleton static uniqueInstance singletonData static Instance() SingletonOp() GetSingletonData() return uniqueinstance Singleton Pattern: code class Singleton { // Only one instance can ever be created. public: static Singleton Instance(); protected: // Creation hidden inside Instance(). Singleton(); private: Static Singleton _instance } // Cannot access directly. Singleton Pattern: code Singleton _instance; Singleton Instance(){ if (_instance ==null) { _instance=new Singleton; } Return _instance; } // Clients access the singleton // exclusively via the Instance member // function. Singleton Pattern Pros 54 Controls the number of instances (no need to refer to another class) Lazy initialization Cons Latent coupling between components and singleton Singleton may have states: hard to test Cannot extend singleton class Terrible in multi-thread if not well implemented View points 55 We love singletons (getting weaker and weaker in the past 10 years) Singletons are evil, actually an anti-pattern (many people believe this) Use singletons carefully and only when no other alternatives, control the number of singleton classes in your project Alternative Dependency injection: 56 Create the object at the beginning and pass it as a parameter A story from google A new developer go to a group and want to try on some methods testCreditCardCharge() { CreditCard c = new CreditCard( "1234 5678 9012 3456", 5, 2008); c.charge(100); } This code: Only works when you run as part of the suite. When run in isolation, throws NullPointerException. When you get your credit card bill, you are out $100 for every time the test runs. 57 A story from google After a lot of digging, you learn that you need to initialize the CreditCardProcessor. testCreditCardCharge() { CreditCardProcessor.init(); CreditCard c = new CreditCard( "1234 5678 9012 3456", 5, 2008); c.charge(100); } Still not working 58 A story from google After a lot of digging, you learn that you need to initialize the OfflineQueue. testCreditCardCharge() { OfflineQueue.init(); CreditCardProcessor.init(); CreditCard c = new CreditCard( "1234 5678 9012 3456", 5, 2008); c.charge(100); } Still not working 59 A story from google After a lot of digging, you learn that you need to initialize the Database. testCreditCardCharge() { Database.init(); OfflineQueue.init(); CreditCardProcessor.init(); CreditCard c = new CreditCard( "1234 5678 9012 3456", 5, 2008); c.charge(100); } But sometimes you have to find out the correct sequence! 60 Solution with dependency injection testCreditCardCharge() { Database db = Database(); OfflineQueue q = OfflineQueue(db); CreditCardProcessor ccp = new CreditCardProcessor(q); CreditCard c = new CreditCard( "1234 5678 9012 3456", 5, 2008); c.charge(ccp, 100); } 61