TheoryAndPractice1

Download Report

Transcript TheoryAndPractice1

From Theory to Practice 1
OOP 2006
Overview
• Reminder – some OOD principles from
previous lessons:
– Class Inheritance vs. Object Composition
– Program to an interface and not an implementation
– Modularity
• The impact of these and other principles is
greatly illustrated in the book Effective Java ™
by Joshua Bloch
– The book contains many rules of thumb
for writing a code that is clear, correct,
robust and reuseable
– Most code samples are taken from
http://java.sun.com/docs/books/effective/
Inheritance vs. Composition
Reminder
• The two most common techniques for reusing
functionality are inheritance and composition
• Class Inheritance
– Define implementation of one class in terms of another
– The internals of the parent are visible to subclass
• Object Composition
– New functionality is obtained by assembling objects
– No internal details of objects are visible
• There are pros and cons for each alternative
• But, the common rule of thumb is:
Favor Composition over Inheritance
Example – Profiling a HashSet
• Recall some Map<E> API functions:
– boolean add(E o)
– boolean addAll(Collection<? extends E> c)
• One implementing class is HashSet<E>.
Some constructors of this class are:
– HashSet()
– HashSet(Collection<? extends E> c)
– HashSet(int initialCapacity, float loadFactor)
• We would like to add a profiling function:
– int getAddCount()
This function returns the number of attempted element
insertions
Using Inheritance
import java.util.*;
public class InstrumentedHashSet extends HashSet{
// The number of attempted element insertions
private int addCount = 0;
public InstrumentedHashSet() {}
public InstrumentedHashSet(Collection c) {
super(c);
}
public InstrumentedHashSet
(int initCap, float loadFactor) {
super(initCap, loadFactor);
}
Using Inheritance – Handling
Counts
public boolean add(Object o) {
addCount++;
return super.add(o);
}
public boolean addAll(Collection c) {
addCount += c.size();
return super.addAll(c);
}
public int getAddCount() {
return addCount;
}
Using Inheritance –
Results
• What is the result of the following code?
public static void main(String[] args) {
InstrumentedHashSet s =
new InstrumentedHashSet();
s.addAll(Arrays.asList(new String[]
{"Snap","Crackle","Pop"}));
System.out.println(s.getAddCount());
}
• Unfortunately the answer is 6
• The reason: addAll uses add
Possible Solutions
• Do not override addAll:
– Problem: depends on implementation details
• Write addAll using iteration and add.
Cons:
– Complete re-implementation of the method
– Sometimes requires access to private members
• Another problem with previous solutions:
– What if a different add function is add to HashSet?
• Another alternative: Composition
Using Composition
import java.util.*;
public class InstrumentedSet implements Set {
private final Set s;
private int addCount = 0;
Decorator
public InstrumentedSet(Set s)
Pattern
{ this.s = s; }
public boolean add(Object o) {
addCount++; return s.add(o);
}
public boolean addAll(Collection c) {
addCount += c.size(); return s.addAll(c);
}
public int getAddCount() {
return addCount; }
Forwarding Methods
public void clear()
{ s.clear();
}
public boolean contains(Object o)
{ return s.contains(o); }
public boolean isEmpty()
{ return s.isEmpty();
}
public int size()
return s.size();
} in
A{forwarding
method is a method
public Iterator iterator()
the wrapper class which invokes
corresponding
method in the
{ the
return
s.iterator();
}
contained class o)
and returns the
public boolean remove(Object
result
{ return s.remove(o);
}
public boolean containsAll(Collection c)
{ return s.containsAll(c); }
Forwarding Methods - cont
public boolean removeAll(Collection c)
{ return s.removeAll(c);
}
public boolean retainAll(Collection c)
{ return s.retainAll(c);
}
public Object[] toArray()
{ return s.toArray(); }
public Object[] toArray(Object[] a)
{ return s.toArray(a); }
public boolean equals(Object o)
{ return s.equals(o); }
public int hashCode() { return s.hashCode(); }
public String toString()
{ return s.toString(); }
}
The Pros and Cons of
Composition
• Pros for composition
– The underlying Set is completely encapsulated while
inheritance breaks encapsulation
– Robust, not implementation dependent
– Flexible, the profiling functionality works with any kind of set
• Pros for inheritance
– Only methods concerned with new functionality need to be
overridden
– Natural choice for some ‘is-a’ relations (e.g. Strategy, and
Composite patterns)
• Additional pros and cons in the slides of Lecture 5
Use common sense to weigh the tradeoffs
Program to an interface and not an
implementation
• Reminder from lecture 5:
Variables should not be instances of a particular concrete
class. Instead commit only to an interface defined by an
abstract class.
• Therefore, if an appropriate interface types exists,
parameters, return values, variables and fields
should all be declared using interface types.
• The only reference to an object class is when
creating it.
Example
• Good, uses interface as type:
List subscribers = new Vector();
• Bad, use class as type
Vector subscribers = new Vector();
• Using interfaces increase flexibility, such as
changing implementation:
List subscribers = new ArrayList();
• However, this should be done carefully,
considering behavioral differences
• Exceptions to the principle:
– Classes without interfaces (such as String)
– Classes with extra functionality (LinkedList)
Building a Good Interface
• We would like to supply a good interface to the
client
• In Java types the permit multiple implementations
can be defined as interfaces or as abstract class
– Abstract classes allow implementation of some
methods while interfaces do not allow this
– A class can implement multiple interfaces while it can
inherit only from a single class
• Should we use Interfaces or Abstract Classes?
Interfaces vs. Abstract Classes
• Advantages of interfaces
– Existing class can be easily retrofitted to implement
a new interface
• Example: Comparable
– Allows construction of non-hierarchical type
frameworks
• Example: Integer is both Serializable, Comparable<Integer>
– Enable safe and powerfull functionality
enhancements
• As in the previous example of InstrumentedMap
Interfaces vs. Abstract Classes
• Advantages of abstract classes
– Provides default implementation
– It is far easier to evolve an abstract class than it is to
evolve an interface:
What happens to the implementing classes if we add
a new function to the interface?
• Some advantages can be combined
Skeletal Implementations
• Skeletal implementations Combine the
advantages of interfaces and the ability to
provide default implementation in an abstract
class
• These are abstract classes implementing
some of the methods defined in the interface
• Naming convention Abstract<Interface>
– Examples:
AbstractCollection, AbstractSet,
AbstractList, AbstractMap
Example - AbstractMapEntry
public abstract class AbstractMapEntry<K,V>
implements Map.Entry<K,V> {
// Primitives: must be implemented in the
// derived class
public abstract K getKey();
public abstract V getValue();
// Entries in modifiable maps must overide
// this method
public V setValue(V newValue) {
throw new UnsupportedOperationException()
}
Note that this class in not included in the Java Platform library
AbstractMapEntry - cont
// Implements the general contract of
// Map.Entry.hashCode
public int hashCode() {
return
(getKey()==null ? 0 : getKey().hashCode()) ^
(getValue()==null
? 0 : getValue.hashCode());
.
.
.
}
Additional implementations here
Rules of Thumb for Interfaces
• Prefer interfaces to abstract classes
• Public interfaces should be designed and
tested carefully
– It is almost impossible to change an interface once
it is released
• If you export a non trivial interface, consider
providing a skeletal implementation
• Use abstract classes only if ease of evolution
is more important than flexibility and power
From Requirements to Practicalities
(Via Principles)
General requirements of a good software system
Correct ,Clear, Reusable, Robust, Efficient, More?
Principles
• Favor composition over inheritance
• Write to an interface
• More…
Practicalities
• Design patterns (e.g. Decorator for composition)
• Using the Interface mechanism of Java, skeletal
implementation
• Many more …