Presentation is loading. Please wait.

Presentation is loading. Please wait.

Software Design "You can use an eraser on the drafting table or a sledgehammer on the construction site.“ --Frank Lloyd Wright Software design is an important.

Similar presentations


Presentation on theme: "Software Design "You can use an eraser on the drafting table or a sledgehammer on the construction site.“ --Frank Lloyd Wright Software design is an important."— Presentation transcript:

1 Software Design "You can use an eraser on the drafting table or a sledgehammer on the construction site.“ --Frank Lloyd Wright Software design is an important topic because: It is one of the most challenging activities of s/w development (no algorithm or prescriptive process that is guaranteed to deliver a good design), and There is a high payoff associated with good design (or high penalty with bad design) TBD: Good book on design heuristics I haven’t read: OOD Heuristics by Riel. TBD: shows a good example of the advantages of modularizing around objects vs. steps in the application’s work-flow process. TBD: good discussion of design/Architecture and abstraction: pl/design/abstraction-classes-sw-design_ieesw.pdf Design: manage complexity and reduce rework. Whenever building or creating something with a complex semi-rigid final form, it pays to plan out the solution in advance of the actual implementation. In the construction industry, builders know better than to begin construction without having a full set of designs and blueprints. (Database design, building design, landscaping design) (multiple configuration options, constraints, trade-offs, priorities). Because software is easier to change after construction than physical products such as buildings and mechanical devices, maybe ideas about design that apply to physical products don’t apply to the same degree to software. There are software development methodologies that advocate just enough design up-front with the idea that design is ongoing and can occur synchronous with construction. Refactoring is one way of doing “retroactive design”. Interesting because of the incredible opportunities associated with good design and serious risks associated with bad design. One indicator it is a challenging topic: it takes 15 slides just to define design. Don’t have a precise process, but rather a bunch of heuristics that are much more useful with experience. Image above is from wikipedia. It is in the public domain.

2 Organization of Topics
Introduction Opportunities for Design What is Software Design? The Importance of Managing Complexity What makes design challenging Designs are Abstractions of the Anticipated Implementation Design is a Wicked Problem Design Concepts Design is a Universal Activity Design Occurs at Different Levels Characteristics of Software Design The Benefits of Good Design A Generic Design Process Design Methods Design Techniques and Tactics Step-Wise Refinement Look for Real-World Objects Noun-Verb Analysis CRC Cards Test-Driven Development Core Design Principles and Heuristics Modularity Information hiding Encapsulation Abstraction Coupling and Cohesion Supporting Design Principles and Heuristics Don’t Repeat Yourself (DRY) Principle of Least Astonishment/Surprise (POLA) Single Responsibility Principle (SRP) Open-Closed Principle (OCP) Interface segregation principle (ISP) Dependency Inversion Principle (DIP) Separation of Concerns Consider Brute Force Object-Oriented Design Principles and Heuristics Generalization and Specialization Principle of Substitution (aka Liskov substitution principle) Favor Composition Over Inheritance Law of Demeter Make sure the following are covered somewhere [Head First DP’s]: Encapsulate what varies Favor composition over inheritance. (Described in detail in notes.) Program to interfaces, not implementations. Strive for loosely coupled designs between objects that interact. Classes should be open for extension but closed for modification Depend on abstractions. Do not depend on concrete classes. And these from Software Development and Professional Practice: Encapsulate things likely to change Code to an interface rather than to an implementation Principle of least knowledge Design Challenges?? Here is the “design” or organization of topics related to software design. Here is an outline of chapter topics. The topics and their organization are this way because there is no reliable prescriptive process that is guaranteed to produce an optimal or even good design. Instead we have generic processes and best practices guided by principles and heuristics. Concepts are generalizations or broad principles. Concepts are the foundation for deep understanding. A practice (method or process) is a customary way of doing something. Practices are scripted behaviors that produce a result. Principles are basic truths that shape practices and heuristics. There is a fine line between principles and heuristics. Here I’m using the term principles to refer to guidelines that are more abstract and need more interpretation than the guidelines I'm calling heuristics. Heuristics are trial and error methods for solving a problem. A heuristic is a rule-of-thumb that works in most but not all situations. Example heuristic: “If someone tells you to look in the kitchen sink, don’t.” OR A rule of thumb when designing a kitchen is arrangement of sink, stove and refrigerator should form a triangle. Example principle (in economics): opportunity costs. The cost of something is what you give up to get it. How much does 4 years of school cost? You can't simply add up the cost of tuition, books, room, board, etc. You also have to add in the cost of what you gave up to go to school, which might be 4 years of wages (assuming you don't work while in school). Heuristics you can directly apply without much though. Principles are more general, not any less valuable than heuristics, but are used in more subtle ways.

3 Opportunities for Design
There are three major opportunities for design during the software life cycle: product design or the design of the user experience, software design or the design of the internal system structure and low-level code design or the design of a routine. Product design is concerned with the outwardly appearance of the software--the look and behavior of the software from the user's perspective. Software design is concerned with the structure of the solution from the programmer's perspective. Code design is still from the programmers perspective but usually occurs during coding (decisions about control structures and local variables). You can even break software design into architecture and mid-level design. Software architecture is described in chapter x. User interface design is the topic of chapter x. The SRS describes how the software will work from the user’s perspective (screens, navigation, menus, dialogs, etc.). The engineering design describes the internal implementation of the software (modules, relationships between modules, database schema, data structures, algorithms, etc.) The focus of this chapter is on engineering design. Product design or user interface design is a part of system definition or requirements. Engineering design is about defining systems, sub-systems and their relationships (static and dynamic). Good product design leads to products that are easy and pleasant to use. Good engineering design controls complexity and makes it easier to modify and extend the software. Most non-functional requirements are also driven by design (architecture).

4 What is Software Design?
Design bridges that gap between knowing what is needed (software requirements specification) to entering the code that makes it work (the construction phase). Design is both a verb and a noun. Software design takes place between the requirements phase and the construction phase of the software life cycle. Design is both a noun and a verb: Don’t bother me; I’m designing (verb). After a few more iterations I should be done with the design (noun). Design = divide the anticipated solution into modules. Specify general content/responsibilities of each module along with collaborations between modules.

5 What is Software Design? [cont]
During the design phase, software engineers apply their knowledge of the problem domain and implementation technologies in order to translate system specifications into plans for the technical implementation of the software. The resulting design expresses the overall structure and organization of the planned implementation. It captures the essence of the solution independent of any implementation language. Bridge the gap between requirements and implementation. Output of the requirements process is a system specification describing the desired system behavior. The purpose of design is to create a plan or “blueprint” for implementing the solution called for in the system specification. A software design expresses the overall structure and organization of the planned implementation. The resulting design captures the essence of the solution independent of any implementation language. Output of the requirements process is a system specification or black-box model of the proposed system’s behavior. Designers must be aware of implementation technologies (look forward to the implementation phase) in order to create practical designs. Design describes the work that goes on between requirements and implementation. During design attention shifts from understanding the problem and desired system behavior toward planning the implementation of the solution. Output of the requirements process is a system specification that describes the outwardly system behavior which is expected to meet the needs and expectations of customers and users. The purpose of design is to create the overall organization and structure of the solution needed to implement the behavior called for in the system specification. The purpose of design is to create solution models that express the overall organization and structure of the eventual implementation. The output of the design process is a technical plan for implementation. This technical plan specifies the overall organization and structure of the solution. Output of the requirements process is a system specification or black box model of the system’s behavior. The purpose of design is to create solution models that can be elaborated into executable code. During the requirements process a black box model of system behavior is created. Design marks the transition from understanding the problem to inventing a solution. It marks the transition from understanding the problem to trying to devise a solution to the problem. “invent the solution” which is elaborated into an implementation. Design encompasses the creative part. Leap of intuition. Design is both a process and a product. All but the simplest programming problems are too complex to solve without at least some initial technical planning. Design is both an activity/process and an artifact/product. Design is opportunistic; designs are emergent, that is they emerge over time. System requirements: user/customer wants, needs and expectations. System specifications: outwardly system behavior expected to meet user/customer wants, needs and expectations. Software design is scheduled to occur after requirements specification but before implementation (in practice, most designs evolve iteratively) Design is an especially creative activity and therefore hard to schedule (hence iteration)

6 There is a famous cartoon showing two professors at a chalkboard examining a proof that includes the step “then a miracle occurs”. At times it seems like this is the most that can be hoped for during software design. Even those proficient at software design might not be able to fully explain how they arrived at their result. (At times it can seem like this is the best description for what takes place during software design.)

7 Relying on miracles or exceptional ingenuity isn’t a reliable (predictable or repeatable) way of arriving at a design. The design process can be made more systematic and predictable through the application of methods, techniques and patterns, all applied according to principles and heuristics. [tbd: replace inside of req to text. Increase width of evolving design boxes.] There is a process for design but not an algorithm. There is no guarantee the process of design will yield a satisfactory result. Consequently, principles and heuristics also play an important role during design. They are used along the way to guide decisions and assess intermediate results. Many times it is sufficient to just get something that sort-of kind-of works. This can be refactored into a solid design. Software design is a heuristic rather than deterministic process. Any procedure process of design will include many rules-of-thumb. Concepts and principles provide direction and criteria for evaluating candidate solutions. As figure x illustrates, the application of established methods, techniques and patterns reduces the scope of work that requires creativity and innovation. Rather than needing a miracle or extraordinary insight to make the gigantic leap from requirements to workable design, there are targeted opportunities for creativity and insight during the application of methods, techniques and patterns. For example, experience and judgment might be used to recognize when a design pattern applies. Once a design pattern is identified, the process of adapting it to the problem at hand is fairly routine. (When applying established methods, techniques and patterns there is still opportunity for creativity and innovation, it’s just more focused.) (Concepts, principles and heuristics are used along the way to guide decisions and assess candidate solutions.) (Reliance on miracles or extraordinary insight is replaced by targeted opportunities for creative insight.) No reliable process for conjuring up a design

8 Importance of Managing Complexity
Poorly designed programs are difficult to understand and modify. The larger the program, the more pronounced are the consequences of poor design. The importance of having a sound design increases with the size and complexity of the software system. Figure x illustrates the consequences of good and bad design. X-axis is the cost of another feature, not accumulative cost of n features. It’s relatively easy to make a change to a small program that has only a few features. As program size increases the ease with which code can be added or changed depends on the program’s design. For a well-designed program the cost or difficulty of making a change goes up moderately with the size of the program. With a poorly design program the cost and difficulty of making a change rises rapidly with program size. On some projects it becomes a race against time to complete the project before the complexity becomes so overwhelming that progress grinds to a halt. Good design also helps control maintenance costs. Since 40-70% of the total life cycle cost of a software system is spent on maintenance, the payback from having a flexible design can be substantial. Even when a project doesn’t fail outright because of uncontrolled complexity, the accumulative long-term costs of a poorly design program can have greater consequence than a single project failure. Good Design Minimizes Intellectual Complexity Cost of adding the ith feature to a well-designed and poorly designed program

9 Two Types of Complexity in Software
To better understand how good design can minimize technical complexity, it’s helpful to distinguish between two major types of complexity in software: Essential complexities – complexities that are inherent in the problem. Accidental/incidental complexities – complexities that are artifacts of the solution. The total amount of complexity in a software solution is: Essential Complexities + Accidental complexities The primary purpose of design is to control complexity Goal: manage essential complexity while avoiding the introduction of additional accidental complexities [tbd: expand on essential and accidental complexities with a few examples that make it clear the role that design can play] There is a certain amount of complexity inherent in the problem. The real world is complex and if you are going to automate processes from the real world a good portion of that complexity will inevitably carry over into the solution. There is no way to avoid it. This puts a lower bound on the amount of complexity that will be present in the solution. Just as there is conservation of energy in thermodynamics, there is conservation of complexity in software solutions. Coding a solution to a problem creates the opportunity to introduce additional complexities, what Fred Brooks terms accidental complexities. These complexities are a consequence of implementation decisions. For example, if you used random 3-letter abbreviations for variables names your solution would add a layer of complexity not present in a solution that used descriptive names. It's the lack of constraints in s/w that creates the opportunity for complexity. When building something with your hands in the garage there are limits to what you can build. Budget constraints limit the amount of resources you have to work with and physical constraints limit what you can do with the resources that are available. For example, what you create can't be larger then the garage in which you are working. With software there are no constraints. There is no direct cost to adding another line of code. It's as if you were a contractor building a house next to a hardware store that offered an unlimited supply of materials and at the same time you had access to an unlimited supply of labor. “As a general principle, where there’s a lack of constraints, there often is a concomitant lack of restraint.” “Complexity is the root cause of many other problems, such as computer security.”

10 Design: An Antidote to Complexity
Design is the primary tool for managing essential and accidental complexities in software. Good design doesn’t reduce the total amount of essential complexity in a solution but it will reduce the amount of complexity that a programmer has to deal with at any one time. A good design will manage essential complexities inherent in the problem without adding to accidental complexities consequential to the solution. One way to add to the accidental complexity of a solution is to use one character variable names.

11 Design Techniques for Dealing with Software Complexity
Modularity – subdivide the solution into smaller easier to manage components. (divide and conquer) Information Hiding – hide details and complexity behind simple interfaces Using design techniques to minimize complexity, an illustrative example. Two key techniques for managing complexity in software are modularity and information hiding. The amount of detail in even simple programs is too much for most programmers to deal with all at once. The solution is to subdivide the code into smaller easier to manage components. Understanding the program is now a matter of understanding each individual component and then the overall interaction of the components. Information hiding prevents the details within one components from interfering with those of another. Simple interfaces around each component makes it possible to understand their interaction/function as a whole. Controlling S/W Complexity abstractions are simplifications that suppress details that are unimportant for a particular purpose One of the boxes on the right might be a Linked List class with a simple interface add(), remove(), etc.. Here are some principles and techniques for dealing with software complexity. Dealing with Complexity Created layered designs.

12 Additional Techniques for Dealing with Complexity
Abstraction – use abstractions to suppress details in places where they are unnecessary. Hierarchical Organization – larger components may be composed of smaller components. Examples: a complex UI control such as tree control is a hierarchical organization of more primitive UI controls. A book outline represents the hierarchical organization of ideas.

13 Why Design is Hard Design is difficult because design is an abstraction of the solution which has yet to be created We said before that design is “the overall structure and organization of the planned implementation. It captures the essence of the solution independent of any implementation language.” Models are created during analysis and design. Design models are harder to created because they are abstractions of the yet-to-be-created implementation. Analysis models represent real concepts in the problem domain. You can get an analysis model simply by ignoring details from the entity in the problem domain. Example: Financial services company. Investment is an abstraction. It might be stocks, bonds or mutual funds. Investor is another abstraction. It might be individual or institutional. One solution to the vexing problem of how to craft a design from software requirements is to create analysis or domain models that capture the structure and workflow of the problem domain and then tap these models with a sorceress magic wand and declare them design models. As solution models they may be incomplete (the solution domain has implementation issues to deal with like programming languages and library infrastructure) but if you have lead a good clean life there is a good chance these newly christened design models can be worked into suitable design models. For more on the idea of using analysis models (aka domain models) as the foundation or starting point for design models, see page 183 of Pattern-Oriented Software Architecture: A Pattern Language for Distributed Computing, 4th Volume.

14 Design is a wicked problem
The term wicked problem was first used to describe problems in social planning but engineers have recognized it aptly describes some of the problems they face as well. A wicked problem is one that can only be clearly defined by solving it. Need two solutions. The first to define the problem, and the second to solve it in the most efficient way. Fred Brooks could have been talking about wicked problems when he advised: “Plan to throw one away; you will anyhow.” “The concept of "wicked problems" was originally proposed by Horst Rittel (a pioneering theorist of design and planning, and late professor at the University of California, Berkeley) and M. Webber [1] in a seminal treatise for social planning” [Wikipedia] “Wicked problems have incomplete, contradictory, and changing requirements; and solutions to them are often difficult to recognize as such because of complex interdependencies.” Here is a simple example of a wicked problem. At the start of the growing season farmer Brown needs to decide whether to plant cotton or peanuts. Cotton prices are at record levels, so the answer seems obvious: plant cotton. However, when it comes time to sell the harvest, farmer Brown discovers that many other farmers made the same decision thereby depressing cotton prices. To add insult to injury, peanut prices soar. The obvious solution to the problem had unanticipated consequences. The problem needed to be solved in order to fully understand the dynamics of the problem.

15 Design is a Universal Activity
Any product that is an aggregate of more primitive elements, can benefit from the activity of design. [Start of Concepts section] Design = plan for the aggregation of more primitive elements. Implementation decisions can be hard to reverse. Therefore, there is considerable value in design or implementation planning. A building design is a plan for aggregating doors, windows, concrete, steel, etc. Notice that some of the primitive components being aggregated are themselves aggregates of more primitive elements. For example, a window is an aggregate of glass and wood. Given the same pile of building materials, there are many different ways of organizing them into a home—some results more useful and esthetically pleasing than others. A database design is a plan for the aggregation of fields, data types and relations. A UI design is a plan for the organization of primitive UI controls such as buttons, labels, tree views, etc. Notice, again some of the primitive components are again aggregates of more primitive elements. A software design is a plan for aggregating more primitive programming elements such as classes, functions, programming language statements, etc. A Design is a plan for aggregating more primitive components Design is a critical activity whenever creating something that is an aggregate of more primitive elements. A design is an overall plan for the combination and organization of more primitive elements.

16 Design Occurs at Different Levels
Three broad levels of design: High-level design or architecture where components are programs or subsystems Mid-level design where components are subsystems or classes Low level design were components are procedures, algorithms and data structures (Notice the overlap) Notice that the components at each level are aggregates of the components present one level deeper. o Classes are aggregates of algorithms and data structures o Subsystems are aggregates of classes o Systems are aggregates of subsystems Regardless of level, the concepts and principles are the same. There are components and communication between components. Mechanisms of communication: shared variables, method calls, wire protocols such as HTTP. Detailed design typically ends at the class level. Decisions about algorithms and data structures are typically made by programmers during the implementation phase. (Design at the point of construction.) Designs tend to evolve top-down with high-level architecture designs gradually being elaborated into lower-level detailed designs. Although, designs tend to evolve, good designers work at multiple levels of abstraction and detail simultaneously [Curtis, 1989]. Design occurs at different levels. The distinction between the levels is one of scale and purpose. High-level design or architecture is concerned with modules that define the overall system structure. These modules are typically subsystems and stand-alone programs. High-level design is where non-functional requirements are realized. Mid-level design is concerned with the design elements needed to support the architecture. When doing object-oriented design these design elements are typically classes and objects. Mid-level design is where functional requirements are realized. Low-level design is concerned with algorithms and data structures. What is a departure from high-level and mid-level design, low level design is concerned with computational efficiency rather than program flexibility, maintainability, reuse, etc. Standard Levels of Design

17 Characteristics of Software Design
Non-deterministic – A deterministic process is one that produces the same output given the same inputs. Design is non-deterministic. No two designers or design processes are likely to produce the same output. Heuristic – because design is non-deterministic design techniques tend to rely on heuristics and rules-of-thumb rather than repeatable processes. Emergent – the final design evolves from experience and feedback. Design is an iterative and incremental process where a complex system arises out of relatively simple interactions. Finding the average is a deterministic process. Finding a design is a non-deterministic process. In school we have a word for coming up with the same design given the same inputs: cheating. Software design is a heuristic rather than deterministic process. Any procedure process of design will include many rules-of-thumb.

18 The Evolution of Designs
Design as a single step in the software life cycle is somewhat idealized. More often the design process is iterative and incremental. Designs tend to evolve over time based on experience with their implementation. The design process is often ongoing and continuous. The complexity of software solutions makes it difficult to get their design correct on the first try. Designs tend to evolve over time based on experience with their implementation.

19 Elaboration and Transformation
Should this be Elaboration and *Invention*? Transformation = represents insight Design is nondeterministic. There is no step-by-step procedure that is guaranteed to deliver a good design. To many the transition between requirements and implementation can best be described as “and a miracle occurs here”. It’s impossible to completely eliminate the portion of design that requires intuition, invention (some might say, divine intervention) but most methods seek to minimize the portion of design that is non-analytical. The nondeterministic part is usually the transformation part when you view design as a mix of elaboration and transformation. [UI]  specifying the UI is usually optional.

20 The Benefits of Good Design
Good design reduces software complexity which makes the software easier to understand and modify. This facilitates rapid development during a project and provides the foundation for future maintenance and continued system evolution. It enables reuse. Good design makes it easier to reuse code. It improves software quality. Good design exposes defects and makes it easier to test the software. Complexity is the root cause of other problems such as security. A program that is difficult to understand is more likely to be vulnerable to exploits than one that is simpler.

21 A Generic Design Process
Understand the problem (software requirements). Construct a “black-box” model of solution (system specification). System specifications are typically represented with use cases (especially when doing OOD). Look for existing solutions (e.g. architecture and design patterns) that cover some or all of the software design problems identified. Design not complete? Consider using one or more design techniques to discover missing design elements Noun-verb analysis, CRC Cards, step-wise refinement, etc. Take final analysis model and pronounce a first-draft design (solution) model Consider building prototypes Document and review design Iterate over solution (Refactor) (Evolve the design until it meets functional requirements and maximizes non-functional requirements) Understand the Problem Create analysis models with the goal of better understanding problem domain and system requirements Look for Existing Solutions (Look for opportunities to use architecture and design patterns) Divine the Design Elaborate design solution Build Prototypes Document and Review the Design Evolve the Design Use design principles to guide decisions and evaluate evolving design forms Understand requirements Look for Existing Solutions Postulate a “white box” design solution. Compare this process with that of the top-down stepwise refinement approach sometimes advocated. (Just keep refining requirements until you have a solution.) Impractical for all but the simplest designs. Many decisions are made opportunistically. Noun verb analysis is a useful technique for finding objects/classes including their attributes and behaviors. RC cards are a simple technique for allocating behavior and validating a certain behavior allocation. CRC cards can help determine responsibilities and collaborations among objects. Notice the points of elaboration and transformation: Elaboration: use cases, analysis models, design models Transformation: (1) transforming analysis models into design models, (2) selecting and applying design patterns. Transformations require creativity and insight. You can test design solutions by “executing” use cases the way you would execute code to verify it is correct.

22 Inputs to the design process
User requirements and system specification (including any constraints on design and implementation options) Domain knowledge (For example, if it’s a healthcare application the designer will need some knowledge of healthcare terms and concepts.) Implementation knowledge (capabilities and limitations of eventual execution environment) Next up: Inputs and outputs (criteria for evaluating a design)

23 Desirable Internal Design Characteristics
Minimal complexity – Keep it simple. Maybe you don’t need high levels of generality. Loose coupling – minimize dependencies between modules Ease of maintenance – Your code will be read more often then it is written. Extensibility – Design for today but with an eye toward the future. Note, this characteristic can be in conflict with “minimize complexity”. Engineering is about balancing conflicting objectives. Reusability – reuse is a hallmark of a mature engineering discipline Portability – works or can easily be made to work in other environments High fan-in on a few utility-type modules and low-to-medium fan-out on all modules. High fan-out is typically associated with high complexity. Leanness – when in doubt, leave it out. The cost of adding another line of code is much more than the few minutes it takes to type. Stratification – Layered. Even if the whole system doesn’t follow the layered architecture style, individual components can. Standard techniques – sometimes it’s good to be a conformist! Boring is good. Production code is not the place to try out experimental techniques. Minimal complexity – Keep it simple. There will be plenty of essential complexities without adding additional accidental ones. eeb: Design for today but with an eye toward the future. – In the agile world there is the phrase You aint going to need it (YAGNI ). This good advice should be balanced with the idea that you can’t be sure you won’t need it and most programs do live on and are extended. The problem is predicting what changes will be needed in the future. eeb: when in doubt, leave it out. You do want to avoid speculative design.

24 That’s not the way I would have done it is not a criteria for evaluating a design
When evaluating a design some designers have a tendency to dismiss a design simply because it’s not what they would have done. The feeling could be a sign that some underlying design principle was violated or it could simply be a difference in personal preference. If a design is not what you would have done, look for principles of good design that have been violated. If you can’t find any, that suggests the design is OK (or you have discovered a new principle of good design), just not what you would have done. You can still offer an alternate design for consideration, but any criticism would be inappropriate. May hint at a deeper problem. Based on fundamental principles of good design.

25 Attributes of a design Static structure of system (components and their relationships) Interactions between components System data and its structure (database scheme) Physical packaging and distribution of components Algorithms Note, design models must describe static structure and dynamic behavior of system. Usually expressed with models from different view points.

26 Design Methods Design methods provide a procedural description for obtaining a design solution Most methods include: A representation part or notation for representing problem and intermediate forms of the design solution (usually from different view points). Examples: UML, pseudocode. Process part or procedures to following in developing the solution Heuristics – guidelines and best practices for making decisions and assessing intermediate and final results. Remember, design isn’t deterministic. Methods codify “knowledge about how to generate design solutions.” Examples range from the specific to the general: Booch method, OOD, Top-down stepwise refinement, bottom-up synthesis. Process generally involves transformation and elaboration steps or operations on the various representations or view points provided by the representation part. Q. Give examples of representation forms Models, text, pseudocode. Q. A method typically specifies a notation for representing the problem and design solution, a process for developing the solution and heuristics for making decisions and assessing candidate designs. Give a specific example for each of these three.

27 Design – Representational Forms
Class diagrams for static structure Sequence diagrams for dynamic behavior Textual and visual form of use cases are used to create and validate analysis and design representational forms Other UML models are also useful for understanding the problem and conceptualizing a solution (state machine diagram, activity diagram, etc.)

28 Design Representational Forms
Offers particular abstractions of the system from a certain perspective (viewpoint) Types of representational forms Visual models Text Pseudocode Design representations: Functional, static structural, dynamic behavioral, data modeling (database schema) Different viewpoints specialize in representing different aspects or properties of the system. There will be multiple representations or viewpoints of a design.

29 Evolution of Design Methods
Patterns play an important role in the design methods of today New paradigms in programming (eg. structured, object-oriented, patterns) tend to mature first in implementation, followed by design, followed by analysis. The evolution tends to be in reverse order relative to phases in the software life cycle because it’s hard to conceptualize the technology in one phase without being able to visualize its implementation or use in the next.

30 Methods and Patterns Methods and patterns are the principle techniques for dealing with the challenges of design They are useful for: Creating a design Documenting and communicating a design Transferring design knowledge and experience between practitioners Tools for codifying design knowledge and experience. Both incorporate the idea of reuse.

31 Patterns A design pattern is a reusable solution to a commonly occurring design problem Design patterns are adapted for the unique characteristics of the particular problem Just as there are levels of design, there are levels of design patterns: Architecture Styles/Patterns Design Patterns Programming Idioms It might not be a coincident that design patterns emerged shortly after oop became popular. OOP is more complex than procedural methods and less amendable to prescriptive design methods. Patterns go one better than methods in that they also convey design solutions to design problems and not just procedures for constructing a solution.

32 Design Methods Generic Specific
Structured System Analysis and Structured Design Object-Oriented Analysis and Design Specific Jackson System Development (JSD) DSDM JSD is a narrow design method in that it is designed for sequential data process. The result Is a pipe-and-filter architecture solution. Structured Analysis and Design Object-Oriented Analysis and Design Component-Based Software Engineering

33 Structured Analysis and Design
General representational forms: data flow diagram (DFD) and structure chart General design process: (1) model the system processes and information flow with a DFD, (2) transform the DFD into a hierarchical set of subprograms General heuristics: (1) use concepts of coupling and cohesion when deciding how to apportion responsibility among subprograms, (2) try to identify a central transform in the DFD (or create your own) such that all other processes can be subordinate to this transform in the resulting structure chart. The initial hierarchical set of subprograms has one procedure for each transformation or process in the DFD. Elaboration occurs on the initial DFD (leveling), and solution structure (hierarchical set of subprograms). Leveling the the process of elaborating a DFD. Each node is expanded into more detailed processes or nodes. Central transform is that part between input and output processing.

34 Design Strategies, Techniques and Tactics

35 Design Strategies Look for real-world objects Top-Down Decomposition
Bottom-Up Aggregation/Composition - start with what you know the system needs to do. The API you are using might dictate portions of the design. If you aren’t completely familiar with the API, bottom-up design might be the best place to start. Round-Trip Gestalt Organizational Influences on Design (More often limited to architecture, Conway’s Law) Transformation – usually reinterpretation using a different view point. Typically the step requiring the most creativity/knowledge/experience. Elaboration – usually from the same viewpoint. i.e. add more info to a model. Bottom-up – look for commonality among low-level objects. These suggest potential abstractions (abstract classes or interfaces). Both approaches have strengths and weaknesses.

36 Top-Down vs Bottom-up Design
Which approach is better (top-down or bottom up) if the implementation technologies are new (i.e. you have minimal experience with the programming language and/or environment)? From 1968 NATO Conference on SE: “There is probably no single ‘correct’ order in which to take a series of design decisions, though some orderings can usually be agreed to be better than others. Almost invariably some early decisions, thought at the time to have been clearly correct, will turn out to have been premature. There are two distinct approaches to the problem of deciding in what order to make design decisions. The ‘topdown’ approach involves starting at the outside limits of the proposed system, and gradually working down, at each stage attempting to define what a given component should do, before getting involved in decisions as to how the component should provide this function. Conversely the ‘bottom-up’ approach proceeds by a gradually increasing complexity of combinations of building blocks. The top-down approach is for the designer who has faith in his ability to estimate the feasibility of constructing a component to match a set of specifications. The opposite approach is for the designer who prefers to estimate the utility of the component that he has decided he can construct.” “Clearly the blind application of just one of these approaches would be quite foolish.” “The obvious danger in either approach is that certain features will be propagated through the layers and will finally cause trouble by proving undesirable and difficult to remove, when they should have been eliminated in the middle layers. … In practice neither approach is ever adopted completely; design proceeds from top and bottom, to meet somewhere in between, though the height of the meeting point varies with circumstances.” A: Bottom up. Top-down you have to have a pretty clear vision of how you plan to implement each component (from the top all the way down to lines of code).

37 Look for Real World Objects
Start with an object decomposition based on real-world objects. You can start the design phase by promoting analysis models to design models and evolve them as solution models. The objects’ role in the real world will suggest certain attributes and behaviors (operations). The “real-world” doesn’t necessarily imply tangibility. It might be the virtual world of a game. For example, a zombie qualifies as a “real world” object in a game.

38 Stepwise Refinement (this is really top-down programming)

39 Noun/Verb Analysis Classes and their associated behavior can be discovered in the narration in the requirements document that describes the requirements of the system. A very general guideline is that nouns indicate classes and verbs the operations on classes.

40 CRC Cards CRC stands for Class-Responsibility-Collaboration.
CRC cards are a very effective low tech way of identifying classes, responsibilities, and collaborations between classes.

41 Core Design Principles and Heuristics

42 The Importance of Managing Complexity
Humans have a very limited capacity for dealing with complexity directly. Findings from George Miller’s famous paper: “The Magical Number Seven, Plus or Minus Two” Expose the average person to the same stimuli at different levels (pitch, loudness, brightness, etc.) and he or she will be able to discriminate between about 7 different values. The capacity of short-term memory is about 7 items. When given a list of unrelated items, humans are about to recall about 7 of them. We can compensate for our limited cognitive abilities by employing techniques such as chunking (modularity), abstraction, information hiding, etc. Principles and heuristics of software design are used to manage complexity. They reflect human techniques for dealing with complexity. Software isn’t designed for computers. Computers don’t care how complex the solution is. Software is designed to make it easier for people to understand, modify and extend the software. People have a very limited capacity for dealing with complexity directly. We can compensate for our limited cognitive abilities by employing techniques such as chunking (modularity), abstraction, information hiding, etc. The same techniques can be applied to code to make it easier to understand and modify. Design is important because it is the primary means of managing complexity in software. The Limited Capacity of the Human Mind for Dealing with Complexity We are able to cope by organizing or grouping items into larger aggregate chunks. From the article: “the span of absolute judgment and the span of immediate memory impose severe limitations on the amount of information that we are able to receive, process, and remember. By organizing the stimulus input simultaneously into several dimensions and successively into a sequence or chunks, we manage to break (or at least stretch) this informational bottleneck. ” Someone learning Morris Code might start out perceiving chunks in the form of dit’s and dah’s. With some practice, he starts hearing words and possibly even short phrases. If software engineers are to understand a complex system they need a way of organizing it into chunks that make sense. Complexity isn’t an issue for the computer. To the computer, a complex program is no harder to execute than a simple one. Computers don’t have to “understand” the program in order to execute it. Complexity is a concern for the human minds responsible for writing and maintaining the program. “Complexity has always plagued human development as our short-term memories struggle to keep pace with the enormous cognitive demands of large, complicated systems.” “Our short-term memory limitations make it hard for us to get an accurate, integrated view of big systems.” “We get overwhelmed by detail and can’t comprehend all the requirements or behaviours needed to put the whole system together, particularly when any requirement may impact on others.” We need a way of dealing “with complexity and detail in small, localized pieces which avoids stretching our short-term memory beyond its capacity.” We need tools that help us amplify our ability to deal with complexity. Planning deals with abstractions. The motivation for controlling complexity is the fact that humans have a very limited capacity for dealing with complexity directly.

43 Modularity The goal of design is to partition the system into modules and assign responsibility among the components in a way that: High cohesion within modules, and Loose coupling between modules Modularity reduces the total complexity a programmer has to deal with at any one time assuming: Functions are assigned to modules in away that groups similar functions together (Separation of Concerns), and There are small, simple, well-defined interfaces between modules (information hiding) The principles of cohesion and coupling are probably the most important design principles for evaluating the effectiveness of a design. TBD: Read “How effective is modularization” In Making Software: What Really Works, and Why We Believe It Divide and Conquer Uses abstraction and information hiding to hide complexity behind simple interfaces. Partitioning of a program into subunits that are largely independent.

44 Coupling Coupling is the measure of dependency between modules. A dependency exists between two modules if a change in one could require a change in the other. The degree of coupling between modules is determined by: The number of interfaces between modules (quantity), and Complexity of each interface (determined by the type of communication) (quality) High coupling by itself isn't problem. High coupling only becomes a problem when it's combined with volatile or changing modules. How obvious is the coupling? Passing a parameter is obvious. Communicating through a global variable isn’t. “The coupling between two units of a software product is a measure of the degree of interaction between those units (Stevens et al., 1974) and, hence, of the dependency between the units. Although all types of coupling are sometimes useful in design, it has been demonstrated that some types have greater potential for introducing faults into software (Wulf and Shaw, 1973, Kafura and Henry, 1981, Troy and Zweben, 1981, Selby and Basili, 1991). Because some types of coupling are more likely to lead to faults than others, it is widely accepted that some coupling types should be limited in use.” From: Quality Impacts of Clandestine Common Coupling

45 Types of Coupling Content coupling (also known as Pathological coupling) Common coupling Control coupling Stamp coupling Data coupling Could be procedure to procedure or class to class. Notice there is syntactic and semantic coupling. Semantic coupling is a potentially more dangerous form of coupling. A compiler can catch changes in syntax. Compilers are no so good at catching changes in semantics (assertions can though). Image credit: Wikipedia (Software Coupling)

46 Content Coupling One module directly references the contents of another One module modifies the local data or instructions of another One module refers to local data in another One branches to a local label of another One of the absolute worst programming practices (when maintainability is a concern) is to have one module write code that is then executed.

47 Common Coupling Two or more modules connected via global data.
One module writes/updates global data that another module reads

48 Control Coupling One module determines the control flow path of another. Example: print(milesTraveled, displayMetricValues) . . . public void print(int miles, bool displayMetric) { if (displayMetric) { System.out.println(. . .); else { . . .} }

49 Stamp Coupling Passing a composite data structure to a module that uses only part of it. Example: passing a record with three fields to a module that only needs the first two fields.

50 Data Coupling Modules that share data through parameters.

51 Coupling between CSS and JavaScript
A well-designed web app modularizes around: HTML files which specify data and semantics CSS rules which specify the look and formatting of HTML data JavaScript which defines behavior/interactivity of page Assume you have the following HTML and CSS definitions.

52 HTML: CSS: Output: coupling-example.html default.css
<!doctype html> <html> <head> <script type="text/javascript" src="base.js"></script> <link rel="stylesheet" href="default.css"> </head> <body> <button onclick="highlight2()">Highlight</button> <button onclick="normal2()">Normal</button> <h1 id="title" class="NormalClass">CSS <--> JavaScript Coupling</h1> </body> </html> coupling-example.html .NormalClass { color:inherit; font-style:normal; } default.css

53 Suppose you want to change the style of the title in response to user action (clicking on a button).
This is behavior or action so it must be handled with JavaScript. Evaluate the coupling of the following two implementation options. Both have the same behavior.

54 Option A JavaScript code modifies the style attribute of HTML element.
function highlight() { document.getElementById("title").style.color="red"; document.getElementById("title").style.fontStyle="italic"; } function normal() { document.getElementById("title").style.color="inherit"; document.getElementById("title").style.fontStyle="normal"; base.js

55 Option B JavaScript code modifies the class attribute of HTML element.
function highlight() { document.getElementById("title").className = "HighlightClass"; } function normal() { document.getElementById("title").className = "NormalClass"; base.js Scroll down for answer Option B has less coupling. With option A, CSS style values are mixed with JavaScript code. With option B the JavaScript code changes a class value of an element. The definition of the class (style rules) is contained in the .css file. .NormalClass { color:inherit; font-style:normal; } .HighlightClass { color:red; font-style:italic; default.css

56 Cohesion Cohesion is a measure of how strongly related the functions or responsibilities of a module are. A module has high cohesion if all of its elements are working towards the same goal.

57 Cohesion and Coupling The best designs have high cohesion (also called strong cohesion) within a module and low coupling (also called weak coupling) between modules.

58 Benefits of high cohesion and low coupling
Modules are easier to read and understand. Modules are easier to modify. There is an increased potential for reuse Modules are easier to develop and test. 1. Modules are easier to read and understand. High cohesion means fewer unrelated data and functions to get in the way while studying a module. Low coupling means fewer dependent modules that must be understood while studying a module. 2. Modules are easier to modify. Naturally modules that are easier to read and understand will be easier to modify but beyond that low coupling tends to isolate changes. With low coupling a change in one area is less likely to have a ripple effect and require changes in other areas of the system. 3. There is an increased potential for reuse. High cohesion and low coupling makes it easier to reuse modules without dragging along other functionality that isn't needed. (Refer to figure above. Only one interface has to be supported/supplied in new environment for module in the upper right hand corner.) 4. Modules are easier to develop and test. Highly cohesive modules can be developed by a single person or small group of developers. Low coupling enables the system to be delivered in increments. Twenty percent of software defects are related to interface problems. [Kan, et. al., S/W Quality: An Overview from the Perspective of Total Quality Management, IBM Systems Journal, 33(1) p14.]

59 Relationship between Coupling and Cohesion
High cohesion within modules often correlates with low coupling between modules and vise versa. Modules with diverse responsibilities (low cohesion) are likely to be accessed more often and from more places than modules with a singular focus. Cohesion is related to coupling the same way high turnover in a company is related to low employee morale. They are two different concepts that tend to be highly correlated. When there is low morale, people tend to leave. People leaving tends to drag down morale (watching people come and go can be disheartening and lead to more work for those who stay).

60 Abstraction Abstraction is a concept used to manage complexity
An abstraction is a generalization of something too complex to be dealt with in its entirety Abstraction is for humans not computers Abstraction is a technique we use to compensate for the relatively puny capacity of our brains (when compared to the enormous complexity in the world around us) There aren’t enough neurons (or connections) in our brain to process the rich detail around us during a single moment in time Successful designers develop abstractions and hierarchies of abstractions for complex entities and move up and down this hierarchy with splendid ease See file in dir: AbstractionResearch.pdf. It has info on medical (neurological basis for design). Also, see latest wikipedia article. Has new info. Suppressing or ignoring some properties in favor of others. A procedure is an abstraction: sin(double angle). A class is an abstraction: class Stack { public void push(Element e)…. “Another very powerful tool developed by all living organisms and perfected by humans is abstraction. The word "abstraction" has the same root as subtraction. Abstracting means subtracting non-essential features. ” “Abstraction is one of the fundamental ways that we as humans cope with complexity.”[Booch OOA and design with applications, p44] Abstraction – ignore irrelevant details for whatever the current purpose. “By hiding irrelevant details, abstractions reduces conceptual complexity, making it possible for the programmer to focus on a manageable subset of the program text at any particular time. Subroutines are control abstractions.” =================== Compare someone just learning to drive with someone more experienced. Someone new to driving hasn’t learned the abstractions. When you drive you think in terms of abstractions (road, intersection, etc) and move up and down abstraction hierarchy as events unfold. Intersection – lights – color of light. Objects along side road – pedestrian along side road – pedestrian moving from sidewalk to road. See a police car – check speed. The number technical challenge in SE is managing complexity. The number one tool for dealing with complexity is abstraction. High level languages are indispensible for dealing with complexity. They are also about the last application independent layer of abstraction. It’s up to you the programmer to provide any additional layers. The most common is a layered architecture with lower layers providing an API for writing the current and future applications. For example, you might define a collect of domain objects and build your application as a configuration of shared domain objects. The following is from [ “Computer systems are machines of large complexity. This complexity can be mastered intellectually by one tool only: Abstraction. A language represents an abstract computer whose objects and constructs lie closer (higher) to the problem to be represented than to the concrete machine. For example, in a high-level language we deal with numbers, indexed arrays, data types, conditional and repetitive statements, rather than with bits and bytes, addressed words, jumps and condition codes. However, these abstractions are beneficial only, if they are consistently and completely defined in terms of their own properties. If this is not so, if the abstractions can be understood only in terms of the facilities of an underlying computer, then the benefits are marginal, almost given away. If debugging a program - undoubtedly the most pervasive activity in software engineering – requires a “hexadecimal dump”, then the language is hardly worth the trouble.”

61 Form Consistent Abstractions
“Abstraction is the ability to engage with a concept while safely ignoring some of its details.” Base classes and interfaces are abstractions. i.e Scene, SceneItem The interface defined by a class is an abstraction of what the class represents A procedure defines an abstraction of some operation. “The principle benefit of abstraction is that it allow you to ignore irrelevant details.” It’s all about hiding irrelevant details. (Irrelevant at certain times.)

62

63

64 Information Hiding Information hiding is a design principle
The information hidden can be data, data formats, behavior, and more generally, design decisions When information is hidden there is an implied separation between interface and implementation. The information is hidden behind the interface Parnas encourages programmers to hide “difficult design decisions or design decisions which are likely to change” The clients of a module only need to be aware of its interface. Implementation details should be hidden

65 Information Hiding [Cont]
Want to hide design and implementation decisions—especially those likely/subject to change. Information hiding implies encapsulation and abstraction. You are hiding details which creates an abstraction. When skillfully applied, information hiding has the effect of hiding complexity. Hide XNA’s procedures and API’s for playing sound. Settings hides information about program constants. Q. Why isn’t Settings subject to the problems commonly associated with global data? (See answer below) const string ContentPath = “GameName/Content”; A. Global data is a problem with there is uncontrolled reads and writes on the global data. It’s a problem when one part of a program operations on global data without taking into account how another part of the program are doing the same. Global read only variables aren’t a problem. Global data with controlled access (methods that modify the data) aren’t a problem when designed well.

66 Example 1: Evaluate the following design in terms of information hiding
class PersistentData { public ResultSet read(string sql); // write returns the number of rows effected public int write(string sql); } Sample Client Code PersistentData db = new PersistentData(); db.write(“UPDATE Employees SET Dependents = WHERE EmployeeID = 47”); What information is hidden? Logic to connect to DB, sessions, session pooling, etc. Does it hide brand of DB? To a certain extent it does, however SQL isn’t all that standard.

67 Example 1: Evaluate the following design in terms of information hiding
class EmployeeGateway { public static EmployeeGateway find(int ID); public void setName(string name); public string getName(); public void setDependents(int dependents); public int getDependents(); // insert() returns ID of employee public int insert(); public void update(); public void delete(); } Sample Client Code EmployeeGateway e = EmployeeGateway.find(47); e.setDependents(2); e.update(); Implementation could be relational database, flat file, serialized object, etc.

68 Example 2: Evaluate the following class design in terms of information hiding
class Course { private Set students; public Set getStudents() { return students; } public void setStudents(Set s) { students = s; Could it do a better job of information hiding? Could it have been worse? (scroll down for the answer) It could have been worse. It could have made students a public field. As it is, the class controls access to references to its internal data structure. However, the current implementation is not much better than making students a public field. As specified, clients can get a reference to the internal data structure student and modify this data structure outside the control and purview of the class Course.

69 Example 2: Improved Information Hiding
class Course { private Set students; public Set getStudents() { return Collections. unmodifiableSet(students); } public void addStudent(Student student) { students.add(student); public void removeStudent(Student student) { students.remove(student); This class design offers better encapsulation and information hiding. The class Course has complete control over the content of its internal data structure. Alternately, getStudents() could return a non-updatable iterator [Fowler, UML Distilled].

70 Why Practice Information Hiding?
Hiding complexity – limiting the amount of information you have to deal with at any one time Reducing dependencies on design and implementation decisions to minimize the impact of changes. (Avoid the ripple effect of changes.) “Large programs that use information hiding were found …to be easier to modify—by a factor of 4—than programs that don’t.[Korson and Vaishnavi 1986]

71 Encapsulation Encapsulation is an implementation mechanism for enforcing information hiding and abstractions. There is no clear widely accepted definition of encapsulation. It can mean: A grouping together of related things (records, arrays) A protected enclosure (object with private data and/or methods) Good definition of encapsulation in: Encapsulation and Inheritance in OO Programming Languages, Snyder.

72 Supporting Design Principles and Heuristics
Because design isn’t a deterministic process we have heuristics or rules-of-thumb rather than precise steps. Role of principles and heuristics: they are used to guide decisions and assesse intermediate results. Heuristics arrive from trial and error with practice. These heuristics tend to produce designs with the desirable characteristics listed early—primarily managing complexity.

73 Don’t Repeat Yourself (DRY)
In general, every piece of knowledge should have a single, unambiguous, authoritative representation within a system. Most programmers will recognize this principle as it applies to coding: you shouldn’t have duplicate code (e.g. cutting and pasting code or its close cousin: copy-paste-modify). More generally, it applies to documentation, test cases, test plans, etc. Repeating yourself invites maintenance problems. You have two or more locations that have to be kept synchronized/consistent. Code generators can be used to eliminate the duplication of specification. The generated code doesn't have to conform to the DRY principle.

74 Principle of Least Astonishment/Surprise (POLA)
The POLA is probably more applicable during UI design, but is also relevant during software design. In short, don’t surprise the user (UI design) or programmer (software design) with unexpected behavior. Users and developers should be able to rely on their intuition. Most web users expect clicking on the icon in the upper left-hand corner of a web page will take them to the home page of the web site. It would be a surprise if it did anything else. During software design use descriptive names for variables, methods and classes. The name of a method should reflect what it does. The name of a variable should reflect the use or meaning of the data it holds. Putting business logic in a class called Settings would be a violation of the POLA. Most programmers would expect a class called Settings to contain constants only. Before you can apply the POLA to UI design, you have to know the user well enough to know what would surprise him or her. This is another reason it is important to get to know the user as part of understanding requirements. It’s important to pay attention to the POLA when deciding on defaults. Defaults should be what is most common or most sensible.

75 Separation of Concerns
The functions, or more generally concerns, of a program should be separate and distinct such that they may be dealt with on an individual basis. Separation of concerns helps guide module formation. Functions should be distributed among modules in a way that minimizes interdependencies with other modules. Example: many web applications are structured around the 4-tier web architecture: Presentation or UI Business Logic Data Access Database (typically relational) Each layer encapsulates a related set of related functions. “Typically, concerns are synonymous with features or behaviors” The visitor pattern improves separation of concerns. MVC is another good example of Separation of Concerns. The 7 layers of the OSI networking protocol are defined based on the separation of concerns (physical addressing, path determination, reliable end-to-end connection, etc). For example, the first version of HTML mixed content with presentation. (Example: <p font=“Arial”>Hello</p>) These are clearly two different responsibilities. Mixing the two (violating the SoC) makes it harder to update one without causing collateral damage to the other. For example, if the two are mixed, it’s rather easy for a content expert unfamiliar with the subtle rules of HTML to inadvertently break presentation tags when he or she only meant to modify the content.

76 Single Responsibility Principle (SRP)
SRP is a subtle variation on the concept of cohesion. A cohesive module is one where all the elements of the module are functionally related (separation of concerns). A module that conforms to the SRP is one that has a single reason to change. The SRP defines a responsibility as a reason to change. Example: let’s say you had a custom UI control that also included code needed to save it’s state when the app loses focus. If the UI control doesn’t have any event handling code you could argue it is a good example of separation of concerns. (It has one concern: display view.) However, there are two reasons it might change: (1) view changes, (2) change in storage method used to persist state. “Martin defines a responsibility as a reason to change, and concludes that a class or module should have one, and only one, reason to change.” Standard approach: verify cohesion by making sure contents of module are related. Then ask yourself, “what are the reasons this module might change?”. If there is more than one reason, consider dividing it into two or more modules. The visitor design pattern addresses a specific type of low cohesion and class structure that doesn’t abide by the SRP. Consider adding an example that shows a design with low cohesion and class with multiple responsibilities refactored using the visitor design pattern into a design with high cohesion and top marks for abiding by the SRP. A class should have only one reason to change. Using CSS to separate content from presentation conforms to the SRP. Reasons for changing content and presentation of the content are likely to be disjoint. For example, you might have a module responsible for calculating and printing a financial report. The module appears to be cohesive because it’s function is to produce a report. It doesn’t abide by the SRP because there are two reasons it might change: The algorithm used to calculate the values changes The format of the report changes Design is about finding the best compromise among sometimes competing objectives. Document view design pattern is often used in place of the pure MVC design pattern. Document = Model View = View and Controller (two responsibilities) Note, the Active Record architecture pattern violates the SRP because it has two different responsibilities: persistent storage and business logic. This doesn’t mean AR is a bad architecture pattern, it means principles are only guidelines or rules-of-thumb. Use AR when the business logic is simple and the overhead of separating the two responsibilities is greater than the risk assumed by keeping them together.

77 Open-Closed Principle (OCP)
Simply stated, the OCP says that modules (functions, classes, etc) should be open for extension but closed for modification. Translation: it should be possible to extend the function/behavior of a module without having to modify its code. Example 1: a quiz authoring system with an abstract base class (or interface) QuizQuestion and subclasses TrueFalseQuestion, MultilpeChoiceQuestion, etc. The abstract class QuizQuestion meets the criteria of the OCP. Adding a new subclass of QuizQuestion will change the behavior of QuizQuestion from the perspective of code that references objects of type QuizQuestion. Example 2: Web browser plug-in architecture. You can add support for a new media type without making major changes to existing code. From wikipedia entry for visitor pattern: “A practical result of this separation is the ability to add new operations to existing object structures without modifying those structures. It is one way to easily follow the open/closed principle.” Consider using the Visitor design pattern as an example of Separation of concerns, OCP and SRP. Note, no module can be 100% closed. When a module is closed for modification it means it is possible to extend the behavior of the module in certain ways without needing to modify the code of the module. It doesn’t mean all possible extensions are possible without a code change. For example, you might be able to add a MultipleAnswerQuestion subclass to QuizQuestion without needing to make any modifications to QuizQuestion, but what about adding a fill in the blank or essay type question? Certain advanced question types (those that haven’t been anticipated) could very well cause code changes in QuizQuestion. So, the goal is to design classes that are closed for changes that are most likely to occur. Identifying these changes requires thorough requirements work, expert judgment and a little bit of luck. “You'll never reach true Open Closed nirvana, but you can inch much closer by rigorously following the related Single Responsibility Principle: a class should have one, and only one, reason to change” [ *** TBD: Consider adding Chain of Responsibility Pattern to patterns notes *** A single change to a program shouldn’t result in a cascade of changes to dependent modules. References: Bertrand Meyer and Robert Martin.

78 OCP [Cont] Categories in Objective-C allow you to extend the behavior of classes while remaining compliant with the OCP. (You can actually add methods to a class at runtime.) Delegation is another pattern for designing a class that is open for extension but closed for modification. The examples above are in addition to inheritance discussed on the previous slide.

79 OCP Using Delegation [TBD: change the theme of this example to Professor / GTA. The Professor delegates the grade() method to instances of GTA.] Delegation example: AccountType is the delegate. Account delegates the responsibility for deposit() to its instance of AccountType. Account is open for extension because you can change its deposit() behavior by initializing it with a different instance of AccountType. It is closed for modification because you can change Account’s behavior without making changes to the code within Account. Q and A: Modify the following design to make it conform to the open/closed principle. (Have a Client class that creates an instance of a Server object. The modification would be to pass an instance of an abstract Server class to the Client class and have the Client class make calls to Server objects. Basically the example in Move to delegate design pattern discussion: If you are tempted to use inheritance for implementation reuse only, consider using delegation instead.

80 Final Classes can abide by O/C

81 Dependency Inversion Principle (DIP)
The DIP formalizes the general design concept of Inversion of Control (IoC). Both are sometimes called the Hollywood Principle because they describe a phenomena where the reused component embraces the Hollywood cliché, “Don't call us, we'll call you.”

82 Dependency Inversion Principle (DIP)
There are two parts to the DIP: High-level modules should not depend on low-level modules. Both should depend on abstractions. Abstractions should not depend upon details. Details should depend upon abstractions. Example: many class libraries have a factory method for creating an XML parser. At runtime a specific implementation of a parser is provided to application code requesting a parser. High level modules depend on the abstract interface to the parser. Detailed implementation of the parser depends only on the abstract interface it implements.

83 Example Imagine a programming language, let’s call it Java, that didn’t want to implement an XML parser but instead wanted to make community-written parsers available to programmers. One option is to ask the community to submit parsing classes and then make these classes available to programmers. Programmers would write code that looked like: IBMParser p = new IBMParser(); p.parse(inputStream); The obvious problem with this is the dependency between client code and utility code. The consequences are a little more severe since it is a class from a outside source, but this type of dependency is common between client code and utility classes.

84 Example [Cont] Another option is to define an abstract base class for XML parsers, let’s call it DocumentBuilder, and have vendors implement their parsers as subclasses of this abstract base class. The important point is this abstract class is owned by the language designers. The code supplied by XML parser vendors is dependent on this abstract class. With this new arrangement, the dependency is reversed or inverted. This conforms to the DIP: High-level modules should not depend on low-level modules. Both should depend on abstractions. Abstractions should not depend upon details. Details should depend upon abstractions.

85 Inversion of Control and Frameworks
Much of programming today consists of extending existing frameworks rather than writing traditional procedural programs that retain responsibility for the control flow of an application. With traditional procedural programming, control is passed from the operating system to the main entry point of the program. Control may pass temporarily to library subroutines, but the main program has complete control and sole responsibility for the sequence of activities that comprise the application. Difference between frameworks and libraries. Two strategies for reuse. Inversion of Control is what distinguishes a framework from a library. Examples: ASP.NET, Java Servlets, Java Applets, Android Activities,

86

87 Frameworks Programming with frameworks inverts the locus of control. The main thread of control resides in the framework rather than your code. Your code is hooked into the framework and called by the framework as needed. With procedural programming, every routine you write (except the main entry point) is called from your code. When extending a framework, many of the routines you write are only called by the framework. At first it might seem strange to have routines in your code with no apparent caller.

88 Note, the code being reused is the code in the framework (and of course library routines).
The Template Method design pattern also exemplifies inversion of control. Inversion of control: you aren’t doing the calling. Your code is waiting to be called.

89 Program to an interface, not an implementation
There are considerations for the client and service. Service Client interface ILogger { void log(String); } abstract class ALogger { abstract void log(String); class Logger { f1(ILogger l); f2(ALogger l); f3(Logger l); f1( ), programming to an interface, is the most flexible, general and reusable. It will work for any class that implements the ILogger interface. This is a potentially large group of classes because classes can implement multiple interfaces. f2() and f3() are more limited. f3() only works for classes and subclasses of type Logger. f2() only works for subclasses of type ALogger (not much different). The one advantage of ALogger and Logger is you can add additional methods without (immediately) breaking clients. If you add a method to an interface, it will immediately break all clients of that interface.

90 Interface segregation principle (ISP)
In short, the message of the ISP is: avoid “fat” interfaces. An interface is considered “fat” if it attracts clients interested in only a portion of the methods offered by the interface and different clients are interested in different portions of the interface. Notice relation to single responsibility principle (SRP). This knife would be inconvenient to carry because most of the time you only need a few of its features. Over time, you might need all the features, but for any one event you are likely to only need to use a few of the features. The other features get in the way and complicate the use of the tool. Bad because when eating a meal with the fork and spoon features you might pinch yourself with the tweezers feature.

91

92 Interface segregation principle (Cont.)
The suggested alternative to “fat” interfaces, is to define multiple “skinny” interfaces each with a small group of methods that appeal to different classes of clients. Interfaces should be cohesive, that is, focused on one thing. Clients should not be forced to implement or depend on portions of an interface they don’t use

93 Example Java has two interfaces for mouse events, one for common mouse events (MouseListener) and one for motion events (MouseMotionListener). Grouping all events into one interface would have violated the ISP. interface MouseListener {   void mouseClicked(MouseEvent e);   void mousePressed(MouseEvent e);   void mouseReleased(MouseEvent e);   void mouseEntered(MouseEvent e);   void mouseExited(MouseEvent e); } interface MouseMotionListener {   void mouseDragged(MouseEvent e);   void mouseMoved(MouseEvent e); }

94 SOLID Principles of Object-Oriented Design
S – Single responsibility principle O – Open/closed principle L – Liskov substitution principle I – Interface segregation principle D – Dependency inversion principle SOLID is a mnemonic acronym for remembering five basic principles of object-oriented programming and design.

95 Consider Design by Contract
Formalize class contracts. You an define the services of a routine in terms of pre- and post-conditions. This makes it very clear what to expect.

96 Try Design for Testing Create a test-friendly design
A test-friendly module is likely to exhibit other important design characteristics. Example: you would avoid circular dependencies. Business logic will be better isolated from UI code if you have to test it separately from the UI

97 Don’t overlook brute force as an option
Sometimes its better to use an inelegant design if the cost of a better design is prohibitive. You can also encapsulate it behind a well-designed interface.

98 Consider Experimental Prototyping
“Sometimes you can’t really know whether a design will work until you better understand some implementation detail.” SM recommends starting with a specific question that you then answer with by writing the “absolute minimum amount of throwaway code.” Need to be disciplined about how you go about experimental prototyping and how you use the results. There is a strong temptation to start writing production code. I see experimental prototyping more like riffing where riff is defined as “The term is also used in a similar sense in comedy where riffing may be the verbal exploration of a particular subject.” [Wikipedia] Technology and personnel change so quickly that on most projects you don’t have the luxury of being an expert with the implementation technologies. Experimental prototyping can help you better understand aspects of the implementation technology that is likely to affect the design.

99 API Design and Use TBD: Add this topic. Consider:

100 Stop here

101 Design Knowledge and Skills
Ability to understand and use design methods, patterns, principles, heuristics and techniques. This knowledge is useful for: Creating designs Evaluating or assessing designs Ability to communicate designs and design ideas Architect to programmer (communicate implementation specs) Among colleagues (propose a design) Among practitioners (share experience and expertise) This knowledge and skill Communication is needed to (1) communicate design ideas to development team, (2) discuss and communicate design ideas to colleagues during the design phase, (3) communication as part of learning design practice (transfer experience and expertise among practitioners). Methods usually have a process part (transformations between representational forms), a representational part (descriptive forms), and heuristics (general rules of thumb). We are moving away from methods and more toward arch styles and patterns which are more general. Methods provide strategic guidance, but also might be accused of constraining creativity. Ability to work opportunistically.

102 Design Qualities Fitness for purpose. Satisfies product requirements
Reliability Robustness Efficiency Usability Maintainability Evolvability Reusability [Move this to lesson on architecture. The illities of software are system-wide. They apply more at the architecture level than the design level. Usability is affected by UI design more than anything else.] Criteria for judging a good design (assessing quality of a design) Desirable characteristics of design.

103 References How to Design a Good API and Why it Matters, ACM.
[read later]

104 Common Subsystems Business rules UI
Database access – having a data access component allows the business logic and other components to deal with data in the form or at the level of abstraction as it exists in the problem domain. The data access layer isolates the rest of the program from the details of how the data is actually stored. Very few business people view their data in terms of tables and relationships. System dependencies – isolate hardware and other environmental dependencies.

105 Anticipate Change Start during requirements by documenting potential changes and their likelihood. Areas of code likely to change should be isolated. (e.g. hidden in a class.) Architect around stable ideas. Avoid putting volatile ideas in interfaces. Anticipating changes is not the same as designing ahead. Anticipating change guides design decisions. It doesn’t justify new code that’s not needed at the moment. Heuristic: group things together that are likely to change at the same time.

106 Areas Most Likely To Change
Business rules Hardware dependencies – keyboard, controller, file system. Input / Output Difficult areas of code – sections of code done poorly are likely to change in the future. Data-size Constraints

107 Divide and Conquer Even the best minds can’t hope to fully understand any but the most trivial software programs. They shouldn’t have to. A good design is one that allows an individual with average capabilities to fully understand the program by looking at it in pieces. “The goal of all software design techniques is to break a complicated problem into simple pieces.” You should be able to focus on one module nearly independently of others. (Loose Coupling) it’s easier to understand a complex entity when it is broken down in terms of smaller simpler entities. (As long as the relationships between the entities aren’t too complex.)

108 Designers often have to deal with competing priorities
Performance versus Readability/Maintainability Reusability/Extensibility versus understandability <All quality attributers> versus Cost & Schedule Game programmers often trade understandability, maintainability, <just about everything else> for performance and time-to-market. Priorities drive tradeoff decisions The best solutions balance competing priorities

109 References Tbd: Object-Oriented Design Heuristics, Arthur J. Riel (Author);

110 Fragile Brittle designs Design is an investment. Devoting some effort to design at the start of development means less time for feature implementation but the effort spent on design at the start of development is more than offset by the reduced cost of adding features later in the development cycle. On a project with a poor design it is often a race to complete the project before the complexity of the software grows to such a magnitude that further changes or additions are impossible. When players recognize the problem early they face a tough decision. Spend time fixing the design in the hopes that a better design will enable faster progress or hope that they can “out run” the growth in complexity and finish before all progress grinds to a halt. Most go with forging ahead. Figure x illustrates the benefits of good design. Good design minimizes software complexity. With a poorly designed program, the cost of adding another feature (or making a code change) escalates rapidly. On some projects it is a race against time to complete the project before complexity becomes overwhelming and no further changes are possible. When a project fails for technical reasons it is very often because a poor design allowed complexity to grow When a project fails for technical reasons it’s usually because of uncontrolled complexity. As complexity grows it becomes increasing difficult to understand the existing system and therefore make changes. Left unchecked, progress grinds to a halt. The number one technical challenge in software development is managing complexity One of the most important obligations of developers is managing the intellectual complexity of software solutions. Projects don’t often fail for technical reasons, but when they do fail for technical reasons it’s usually because of uncontrolled complexity. Good design minimizes complexity. The importance of having a sound design increases with the size and complexity of the software system.

111 From: The Design of Design: Essays from a Computer Scientist.
“One motivation for study is the wide gaps, in every design discipline, between best practice and average practice, and between average practice and semi-competent practice. Much of design cost, often as much as a third, is rework, the correction of mistakes. ... Design is important…” “So, it was reasoned, systematizing the design process would raise the level of average practice, and it has.” “Team design is increasingly the norm for complex artifacts. Teams are often geographically dispersed. Designers are increasingly divorced from both use and implementation—typically they no longer can build with their own hands the things they design. All kinds of designs are now captured in computer models instead of drawings. Formal design processes are increasingly taught, and they are often mandated by employers.” There is no science of design. Inspiration and experience reign. “Are there invariant properties of the design process itself, properties that hold across a wide range of media of design?” ““The Oxford English Dictionary defines the verb design as To form a plan or scheme of, to arrange or conceive in the mind for later execution” “The essentials of this definition are plan, in the mind, and later execution. Thus, a design (noun) is a created object, preliminary to and related to the thing being designed, but distinct from” “Design starts with an idea or the  “formulation of the conceptual constructs” “For most human makers of things, the incompletenesses and inconsistencies of our ideas become clear only during implementation”

112 Kinds of Coupling Simple data-parameter coupling – passing primitive data types: f(int,float). No global data. Simple object coupling – a module instantiates another object. Object-parameter coupling – one module passes another module an object rather than a primitive type: f(SomeClass). Higher coupling than simple-data-parameter Semantic coupling – one module makes use of semantic information about another module. Could be procedure to procedure or class to class. Notice there is syntactic and semantic coupling. Semantic coupling is a potentially more dangerous form of coupling. A compiler can catch changes in syntax. Compilers are no so good at catching changes in semantics (assertions can though).

113 Semantic Coupling Semantic coupling is depending on knowledge of how something is observed to work. Things are a little better if how it works is a documented part of the interface or behavior. Examples: Module1 passes a control flag to module2. The control flag affects processing in module2. Module1 makes assumptions about internal workings of module2. Module2 uses global data after it has been modified by Module1. You know you are suppose to call initialize() before calling f() but you use special knowledge of the class to conclude that in some cases you can skip calling initialize() before calling f(). Control flag example: you might pass an object and a flag that says “delete after use” You start to depend on a side effect of calling a method which isn’t guaranteed to be there in the future.


Download ppt "Software Design "You can use an eraser on the drafting table or a sledgehammer on the construction site.“ --Frank Lloyd Wright Software design is an important."

Similar presentations


Ads by Google