Typehierarchy
-
Upload
pakminjini -
Category
Education
-
view
69 -
download
0
description
Transcript of Typehierarchy
![Page 1: Typehierarchy](https://reader035.fdocuments.in/reader035/viewer/2022081403/554f668bb4c905c8088b4ddf/html5/thumbnails/1.jpg)
EECE 210: Software Design
Type Hierarchies and the Substitution Principle
![Page 2: Typehierarchy](https://reader035.fdocuments.in/reader035/viewer/2022081403/554f668bb4c905c8088b4ddf/html5/thumbnails/2.jpg)
Objectives
• Apply the Liskov Substitution Principle (LSP) to the design of type hierarchies
• Use abstract base classes to solve LSP problem
• Decide when to use composition over inheritance
![Page 3: Typehierarchy](https://reader035.fdocuments.in/reader035/viewer/2022081403/554f668bb4c905c8088b4ddf/html5/thumbnails/3.jpg)
Objectives
• Apply the Liskov Substitution Principle (LSP) to the design of type hierarchies
• Use abstract base classes to solve LSP problem
• Decide when to favor composition over inheritance and vice versa
![Page 4: Typehierarchy](https://reader035.fdocuments.in/reader035/viewer/2022081403/554f668bb4c905c8088b4ddf/html5/thumbnails/4.jpg)
NonEmptySet Type
• Consider a subtype of IntSet called non-empty set, with the stipulation that it must *never* be empty. i.e., it has at least 1 element always– Constructor takes the element as an argument
and adds it to the els vector (the rep)– insert, size, isIn work as before (no change)– remove must make sure it never leaves the set
empty, otherwise it throws an EmptySetException
4
![Page 5: Typehierarchy](https://reader035.fdocuments.in/reader035/viewer/2022081403/554f668bb4c905c8088b4ddf/html5/thumbnails/5.jpg)
NonEmptySet: Remove
public class NonEmptySet extends IntSet { …
public void remove(int x) throws EmptySetException {
// EFFECTS: If set has at least two elements, // then remove x from the set
// Otherwise, throw the EmptySetException….
}}
5
![Page 6: Typehierarchy](https://reader035.fdocuments.in/reader035/viewer/2022081403/554f668bb4c905c8088b4ddf/html5/thumbnails/6.jpg)
RemoveAny procedure
public static boolean removeAny(IntSet s) {// EFFECTS: Remove an arbitrary element from
// the IntSet if the set is not empty, return true // Otherwise do nothing and return false if (s.size() == 0) return false; int x = s.choose(); s.remove(x); return true; }
6
![Page 7: Typehierarchy](https://reader035.fdocuments.in/reader035/viewer/2022081403/554f668bb4c905c8088b4ddf/html5/thumbnails/7.jpg)
Usage of removeAny
IntSet s = new IntSet();…// Add elements to swhile ( removeAny(s) ) { …}// s is empty at this point
7
![Page 8: Typehierarchy](https://reader035.fdocuments.in/reader035/viewer/2022081403/554f668bb4c905c8088b4ddf/html5/thumbnails/8.jpg)
What about this one ?
IntSet s = new NonEmptySet(3);…// Add elements to swhile ( removeAny(s) ) { …}// control never reaches here !
Can potentiallythrow an EmptySet exception !
8
![Page 9: Typehierarchy](https://reader035.fdocuments.in/reader035/viewer/2022081403/554f668bb4c905c8088b4ddf/html5/thumbnails/9.jpg)
Liskov Substitution principle• Intuition
– Users can use and reason about subtypes just using the supertype specification.
• Definition– Subtype specification must support
reasoning based on the super-type specification according to following rules:
1. signature rule2. methods rule3. properties rule
9
![Page 10: Typehierarchy](https://reader035.fdocuments.in/reader035/viewer/2022081403/554f668bb4c905c8088b4ddf/html5/thumbnails/10.jpg)
Signature Rule
• Every call that is type-correct with the super-type objects must also be type-correct with the sub-type objects – Sub-type objects must have all the methods of the
super-type – Signatures of the subtype’s implementations must
be compatible with the signatures of the corresponding super-type methods
10
![Page 11: Typehierarchy](https://reader035.fdocuments.in/reader035/viewer/2022081403/554f668bb4c905c8088b4ddf/html5/thumbnails/11.jpg)
Signature Rule in Java1. Subtype’s method can have fewer exceptions
but NOT throw more exceptions
2. Arguments and return type should be identical: (stricter than necessary)
Foo clone();
Foo x = y.clone();
Object clone();
Foo x = (Foo) y.clone();
3. Enforced by the compiler at compile-time
11
![Page 12: Typehierarchy](https://reader035.fdocuments.in/reader035/viewer/2022081403/554f668bb4c905c8088b4ddf/html5/thumbnails/12.jpg)
NonEmptySet: Remove
public class NonEmptySet extends IntSet { …
public void remove(int x) throws EmptySetException {
// EFFECTS: If set has at least two elements, // then remove x
// Otherwise, throw the EmptySetException….
}}
Violates signature rule – will not compile
12
![Page 13: Typehierarchy](https://reader035.fdocuments.in/reader035/viewer/2022081403/554f668bb4c905c8088b4ddf/html5/thumbnails/13.jpg)
Will this solve the problem ?
public class NonEmptySet extends IntSet { …
public void remove(int x) {// EFFECTS: If set has at least two elements,
// then remove x
// Otherwise, do nothing….
}}
13
![Page 14: Typehierarchy](https://reader035.fdocuments.in/reader035/viewer/2022081403/554f668bb4c905c8088b4ddf/html5/thumbnails/14.jpg)
What will happen in this case ?
IntSet s = new NonEmptySet(3);…// Add elements to swhile ( removeAny(s) ) { …}// control never reaches here !
Will loop forever because the set never becomes empty (why ?)
14
![Page 15: Typehierarchy](https://reader035.fdocuments.in/reader035/viewer/2022081403/554f668bb4c905c8088b4ddf/html5/thumbnails/15.jpg)
What’s the problem here ?
• The remove method of NonEmptyIntSet has a different behavior than the remove method of the IntSet ADT (it’s parent type)– In the IntSet ADT, after you call remove(x), you are
assured that x is no longer part of the set (provided the set was non-empty prior to the call)
– In the NonEmptyIntSet ADT, after you call remove(x), you do not have this assurance anymore which violates the substitution principle
15
![Page 16: Typehierarchy](https://reader035.fdocuments.in/reader035/viewer/2022081403/554f668bb4c905c8088b4ddf/html5/thumbnails/16.jpg)
Methods rule
• A sub-type method can weaken the pre-condition (REQUIRES) of the parent method and strengthen its post-condition (EFFECTS)– Pre-condition rule: presuper=> presub
– Post-condition rule: presuper && postsub => postsuper
• Both conditions must be satisfied to achieve compatibility between the sub-type and super-type methods
16
![Page 17: Typehierarchy](https://reader035.fdocuments.in/reader035/viewer/2022081403/554f668bb4c905c8088b4ddf/html5/thumbnails/17.jpg)
Remember …
• Weakening of pre-condition: REQUIRES less– Example: Parent-type requires a non-empty
collection, but the sub-type does not– Example: Parent-type requires a value > 0, sub-
type can take a value >=0 in its required clause
• Strengthening of post-condition: DOES more– Example: Sub-type returns the elements of the set
in sorted order while parent-type returns them in any arbitrary order (sorted => arbitrary)
17
![Page 18: Typehierarchy](https://reader035.fdocuments.in/reader035/viewer/2022081403/554f668bb4c905c8088b4ddf/html5/thumbnails/18.jpg)
Example of methods rule
• Consider a sub-type of IntSet LogIntSet which keeps track of all elements that were ever in the set even after they are removed
public void insert(int x)// MODIFIES: this// EFFECTS: Adds x to the set and to the log
Does this satisfy the methods rule ?
18
![Page 19: Typehierarchy](https://reader035.fdocuments.in/reader035/viewer/2022081403/554f668bb4c905c8088b4ddf/html5/thumbnails/19.jpg)
Is the methods rule satisfied here ?
• Consider another sub-type PositiveIntSet which only adds positive Integers to the set
public void insert(int x)// MODIFIED: this// EFFECTS: if x >= 0 adds it to this
// else does nothing
19
![Page 20: Typehierarchy](https://reader035.fdocuments.in/reader035/viewer/2022081403/554f668bb4c905c8088b4ddf/html5/thumbnails/20.jpg)
Back to the NonEmptySet Typepublic class NonEmptySet { // Not derived from IntSet // A Non-empty IntSet is a mutable set of integers // whose size is at least 1 always
public void removeNonEmpty(int x) {// EFFECTS: If set has at least two elements,
// then remove x// Otherwise, do nothing….
}}
20
![Page 21: Typehierarchy](https://reader035.fdocuments.in/reader035/viewer/2022081403/554f668bb4c905c8088b4ddf/html5/thumbnails/21.jpg)
Regular IntSet
public class IntSet extends NonEmptySet { // Overview: A regular IntSet as before
public void remove(int x) {// MODIFIES: this// EFFECTS: Removes x from this
… }}
21
![Page 22: Typehierarchy](https://reader035.fdocuments.in/reader035/viewer/2022081403/554f668bb4c905c8088b4ddf/html5/thumbnails/22.jpg)
What happens in this code ?
public static void findMax (NonEmptySet s) { int max = s.choose(); iterator g = s.elements();
while (g.hasNext() ) {…
}}
Can throw an exception if IntSet is passed in as argument
22
![Page 23: Typehierarchy](https://reader035.fdocuments.in/reader035/viewer/2022081403/554f668bb4c905c8088b4ddf/html5/thumbnails/23.jpg)
What’s the problem here ?
• The IntSet type has an operation remove which causes it to violate the invariant property of its parent type NonEmptySet– Calling code may be able to make the set empty
by calling remove and then pass it to findMax
• Not enough if the derived methods preserve the parent-type’s invariant, the new methods in sub-type must do so as well
23
![Page 24: Typehierarchy](https://reader035.fdocuments.in/reader035/viewer/2022081403/554f668bb4c905c8088b4ddf/html5/thumbnails/24.jpg)
Properties Rule
• Subtype must preserve each property of the super-type in each of its methods– Invariant properties (always true)– Evolution properties (evolve over time)
• Examples– Invariant property: The set never becomes empty– Evolution property: The set size never decreases
24
![Page 25: Typehierarchy](https://reader035.fdocuments.in/reader035/viewer/2022081403/554f668bb4c905c8088b4ddf/html5/thumbnails/25.jpg)
Putting it together: Substitution Principle
• Signature rule: If program is type-correct based on super-type specification, it is also type-correct with respect to the sub-type specification.
• Methods rule: Ensures that reasoning about calls of super-type methods is valid even if the call goes to code that implements a subtype.
• Properties rule: Reasoning about properties of objects based on super-type specification is still valid even when objects belong to the sub-type.
25
![Page 26: Typehierarchy](https://reader035.fdocuments.in/reader035/viewer/2022081403/554f668bb4c905c8088b4ddf/html5/thumbnails/26.jpg)
Summary of LSP
• Liskov Substitution Principle (LSP) is a unifying way of reasoning about the use of sub-types– Signature rule: Syntactic constraint and can be
enforced by compiler– Methods rule and properties rule: Pertain to
semantics (behavior) and must be enforced by programmer
• LSP is essential for locality and modifiability of programs using types and sub-types
26
![Page 27: Typehierarchy](https://reader035.fdocuments.in/reader035/viewer/2022081403/554f668bb4c905c8088b4ddf/html5/thumbnails/27.jpg)
Group Activity
• In the handout
![Page 28: Typehierarchy](https://reader035.fdocuments.in/reader035/viewer/2022081403/554f668bb4c905c8088b4ddf/html5/thumbnails/28.jpg)
Objectives
• Apply the Liskov Substitution Principle (LSP) to the design of type hierarchies
• Use abstract base classes to solve LSP problem
• Decide when to favor composition over inheritance and vice versa
![Page 29: Typehierarchy](https://reader035.fdocuments.in/reader035/viewer/2022081403/554f668bb4c905c8088b4ddf/html5/thumbnails/29.jpg)
Why do we use sub-types ?
• Define relationships among a group of types– SortedList and UnsortedList are sub-types of List
• Specification reuse (common interface)– Using code simply says “give me a list”
• Implementation reuse (code sharing)– SortedList need not re-implement all of List’s methods
• Modifiability of parent type– Client need not change if parent class implementation
changes (if done through public interface)
![Page 30: Typehierarchy](https://reader035.fdocuments.in/reader035/viewer/2022081403/554f668bb4c905c8088b4ddf/html5/thumbnails/30.jpg)
Why not to use sub-types ?
• Sub-types are not appropriate in many cases– Sub-type must satisfy Liskov Substitution Principle. In
other words, it must not cause existing code to break.– Subtype’s implementation must not depend on the
implementation details of the parent type
• Common rule of thumb: “Sub-types must model is a special kind of relationship”– Not always as simple or true as we will soon see
![Page 31: Typehierarchy](https://reader035.fdocuments.in/reader035/viewer/2022081403/554f668bb4c905c8088b4ddf/html5/thumbnails/31.jpg)
Example: Rectangle// A vanilla Rectangle class.public class Rectangle {
private double width;private double height;public Rectangle(double w, double h) {
width = w;height = h;
}public double getWidth() {return width;}public double getHeight() {return height;}public void setWidth(double w) {width = w;}public void setHeight(double h) {height = h;}
}
![Page 32: Typehierarchy](https://reader035.fdocuments.in/reader035/viewer/2022081403/554f668bb4c905c8088b4ddf/html5/thumbnails/32.jpg)
Example: Square Sub-type ?
• Should we model a square as a sub-type of rectangle (isn’t square a “type of” rectangle ?)– We won’t need two instance variables, height and
width, but this is a minor irritant– Need to override setHeight and setWidth
operations so that width and height cannot be changed independently
– Remember, you cannot change the Rectangle class
![Page 33: Typehierarchy](https://reader035.fdocuments.in/reader035/viewer/2022081403/554f668bb4c905c8088b4ddf/html5/thumbnails/33.jpg)
Example: Squarepublic class Square extends Rectangle {
private double width;private double height;public Square(double s) {
super(s, s);}public void setWidth(double w) {
super.setWidth(w); super.setHeight(w);}public void setHeight(double h) {
super.setWidth(h); super.setHeight(h);}
}
![Page 34: Typehierarchy](https://reader035.fdocuments.in/reader035/viewer/2022081403/554f668bb4c905c8088b4ddf/html5/thumbnails/34.jpg)
What is the problem here ?
void testRectangle(Rectangle r) {
r.setWidth(4); r.setHeight(5);assert( r.getHeight() * r.getWidth() ==
20 );}
testRectangle( new Square(3) );
![Page 35: Typehierarchy](https://reader035.fdocuments.in/reader035/viewer/2022081403/554f668bb4c905c8088b4ddf/html5/thumbnails/35.jpg)
Problem
• Although Square is a type of rectangle in the real world, a square object is NOT a sub-type of a rectangle object because it is more constrained than the rectangle object– Which rule of LSP does it break ?
• We should NOT model square as a sub-type of rectangle because behaviorally, a square object cannot be substituted for a rectangle object.
![Page 36: Typehierarchy](https://reader035.fdocuments.in/reader035/viewer/2022081403/554f668bb4c905c8088b4ddf/html5/thumbnails/36.jpg)
So how do you fix this ?
• Square and rectangle should not be in an inheritance relationship with one-another– They are really doing two different things, just so
happens they share some (minimal) features– Do not satisfy the LSP (behavioral substitution)
• But, how to share code between two ADTs which are not in inherited from each other ?
![Page 37: Typehierarchy](https://reader035.fdocuments.in/reader035/viewer/2022081403/554f668bb4c905c8088b4ddf/html5/thumbnails/37.jpg)
One “Solution”: Abstract base class
public abstract class Quadrilateral {// Represents a generic square or rectangleprotected Quad(); // do we need this ?
public int getHeight();public int getWidth();
public abstract void setWidth(); public abstract void setHeight();}
![Page 38: Typehierarchy](https://reader035.fdocuments.in/reader035/viewer/2022081403/554f668bb4c905c8088b4ddf/html5/thumbnails/38.jpg)
Abstract Base Class• Abstract classes are used for providing partial
implementations of data types– Declared using the keyword abstract in Java– May or may not have instance variables– Needs to have constructors if it has instance vars– Has one or more methods partially implemented,
other methods are declared as abstract
• Sub-classes implement the abstract methods– Can call the base class’s (non-abstract) methods– Need not implement all abstract methods
![Page 39: Typehierarchy](https://reader035.fdocuments.in/reader035/viewer/2022081403/554f668bb4c905c8088b4ddf/html5/thumbnails/39.jpg)
Abstract Classes Vs. Interfaces
![Page 40: Typehierarchy](https://reader035.fdocuments.in/reader035/viewer/2022081403/554f668bb4c905c8088b4ddf/html5/thumbnails/40.jpg)
Class Exercise
• Distributed as a handout in class
![Page 41: Typehierarchy](https://reader035.fdocuments.in/reader035/viewer/2022081403/554f668bb4c905c8088b4ddf/html5/thumbnails/41.jpg)
Objectives
• Apply the Liskov Substitution Principle (LSP) to the design of type hierarchies
• Use abstract base classes to solve LSP problem
• Decide when to favor composition over inheritance and vice versa
![Page 42: Typehierarchy](https://reader035.fdocuments.in/reader035/viewer/2022081403/554f668bb4c905c8088b4ddf/html5/thumbnails/42.jpg)
Fragile Base Class Problem• LSP is not the only problem with inheritance. Even if
LSP is satisfied, there are other issues
• Assume that you add a new method to IntSetpublic void addAll(Collection<int> c) {
// EFFECTS: Adds all elements of c to IntSetIterator<int> ci = c.elements();while (ci.hasNext()){
this.add( c.next() ); }
}
![Page 43: Typehierarchy](https://reader035.fdocuments.in/reader035/viewer/2022081403/554f668bb4c905c8088b4ddf/html5/thumbnails/43.jpg)
InstrumentedIntSet
• Consider an example InstrumentedIntSet which keeps track of the number of elements ever added to the IntSet ADT (different from its size). Assume this type inherits from IntSet– Must add a new field to keep track of count– Override the add method to increment count– Override the addAll method to increment count
![Page 44: Typehierarchy](https://reader035.fdocuments.in/reader035/viewer/2022081403/554f668bb4c905c8088b4ddf/html5/thumbnails/44.jpg)
InstrumentedIntSet: Inheritancepublic class InstrumentedIntSet extends IntSet {
private int addCount; // The number of attempted element insertions
public InstrumentedIntSet() { super(); addCount = 0; }
public boolean add(Integer i) { addCount++;
return super.add(i);}
public boolean addAll(Collection<int> c) {addCount += c.size();
return super.addAll(c);}
public int getAddCount() {return addCount;
}
![Page 45: Typehierarchy](https://reader035.fdocuments.in/reader035/viewer/2022081403/554f668bb4c905c8088b4ddf/html5/thumbnails/45.jpg)
What’s the problem here ?
Consider the following code:IntSet s = new InstrumentedIntSet();Vector v = new Vector<int>();// Assume that vector v has 3 int elementss.addAll( v );int i = s.getAddCount( ); // What does it return ?
How will you fix this problem ?1. Modify addAll to not do the increment,
but what if base class does not call the add method? 2. Write your own version of addAll in the derived class to do the iteration (no reuse)
![Page 46: Typehierarchy](https://reader035.fdocuments.in/reader035/viewer/2022081403/554f668bb4c905c8088b4ddf/html5/thumbnails/46.jpg)
Solution: Use Composition
• Instead of making InstrumentedIntSet a sub-type of IntSet, make it contain an IntSet– In Java, it holds a reference to an IntSet rather
than a copy, so be careful to not expose it– Do not have to worry about substitution principle
(though that is not a problem in this example)– Make both classes implement a common Set
interface if you want them to be inter-changeable
![Page 47: Typehierarchy](https://reader035.fdocuments.in/reader035/viewer/2022081403/554f668bb4c905c8088b4ddf/html5/thumbnails/47.jpg)
InstrumentedIntSet: Composition-1
public class InstrumentedIntSet implements Set{
private IntSet s; private int addCount; public InstrumentedIntSet( ) { addCount = 0;
s = new IntSet(); }
![Page 48: Typehierarchy](https://reader035.fdocuments.in/reader035/viewer/2022081403/554f668bb4c905c8088b4ddf/html5/thumbnails/48.jpg)
InstrumentedIntSet: Composition-2
public class InstrumentedIntSet implements Set{
public void add(int element) { addCount = addCount + 1;
s.add(element); }
public void addAll(Collection c) {addCount = addCount + c.size();s.addAll( c );
}
![Page 49: Typehierarchy](https://reader035.fdocuments.in/reader035/viewer/2022081403/554f668bb4c905c8088b4ddf/html5/thumbnails/49.jpg)
Should B be a subtype of A ?
Start
Do we need to use B in place of
A ?
Does B satisfy
the LSP ?
Do B and A need to share any
code ?
Make B a sub-type of A, but try to use the public
interface of A in B
Make B contain an object of type A
(common interface if necessary)
Make B and A independent
types (common interface if necessary)
NO
YES
YES
NO
NO
YES
![Page 50: Typehierarchy](https://reader035.fdocuments.in/reader035/viewer/2022081403/554f668bb4c905c8088b4ddf/html5/thumbnails/50.jpg)
Class Exercise
• Consider the NonEmptySet type that we saw earlier. Can you rewrite it to use an IntSet rather than be derived from an IntSet ?
• How will you make them inherit from a common base class ?
![Page 51: Typehierarchy](https://reader035.fdocuments.in/reader035/viewer/2022081403/554f668bb4c905c8088b4ddf/html5/thumbnails/51.jpg)
Objectives
• Apply the Liskov Substitution Principle (LSP) to the design of type hierarchies
• Use abstract base classes to solve LSP problem
• Decide when to favor composition over inheritance and vice versa