Software Engineering
Lecture: Modular Design
Thomas Fritz
Many thanks to Philippe Beaudoin, Gail
Murphy, David Shepherd, Neil Ernst and
Meghan Allen
Reading!
For next lecture: (all required)
Composite Design Pattern
http://sourcemaking.com/design_patterns/composite
Mediator Design Pattern
http://sourcemaking.com/design_patterns/mediator
Faade Design Pattern
http://sourcemaking.com/design_patterns/facade
Modular Design Overview
Introduction to Modularity
Principles and Heuristics for good Modularity
High Cohesion
Loose Coupling
Information Hiding
Open/Closed Principle
Liskov Substitution Principle
Law of Demeter
Learning Goals
By the end of this unit you will be able to:
Critique a UML diagram and provide concrete
suggestions of how to improve the design
Explain the goal of a good modular design and why it is
important
Apply the following design-principles appropriately: high
cohesion, loose coupling, principle of least knowledge,
Liskov substitution principle, information hiding,
open/closed principle
4
Recap: Bad Design Activity
Web site slow or error-prone / crashes ?
5
Software Design Modularity
The goal of all software design techniques is to
break a complicated problem into simple pieces.
6
Modular Design
Why Modularity?
Why Modularity?
Minimize Complexity
Reusability
Extensibility
Portability
Maintainability
10
What is a good modular Design?
There is no right answer with design
Applying heuristics/principles can provide
insights and lead to a good design
11
Principles & Heuristics for modular
Design
High Cohesion
Loose Coupling
Information Hiding
Open/Closed Principle
Liskov Substitution Principle
Law of Demeter
.
12
Discussion question
Which of these two designs is better?
A.public class AddressBook {
private LinkedList<Address> theAddresses;
public void add (Address a)
{theAddresses.add(a);}
// ... etc. ...
}
B. public class AddressBook
extends LinkedList<Address> {
// no need to write an add method, we inherit it
}
13
Design
Principles
14
High Cohesion
Cohesion refers to how closely the functions in a
module are related
Modules should contain functions that logically
belong together
Group functions that work on the same data
Classes should have a single responsibility
(no schizophrenic classes)
15
High or low cohesion?
public class EmailMessage {
public void sendMessage() {}
public void setSubject(String subj) {}
public void setSender(Sender sender) {}
public void login(String user, String passw) {}
.
}
16
Loose Coupling
Coupling assesses how tightly a module is
related to other modules
Goal is loose coupling:
modules should depend on as few modules
as possible
Changes in modules should not impact other
modules; easier to work with them separately
17
Tightly or loosely coupled?
from Alverson (UW)
18
Tightly or loosely coupled?
from Alverson (UW)
19
Information Hiding
A good class is a lot
like an iceberg: seveneights is under water,
and you can see only
the one-eight thats
above the surface.
from CodeComplete by Steve McConnell
20
Information Hiding
Only expose necessary
functions
Abstraction hides complexity
by emphasizing on essential
characteristics and
suppressing detail
Caller should not assume
anything about how the
interface is implemented
Effects of internal changes
are localized
21
Information Hiding: Example 1
The chief scientist of the elementary particle research
lab asks the new intern about his latest results: So
what is the average momentum of these neutral
particles?
a) 42
b) Hmmm. Take this pile of sheet with my
observations, here is the textbook that explains how to
calculate momentum, also you will need to search
online for the latest reference tables. Oh, and dont
forget to correct for multiplicity!
Which answer is the most likely to get the intern fired?
22
Information Hiding: Example 2
Class DentistScheduler has
A public method automaticallySchedule()
Private methods:
whoToScheduleNext()
whoToGiveBadHour()
isHourBad()
To use DentistScheduler, just call
automaticallySchedule()
Dont have to know how its done internally
Could use a different scheduling technique: no
problem!
23
Open/Closed Principle
Classes should be open for extensions
Classes should be closed for change
It is often desirable to modify the behavior of a class
while reusing most of it
Modifying the source code of a class risks breaking
every other class that relies on it
Achieved through inheritance and
dynamic binding
24
Open/Closed and Information Hiding
Modifying the source code of a class risks
breaking every other class that relies on it
However, information hiding says that we should
not assume anything about implementation
So is there a need to keep classes closed for
change?
Yes because the implied behavior should never
change!
Inherit to reuse an interface while changing the
behavior
25
Open/Closed Example
class Drawing {
public void drawAllShapes(List<IShape> shapes) {
for (IShape shape : shapes) {
if (shape instanceof Square()) {
drawSquare((Square) shape);
} else if (shape instanceof Circle) {
drawCircle((Circle) shape));
}}}
private void drawSquare(Square square) { // draw the square }
private void drawCircle(Circle square) { // draw the circle }
}
26
Open/Closed Example
class Drawing {
public void drawAllShapes(List<IShape> shapes) {
for (IShape shape : shapes) {
shape.draw();
}}}
interface IShape {
public void draw();
}
class Square implements IShape {
public void draw() { // draw the square }
}
27
Open/Closed Caveat
nice in theory, but in practice deriving a class to
modify its behavior is not always best thing to do
however, it becomes increasingly important to
adhere to the open/closed principle as classes
mature and are more and more relied upon
some classes are not meant to be reusable, so
the Open/Closed principle doesnt apply
28
Liskov Substitution Principle
An object of a superclass should always be
substitutable by an object of a subclass
Subclass has same or weaker preconditions
Subclass has same or stronger postconditions
Derived methods should not assume more or
deliver less
29
Liskov Substitution Principle
Example
Rectangle
Square
Reasonable to derive a square from a rectangle?
see http://www.objectmentor.com/resources/articles/lsp.pdf
30
LSP Example Rectangle & Square
class Rectangle {
private double fWidth, fHeight;
public void setWidth(double w) { fWidth = w; }
public void setHeight(double h) { fHeight = h; }
public double getWidth() { return fWidth; }
public double getHeight() { return fHeight; }
31
LSP Example Rectangle & Square
class Square extends Rectangle {
public void setWidth(double w) {
super.setWidth(w);
super.setHeight(w);
}
public void setHeight(double h) {
super.setHeight(h);
super.setWidth(h);
}
}
32
LSP Example Rectangle & Square
// somewhere else
public void calculate(Rectangle r) {
r.setWidth(5);
r.setHeight(6);
assert(r.getWidth() * r.getHeight() == 30);
}
// somewhere else
Rectangle r = new Square();
calculate(r);
33
LSP Example Rectangle & Square
Postcondition for Rectangle setWidth() method
assert((fWidth == w) && (fHeight == old.fHeight));
Square setWidth() has weaker postcondition
does not conform to (fHeight == old.fHeight)
Square has stronger preconditions
Square assumes fWidth == fHeight
In other words
Derived methods assume more and deliver less.
34
LSP Continued
LSP shows that a design can be structurally consistent
(A Square ISA Rectangle)
But behaviourally inconsistent
So, we must verify whether the pre and postconditions in
properties will hold when a subclass is used.
It is only when derived types are completely substitutable
for their base types that functions which use those base
types can be reused with impunity, and the derived types
can be changed with impunity.
35
Law of Demeter
(a.k.a. Principle of Least Knowledge)
Assume as little as possible about
other modules
Restrict method calls to your
immediate friends
Only talk to your friends
36
Law of Demeter for classes
Method M of object O should only call methods
of:
O itself
Ms parameters
Any object created in M
Os direct component objects
Single dot rule
a.b.method() breaks LoD
a.method() does not
37
Class Activity
Which principle is violated?
a) 52 different import statements at the top of a Java file
b) public final class Bird { }
c) Point x = body.getCenterOfMassPos();
Vec s = body.getCenterOfMassSpeed();
Vec a = body.getCenterOfMassAcceleration();
a = a + force * body.getMass();
s = s + a * dt;
x = x + s * dt;
body.setCenterOfMassPos(x);
body.setCenterOfMassSpeed(s);
body.setCenterOfMassAcceleration(a);
38
Which principle is violated?
d) public class Road extends Highway { }
e) rect.setHeight( 52 );
// Following line is not needed because setHeight updates maximum
height
// rect.setMaxHeight(52);
f) public class System {
public void changeCarSpeed();
public void changeCarColor();
public void changeHighwayMaxSpeed();
public void updatePoliceCarPosition();
};
g) public class Kid extends Person {
// Inherited from parent class. Does nothing because kids
// do not know how to Reason
public void Reason() {} }
39
Class Activity Revisiting Your Design
Examine your class diagrams and check for the
design principles in your design
Did you violate any
Did you use any, which ones and why
Be able to articulate which principles you used
and why!
40
Modular Design Summary
Goal of design is to manage complexity by
decomposing problem into simple pieces
Many principles/heuristics for modular design
Strong cohesion, loose coupling
Call only your friends
Information Hiding
Open/Closed Principle
Hide details, do not assume implementation
Open for extension, closed for modification
Liskov Substitution Principle
Subclass should be able to replace superclass
41