DesignPrinciples

advertisement
Design Principles
Hans Van Vliet, Software Engineering, Principles and Practice, 3rd
edition, John Wiley & Sons, 2008. Section 12.1.
Robert C. Martin, Clean Code: A Handdbook of Agile Software
Craftsmanship, Prentice Hall, 2008.
Joshua Bloch, Effective Java, 2nd edition, Addition Wesley, 2008.
CS 4311
1
Outline
Why?
Encapsulate what varies
 Minimize access of members
 Favor composition over inheritance
 Program to interface
 Open-closed principle
 Liskov substitution principle

CS 4311
2
Abstraction

“Abstraction arises from a recognition of similarities between certain
objects, situations, or processes in the real world, and the decision
to concentrate upon those similarities and to ignore for the time
being the differences.” Tony Hoare

“An abstraction denotes the essential characteristics of an object
that distinguish it from all other kinds of objects and thus provide
crisply defined conceptual boundaries, relative to the perspective of
the viewer.” Grady Booch

Q: Examples in programming/code?
Q: Why abstraction?

CS 4311
3
Encapsulation

“Encapsulation is the process of compartmentalizing the elements
of an abstraction that constitute its structure and behavior;
encapsulation serves to separate the contractual interface of an
abstraction and its implementation.” Grady Booch

“Encapsulation is a mechanism used to hide the data, internal
structure, and implementation details of an object. All interaction
with the object is through a public interface of operations.” Craig
Larman

Q: Language constructs that support encapsulation?
Q: Why encapsulation?

CS 4311
4
Minimize the Accessibility of
Classes and Members



Classes should be opaque
Classes should not expose their internal implementation details
Use getters and setters
Compare
public double speed;
vs
private double speed;
public double getSpeed() { return speed; }
public void setSpeed(double speed) { this.speed = speed; }
CS 4311
5
Minimize the Accessibility of
Classes and Members



Classes should be opaque
Classes should not expose their internal implementation details
Use getters and setters
Compare
public double speed;
Is this better?
Why?
vs.
private double speed;
public double getSpeed() { return speed; }
public void setSpeed(double speed) { this.speed = speed; }
CS 4311
6
Advantages of Minimizing Accessibility
Centralized checking of constraints
 Add useful side-effects (monitoring)
 Decouple internal representation from
function:

 Improved
encapsulation
 Reduced coupling of components
CS 4311
7
Outline
Encapsulate what varies
 Minimize access of members
 Favor composition over inheritance
 Program to interface
 Open-closed principle
 Liskov substitution principle

CS 4311
8
Composition vs. Subclassing




Two different methods for code reuse
Delegation (prototype) vs inheritance (OO)
Black box vs. white/grey box reuse
Q: pros and cons? which one to use?
A
CS 4311
B
A
B
9
Composition

B
Method of reuse in which new functionality is obtained by
creating an object composed of other objects


A
New functionality obtained by delegating to objects being
composed
Sometimes called aggregation or containment



CS 4311
Aggregation - when one object owns or is responsible for
another object and both objects have identical lifetimes (GoF)
Aggregation - when one object has a collection of objects that
can exist on their own (UML)
Containment - a special kind of composition in which the
contained object is hidden from other objects and access to the
contained object is only via the container object (Coad)
10
Advantages of Composition






Contained objects are accessed by the containing class
solely through their interfaces
"Black-box" reuse, since internal details of contained
objects are not visible
Good encapsulation
Fewer implementation dependencies
Each class is focused on just one task (cohesion)
The composition can be defined dynamically at run-time
through objects acquiring references to other objects of
the same type
CS 4311
11
Disadvantages of Composition


Resulting systems tend to have more objects
Interfaces must be carefully defined in order to
use many different objects as composition
blocks
CS 4311
12
Inheritance



A
B
Method of reuse in which new functionality is obtained by
extending the implementation of an existing object
The generalization class (superclass) explicitly captures
the common attributes and methods
The specialization class (subclass) extends the
implementation with additional attributes and methods
CS 4311
13
Advantages of Inheritance


New implementation is easy, since most of it is
inherited
Easy to modify or extend the implementation
being reused
CS 4311
14
Disadvantages of Inheritance




Breaks encapsulation, since it exposes a subclass to
implementation details of its superclass
"White-box" reuse, since internal details of superclasses
are often visible to subclasses
Subclasses may have to be changed if the
implementation of the superclass changes
Implementations inherited from superclasses can not be
changed at runtime.
CS 4311
15
Example (from Effective Java by Bloch)

Variant of HashSet that tracks number of insertions
public class InstrumentedHashSet<E> extends HashSet<E> {
private int addCount = 0; // The number of attempted element insertions
public InstrumentedHashSet() { super(); }
public boolean add(E e) {
addCount++; return super.add(e); }
public boolean addAll(Collection<? extends E> c) {
addCount += c.size(); return super.addAll(c); }
public int getAddCount() { return addCount; }
}
CS 4311
16
Example

Variant of HashSet that tracks number of insertions
public class InstrumentedHashSet<E> extends HashSet<E> {
private int addCount = 0; // The number of attempted element insertions
public InstrumentedHashSet() { super(); }
public boolean add(E e) {
addCount++; return super.add(e); }
public boolean addAll(Collection<? extends E> c) {
addCount += c.size(); return super.addAll(c); }
public int getAddCount() { return addCount; }
}
CS 4311
Keep track of
insertions
Call super
constructor
17
Example

Variant of HashSet that tracks number of insertions
public class InstrumentedHashSet<E> extends HashSet<E> {
private int addCount = 0; // The number of attempted element insertions
public InstrumentedHashSet() { super(c); }
public boolean add(E e) {
addCount++; return super.add(e); }
When we
add, update
the counter
public boolean addAll(Collection<? extends E> c) {
addCount += c.size(); return super.addAll(c); }
public int getAddCount() { return addCount; }
}
CS 4311
18
Example: Test It---Add!
public class InstrumentedHashSetTest {
private InstrumentedHashSet<String> set;
@Before public void setUp() {
set = new InstrumentedHashSet<String>();
}
@Test public void testAdd() {
assertEquals(0, set.getAddCount());
set.add("CS4311"); assertEquals(1, set.getAddCount());
set.add("Fun"); assertEquals(2, set.getAddCount());
}
% java org.junit.runner.JUnitCore InstrumentedHashSetTest
JUnit version 4.10
.
Time: 0
OK (1 test)
CS 4311
19
Example: Test It---AddAll!
public class InstrumentedHashSetTest {
…
@Test public void testAddAll() {
set.addAll(Arrays.asList(new String[] {"CS4311", "Fun"}));
assertEquals(2, set.getAddCount());
}
% java org.junit.runner.JUnitCore InstrumentedHashSetTest
JUnit version 4.10
..E
Time: 0.015
There was 1 failure:
1) testAddAll(InstrumentedHashSetTest)
java.lang.AssertionError: expected:<2> but was:<4>
at …
FAILURES!!!
Tests run: 2, Failures: 1
CS 4311
20
Example: Test it---AddAll!
public class InstrumentedHashSetTest {
…
@Test public void testAddAll() {
set.addAll(Arrays.asList(new String[] {"CS4311", "Fun"}));
assertEquals(2, set.getAddCount());
}
Test success or
% java org.junit.runner.JUnitCore InstrumentedHashSetTest
failure?
JUnit version 4.10
What went
..E
wrong?
Time: 0.015
There was 1 failure:
1) testAddAll(InstrumentedHashSetTest)
java.lang.AssertionError: expected:<2> but was:<4>
at …
FAILURES!!!
Tests run: 2, Failures: 1
CS 4311
21
Example: Investigate It!

Look at the API document, e.g., JDK 8 says
public boolean addAll(Collection<? Extends E> c) of AbstractCollection
Adds all of the elements in the specified collection to this collection (optional
operation). …This implementation iterates over the specified collection, and
adds each object returned by the iterator to this collection, in turn. …

Look at the source code.



The internal implementation of addAll() in the superclass
superclass invokes the add() method!
First we add 2 to addCount in InstrumentedHashSet’s
addAll().
Then we invoke super’s addAll().


CS 4311
For each element, this addAll() invokes the add() method,
which as overridden by InstrumentedHashSet adds one for each
element.
22
Example: Investigate It!

Look at the API document, e.g., JDK 7 says
public boolean addAll(Collection<? Extends E>
c) of AbstractCollection
Need
to know the
implementation
Adds all of the elements in the specified collection
to this collection (optional
operation). …This implementation iterates over
the specified
and
details
to getcollection,
this
adds each object returned by the iterator to this collection, in turn.
right!

Look at the source code.



The internal implementation of addAll() in the superclass
superclass invokes the add() method!
First we add 2 to addCount in InstrumentedHashSet’s
addAll().
Then we invoke super’s addAll().


CS 4311
For each element, this addAll() invokes the add() method,
which as overridden by InstrumentedHashSet adds one for each
element.
23
Example: Fix It!

Variant of HashSet that tracks number of insertions
public class InstrumentedHashSet<E> extends HashSet<E> {
private int addCount = 0; // The number of attempted element insertions
public InstrumentedHashSet() { super(c); }
public boolean add(E e) {
addCount++; return super.add(e); }
public boolean addAll(Collection<? extends E> c) {
addCount += c.size(); return super.addAll(c); }
public int getAddCount() { return addCount; }
}
CS 4311
24
Example: Fix It!

Variant of HashSet that tracks number of insertions
public class InstrumentedHashSet<E> extends HashSet<E> {
private int addCount = 0; // The number of attempted element insertions
public InstrumentedHashSet() { super(c); }
public boolean add(E e) {
addCount++; return super.add(e); }
Any concerns?
Hint: Program to
interface
public boolean addAll(Collection<? extends E> c) {
addCount += c.size(); return super.addAll(c); }
public int getAddCount() { return addCount; }
}
CS 4311
25
Example: Composition



Let’s write an InstrumentedSet class that is composed
of a Set object.
Our InstrumentedSet class will duplicate the Set
interface, but all Set operations will actually be forwarded
to the contained Set object.
The contained Set object can be an object of any class
that implements the Set interface (and not just a
HashSet)
InstrumentedSet
CS 4311
Set
26
Example
public class InstrumentedSet<E> implements Set<E> {
private final Set<E> s;
private int addCount = 0;
public InstrumentedSet() { s = new HashSet<E>(); }
public boolean add(E e) {
addCount++; return s.add(e);
}
public boolean addAll(addAll(Collection<? Extends E> c) {
addCount += c.size(); return s.addAll(c);
}
public int getAddCount() { return addCount; }
CS 4311
27
Example: Forwarding methods
public void clear() { s.clear(); }
public boolean contains(E e) { return s.contains(e); }
public boolean isEmpty() { return s.isEmpty(); }
public int size() { return s.size(); }
public Iterator iterator() { return s.iterator(); }
public boolean remove(E e) { return s.remove(e); }
public boolean containsAll(Collection<? Extends E> c) { return s.containsAll(c); }
public boolean removeAll(Collection<? Extends E> c) { return s.removeAll(c); }
public boolean retainAll(Collection<? Extends E> c) { return s.retainAll(c); }
public E[] toArray() { return s.toArray(); }
public E[] toArray(E[] a) { return s.toArray(a); }
public boolean equals(E e) { return s.equals(e); }
public int hashCode() { return s.hashCode(); }
public String toString() { return s.toString(); }
CS 4311
28
Coad's Rules
Use inheritance only when all of the following
criteria are satisfied:





A subclass expresses “is a special kind of” and not “is a
role played by a.”
An instance of a subclass never needs to become an
object of another class.
A subclass extends, rather than overrides or nullifies, the
responsibilities of its superclass.
A subclass does not extend the capabilities of what is
merely a utility class.
For a class in the actual Problem Domain, the subclass
specializes a role, transaction or device.
CS 4311
29
Outline
Encapsulate what varies
 Minimize access of members
 Favor composition over inheritance
 Program to interface
 Open-closed principle
 Liskov substitution principle

CS 4311
30
Program to an Interface, not an
Why?
Implementation

Q: interface vs. implementation?
A sorted set class provides a “choose” operation that returns an
arbitrary element contained in the set. You learned that the
operation always returns the smallest element of the set. Is this an
interface or implementation feature?
<<uses>>
Client
service
user
service
provider
X
LinkedList
CS 4311
<<interface>>
List
ArrayList
31
Interface





Interfaces express types in a limited way.
An interface is the set of methods one object knows that
it can invoke on another object.
An object can have many interfaces.
Different objects can have the same type and the same
object can have many different types.
An object is known by other objects only through its
interface.
CS 4311
32
Example
public class InstrumentedSet<E> implements Set<E> {
private final Set<E> s;
private int addCount = 0;
public InstrumentedSet(Set<E> s) { this.s = s; }
Which lines?
public boolean add(E e) {
addCount++; return s.add(e);
}
public boolean addAll(addAll(Collection<? Extends E> c) {
addCount += c.size(); return s.addAll(c);
}
public int getAddCount() { return addCount; }
CS 4311
33
Example
public class InstrumentedSet<E> implements Set<E> {
private final Set<E> s;
private int addCount = 0;
public InstrumentedSet(Set<E> s) { this.s = s; }
Which lines?
public boolean add(E e) {
addCount++; return s.add(e);
}
public boolean addAll(addAll(Collection<? Extends E> c) {
addCount += c.size(); return s.addAll(c);
}
public int getAddCount() { return addCount; }
CS 4311
34
Advantages:






Clients are unaware of the specific class of the object
they are using.
One object can be easily replaced by another.
Object connections need not be hardwired to an object
of a specific class, thereby increasing flexibility
Loosens coupling.
Increases likelihood of reuse
Improves opportunities for composition since contained
objects can be of any class that implements a specific
interface
CS 4311
35
Outline
Encapsulate what varies
 Minimize access of members
 Favor composition over inheritance
 Program to interface
 Open-closed principle
 Liskov substitution principle

CS 4311
36
Open/Closed Principle

Software should be open for extension,
but closed for modification
CS 4311
37
Open/Closed Principle

The Open-Closed Principle (OCP) says that we should attempt to design
modules that never need to be changed.

To extend the behavior of the system, we add new code. We do not modify
old code.

Modules that conform to the OCP meet two criteria:

Open For Extension - The behavior of the module can be extended to meet new
requirements.
 Closed For Modification - the source code of the module is not allowed to
change.

How can we do this?






CS 4311
Abstraction
Polymorphism
Inheritance
Interfaces
Design patterns such as template methods
New programming paradigms or languages such as AOP and AspectJ
38
Open/Closed Principle

Consider the following method that totals the price of each part in the
specified array of parts of some class:
public double totalPrice(Part[] parts) {
double total = 0.0;
for (Part p: parts) {
total += p.getPrice();
}
return total;
}

If Part is a base class or an interface and polymorphism is being used,
then this class can easily accommodate new types of parts without
having to be modified!

It conforms to the OCP.
CS 4311
39
Open/Closed Principle

Suppose the Accounting Department decrees that motherboard
parts and memory parts should have a premium applied when
figuring the total price.
public double totalPrice(Part[] parts) {
double total = 0.0;
for (Part p: parts) {
if (p instanceof Motherboard)
total += (1.45 * p.getPrice());
else if (p instanceof Memory)
total += (1.27 * p.getPrice());
else
total += p.getPrice();
}
return total;
}
CS 4311
40
Open/Closed Principle

Suppose the Accounting Department decrees that motherboard
parts and memory parts should have a premium applied when
figuring the total price.
public double totalPrice(Part[] parts) {
double total = 0.0;
for (Part p: parts) {
if (p instanceof Motherboard)
total += (1.45 * p.getPrice());
else if (p instanceof Memory)
total += (1.27 * p.getPrice());
else
total += p.getPrice();
}
return total;
}
CS 4311
Is this
OK?
41
Open/Closed Principle

Suppose the Accounting Department decrees that motherboard
parts and memory parts should have a premium applied when
figuring the total price.
public double totalPrice(Part[] parts) {
double total = 0.0;
for (Part p: parts)
{
Does
not conform to OCP.
if (p instanceof Motherboard)
total += (1.45 * p.getPrice());
else if (p instanceof
Memory) must be changed
totalPrice()
total += (1.27 * p.getPrice());
whenever Accounting
else
total +=changes
p.getPrice(); pricing policy.
}
return total;
}
What could we do instead?
CS 4311
42
Open/Closed Principle

A better idea is to have a PricePolicy class which can be used to provide
different pricing policies:
public class Part {
private double price;
private PricePolicy pricePolicy;
Part
PricePolicy
public void setPricePolicy(PricePolicy pricePolicy)
{ this.pricePolicy = pricePolicy; }
public void setPrice(double price) { this.price = price; }
public double getPrice() { return pricePolicy.getPrice(price); }
}
CS 4311
43
Open/Closed Principle
/**
* Class PricePolicy implements a given price policy.
*/
public class PricePolicy {
private double factor;
public PricePolicy (double factor) { this.factor = factor; }
public double getPrice(double price) {
return price * factor;
}
}
CS 4311
44
Open/Closed Principle
Pricing policies can be set
dynamically by changing the
/**
PricePolicy object.
* Class PricePolicy implements a given price policy.
Multiple pricing policy classes
*/
may exist.
public class PricePolicy {
private double factor;
public PricePolicy (double factor) { this.factor = factor; }
public double getPrice(double price) {
return price * factor;
}
}
CS 4311
45
Pair; 2 minutes

An example of OCP application from
standard libraries or frameworks like JDK?
CS 4311
46
Outline
Encapsulate what varies
 Minimize access of members
 Favor composition over inheritance
 Program to interface
 Open-closed principle
 Liskov substitution principle

CS 4311
47
Liskov Substitution Principle



An object of a subtype may be substituted for an object
of its supertypes.
 An object of a subtype should behave like an object of
its supertypes.
Q: Why?
A program that uses supertypes must be able to use
objects of the subtypes.
Barbara Liskov, Turing Award 2008
CS 4311
48
Substitution Principle --- How?

Syntactically:
If a method T1 m(T2 x) of class T is overridden in a subclass S,
T2_S is a superclass of T2_T (contra-variance of argument types)
T1_S is a subclass of T1_T (covariance of return type)
Q: Java rule?
Q: How about exceptions that may be thrown?

Semantically:
An overriding method should behave like an overridden method
Pre_T  Pre_S
Post_S  Post_T
CS 4311
49
Download