Software Engineering: Design Concepts and Principles
Software Engineering: Design Concepts and Principles
SOFTWARE ENGINEERING
Design Concepts and Principles
Design Engineering
2
Design principles and philosophy guides the designer Understand Design concepts designing Goal of design engineering is to produce a model or representation that is
Bug free (firmness) Suitable for its intended uses (commodity) Pleasurable to use (delight)
Design: Design is a meaningful engineering representation of some software to be built. A design should be traceable to the customer's requirements. Design transforms requirements model into design models that describe the details of the data structures, system architecture, interfaces, and components. Review: Each design product should be reviewed for quality before moving to implementation.
Data design
Architectural design
Interface design
Communication between software components Communication with other systems Communication with human users
Procedural descriptions of software components using information obtained from the PSPEC, and STD
Be readable and understandable guide for those who write code, test components, or maintain
Provide a complete picture (data, function, behavior) of the software from an implementation perspective Exhibit good architectural structure Be modular Contain distinct representations of data, architecture, interfaces, and components (modules)
Lead to data structures that are appropriate for the objects to be implemented and be drawn from recognizable design patterns Lead to components that exhibit independent functional characteristics Lead to interfaces that reduce the complexity of connections between modules and with the external environment Be derived using a repeatable method that is driven by information obtained during software requirements analysis
Design Principles
8
Design Principles
1.
2.
The design process should not suffer from "tunnel vision." A good designer should consider alternative approaches, judging each based on the requirements of the problem, the resources available to do the job, and the design concepts. The design should be traceable to the analysis model. Because a single element of the design model often traces to multiple requirements, it is necessary to have a means for tracking how requirements have been satisfied by the design model.
3.
4.
The design should not reinvent the wheel. Systems are constructed using a set of design patterns, many of which have likely been encountered before. These patterns should always be chosen as an alternative to reinvention. The design should minimize the intellectual distance between the software and the problem as it exists in the real world. That is, the structure of the software design should copy the structure of the problem domain.
5.
6.
The design should be uniform and integrated. A design is uniform if it appears that one person developed the entire thing. Rules of style and format should be defined for a design team before design work begins. The design should be structured to accommodate change. The design concepts (discussed next) enable a design to achieve this principle.
The design should be structured to accommodate in any condition. Well-designed software should be designed to accommodate unusual circumstances, and if it must terminate processing, do so in a graceful manner. 8. Design is not coding, coding is not design. Even when detailed procedural designs are created for program components, the level of abstraction of the design model is higher than source code. The only design decisions made at the coding level address the small implementation details that enable the
7.
9.
10.
The design should be assessed for quality as it is being created, not after the fact. A variety of design concepts are available to assist the designer in assessing quality. The design should be reviewed to minimize conceptual (semantic) errors. There is sometimes a tendency to focus on details when the design is reviewed, missing the forest for the trees. A design team should ensure that major conceptual elements of the design (omissions, ambiguity, inconsistency) have been addressed before worrying about the syntax of the design
About Design
15
There are two ways of constructing a software design. One way is to make it so simple that there are obviously no deficiencies, and other way is to make it so complicated that there are obviously no deficiencies. The first method is for more difficult. (C.A.R. Hoare)
Abstraction
2.
3. 4. 5. 6. 7. 8. 9. 10. 11.
Refinement
Modularity Software Architecture Control Hierarchy Data Structure Software Procedure Information Hiding Patterns Refactoring Functional Independence
1. Abstraction
17
The psychological notion of "abstraction" permits one to concentrate on a problem at some level of generalization without regard to irrelevant low level details
Abstraction is one of the fundamental ways that we as humans cope with complexity. Grady Booch Forms of Abstraction
Procedural abstraction: named sequence of events Data abstraction: named collection of data objects Control abstraction: control mechanism without specifying internal details
Encapsulation is the grouping of related ideas into one unit, which can thereafter be referred to by a single name.
2. Refinement
18
Process of elaboration where the designer provides successively more detail for each design component Stepwise Refinement
1969
A top-down design strategy originally proposed by Niklaus Wirth Article published in 1971
Please enter the number of integer values: 5 Please enter 5 integer values. 0 1 2 3 4 Please enter 5 integer values. 5 6 7 8 9 0 + 5 = 5 1 + 6 = 7 2 + 7 = 9 3 + 8 = 11 4 + 9 = 13 Sum = 45
main
ReadArraySize
CreateArray
ReadArray
AddArrays
SumArray
PrintArrays
PrintSum
DestroyArrays
PrintRow
DestroyArray
int main() { int *x, *y, *z, sum, size; size = ReadArraySize(); x = CreateArray(size); y = CreateArray(size); z = CreateArray(size); ReadArray(x, size); ReadArray(y, size); AddArrays(x, y, z, size); sum = SumArray(z, size); PrintArrays(x, y, z, size); PrintSum(sum); DestroyArrays(x, y, z); return 0;
20
int ReadArraySize() { int size; cout << "Please enter the number of integer values: "; cin >> size; return size; }
int* CreateArray(int size) { int* ptr; ptr = new int[size]; return ptr; } void ReadArray(int array[], int length) { cout << "Please enter " << length << " integer values." << endl; for (int i=0; i<length; i++) cin >> array[i]; }
void AddArrays(int x[], int y[], int z[], int size) { for (int i=0; i<size; i++) z[i] = x[i] + y[i]; }
21
void PrintRow(int a, int b, int c) { cout << a << "\t+\t" << b << "\t=\t" << c << endl; } void PrintArrays(int x[], int y[], int z[], int size) { for (int i=0; i<size; i++) PrintRow(x[i], y[i], z[i]); } void PrintSum(int sum) { cout << "Sum = " << sum << endl; } int SumArray(int array[], int size) { int sum=0; for (int i=0; i<size; i++) sum += array[i]; return sum; } void DestroyArray(int* ptr) { delete[] ptr; } void DestroyArrays(int x[], int y[], int z[]) { DestroyArray(x); DestroyArray(y); DestroyArray(z); }
22
3. Modularity
23
Modularity
The degree to which software can be understood by examining its components independently of one another
Modularity is the single attribute of software that allows a program to be intellectually manageable Module Interface: float sqrt(float value);
Decomposability
Composability
Understandability
Continuity
Protection
4. Software Architecture
25
Architecture is a comprehensive framework that describes its form and structure, its components and how they fit together. (Jerrold Grochow)
Software Architecture
1.
2.
3.
Structural properties. This aspect of the architectural design representation defines the components of a system (e.g., modules, objects, filters) and the manner in which those components are packaged and interact with one another. Extra-functional properties. The architectural design description should address how the design architecture achieves requirements for performance, capacity, reliability, security, adaptability, and other system characteristics. Families of related systems. The architectural design should draw upon repeatable patterns that are commonly encountered in the design of families of similar systems. In essence, the design should have the ability to reuse architectural building blocks.
The architectural design can be represented using one or more of different models:
Structural models
Framework models
Increase the level of design abstraction by attempting to identify repeatable architectural design frameworks (patterns) that are encountered in similar types of applications
Dynamic models
Address the behavioral aspects of the program architecture, indicating how the structure or system configuration may change as a function of external events.
Process models
Focus on the design of the business or technical process that the system must accommodate.
Functional models
5. Control Hierarchy
28
implies a control hierarchy, but does not represent the procedural aspects of the software (e.g. event sequences)
Span of control: Number of levels of control within a software product Depth: Distance between the top and bottom modules in program control structure Fan-out or width: Number of modules directly controlled by a particular module Fan-in: Number of modules that control a particular module Visibility: Set of program components that may be called or used as data by a given component Connectivity: Set of components that are called directly or are used as data by a given component)
6. Data structure
30
Representation of the logical relationship among individual data elements (requires at least as much attention as algorithm design)
7. Software Procedure
31
Precise specification of processing (event sequences, decision points, repetitive operations, data organization/structure) Procedure is layered
8. Information Hiding
32
Information (data and procedure) contained within a module is inaccessible to modules that have no need for such information
every module in the [...] decomposition is characterized by its knowledge of a design decision which it hides from all others. Its interface or definition was chosen to reveal as little as possible about its inner workings. (Prof. David L. Parnas)
internal mechanisms of the component can be improved without impact on other components.
Information hiding hides those program components from other classes which are likely to change.
9. Patterns
34
A pattern is a named nugget(piece) of insight which conveys the essence of a proven solution to a recurring problem within a certain context amidst competing concerns. (Brad Appleton)
Design Patterns
Reusable
Patterns
10. Refactoring
35
Refactoring is the process of changing a software system in such as way that it does not alter the external behavior of the code [design] yet improves its internal structure. (Martin Fowler)
Agile Development
Functional independence is achieved by developing modules with single minded function and an aversion to excessive interaction with other modules. Leads to
Coupling Cohesion
Functional independence
Cohesion
Qualitative indication of the degree to which a module focuses on just one thing
Coupling
Qualitative indication of the degree to which a module is connected to other modules and to the outside world
Second design is Hard to understand Hard to locate faults Difficult to extend or enhance Cannot be reused in another product
minimal
Composite/Structured Design
40
Module cohesion
Module coupling
Cohesion
41
Degree of interaction within a module Seven categories or levels of cohesion (non-linear scale)
1. Coincidental Cohesion
42
A module has coincidental cohesion if it performs multiple, completely unrelated actions void threeinone(float val, float& sin, char *str, float& tax) { code for computing sin of val code for reversing str Code for computing tax }
Task A
Task B
Task C
Module
2. Logical Cohesion
44
A module has logical cohesion when it performs a series of related actions, one of which is selected by the calling module
{
if (op == 1) return x+y; if (op == 2) return x-y; if (op == 3) return x*y;
Task A
Task B
Task C
Module
The interface is difficult to understand Code for more than one action may be intertwined Difficult to reuse
3. Temporal Cohesion
46
A module has temporal cohesion when it performs a series of actions related in time
main() { Queue q; Stack s; s.top = -1; s.size = 100; q.rear = q.front = -1; q.size = 200; q.create(); s.create(); s.push(10); q.insert(s.pop()); }
Task A (t0)
Task B (t0+1)
Task C(t0+2)
Module
Actions of a module are weakly related to one another, but strongly related to actions in other modules.
Not reusable
4. Procedural Cohesion
48
A module has procedural cohesion if it performs a series of actions related by the procedure to be followed by the product
Task A (t0)
Task B (t0+1)
Task C(t0+2)
Module
void conv(int x) {
bits[right] = temp;
left++, right--;
} // print for (i=0; i<BITS; i++) cout << bits[i];
49
5. Communicational Cohesion
50
A module has communicational cohesion if it performs a series of actions related by the procedure to be followed by the product, but in addition all the actions operate on the same data Task A (t0) Task B (t0+1) Task C(t0+2)
Module
Data
6. Functional Cohesion
51
Fault isolation
7. Informational Cohesion
52
A module has informational cohesion if it performs a number of actions, each with its own entry point, with independent code for each action, all performed on the same data structure Essentially, this is an abstract data type (Class)
Data
Entry
Task A
Entry
Exit
Task B
Exit
Entry
Task C
Exit
Coupling
53
No dependencies
High coupling makes modifying parts of the system difficult, e.g., modifying a component affects all the components to which the component is connected. Ripple Effect
Coupling
54
1. Content Coupling
55
Two modules are content coupled if one directly references contents of the other Examples
Module A refers to local data of module B in terms of some numerical displacement within B
Module A branches into local label of module B
Almost any change to B, even recompiling B with new compiler or assembler, requires change to A
2. Common Coupling
56
Two modules are common coupled if they have write access to global data
zero
int x, y, z; void zero() { x=y=0; } void add() { z=x+y; } void loop() { while(z) cout << z--; }
add
Global Variables
loop
All modules must be read to find out a single module does Difficult to reuse Module exposed to more data than necessary
3. Control Coupling
58
{
if (op == 1) return x+y;
if (op == 2) return x-y; if (op == 3) return x*y; if (op == 4) return x/y;
Two modules are control coupled if one passes an element of control to the other
Examples:
Operation code passed to module with logical cohesion Control-switch passed as argument
Why Is Control Coupling So Bad? Modules are not independent; module b (the called module) must know internal structure and logic of module a.
Affects reusability
4. Stamp Coupling
59
Two modules are stamp coupled if a data structure is passed as a parameter, but the called module operates on some but not all of the individual components of the data structure
Many languages support passing of data structures
It is not clear, without reading the entire module, which fields of a record are accessed or changed
There is nothing wrong with passing a data structure as a parameter, provided all the components of the data structure are accessed and/or changed
transpose_matrix (Matrix &m);
5. Data Coupling
61
Two modules are data coupled if all parameters are homogeneous data items [simple parameters, or data structures all of whose elements are used by called module] Examples
int sumAll(int array[], int size); void multiply_matrix(Matrix &a, Matrix &b, Matrix &result); int distance(Point a, Point b);
The difficulties of content, common, control, and stamp coupling are not present Maintenance is easier
Evaluate the first iteration of the program structure to reduce coupling and improve cohesion. Attempt to minimize structures with high fan-out; strive for fan-in as structure depth increases. Keep the scope of effect of a module within the scope of control for that module. Evaluate module interfaces to reduce complexity, reduce redundancy, and improve consistency. Define modules whose function is predictable and not overly restrictive (e.g. a module that only implements a single sub-function). Strive for controlled entry modules, avoid pathological connection (e.g. branches into the middle of another module)
Program structures
64
Requir ements specif ication Design acti ities v Architectur al design Abstr act specif ication Interface design Component design Data structur e design Algorithm design
System architectur e
1.
Select an architectural pattern appropriate to the software based on the analysis model Partition the analysis model into design subsystems, design interfaces, and allocate analysis functions (classes) to each subsystem Examine domain model and design appropriate data structures for data objects and their attributes
2.
3.
4.
Review task analysis (use cases) Specify action sequences based on user scenarios Define interface objects and control mechanisms Review interface design and revise as needed
Specify algorithms at low level of detail Refine interface of each component Define component level data structures Review components and correct all errors uncovered
5.
6.