EECE 310: Software Engineering - PowerPoint PPT Presentation

About This Presentation
Title:

EECE 310: Software Engineering

Description:

EECE 310: Software Engineering Type Hierarchies and the Substitution Principle – PowerPoint PPT presentation

Number of Views:103
Avg rating:3.0/5.0
Slides: 51
Provided by: kart181
Category:

less

Transcript and Presenter's Notes

Title: EECE 310: Software Engineering


1
EECE 310 Software Engineering
  • Type Hierarchies and the Substitution Principle

2
Objectives
  • Apply the Liskov Substitution Principle (LSP) to
    the design of type hierarchies
  • Decide when to favor composition over inheritance
    and vice versa

3
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
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
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
Usage of removeAny
  • IntSet s new IntSet()
  • // Add elements to s
  • while ( removeAny(s) )
  • // s is empty at this point

7
What about this one ?
  • IntSet s new NonEmptySet(3)
  • // Add elements to s
  • while ( removeAny(s) )
  • // control never reaches here !

Can potentially throw an EmptySet exception !
8
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
  • signature rule
  • methods rule
  • properties rule

9
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 subtypes implementations must
    be compatible with the signatures of the
    corresponding super-type methods

10
Signature Rule in Java
  • Subtypes method can have fewer exceptions but
    NOT throw more exceptions
  • Arguments and return type should be identical
    (stricter than necessary)
  • Foo clone()
  • Foo x y.clone()
  • Object clone()
  • Foo x (Foo) y.clone()
  • Enforced by the compiler at compile-time

11
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
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
What will happen in this case ?
  • IntSet s new NonEmptySet(3)
  • // Add elements to s
  • while ( removeAny(s) )
  • // control never reaches here !

Will loop forever because the set never becomes
empty (why ?)
14
Whats the problem here ?
  • The remove method of NonEmptyIntSet has a
    different behavior than the remove method of the
    IntSet ADT (its 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
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 presupergt presub
  • Post-condition rule presuper postsub gt
    postsuper
  • Both conditions must be satisfied to achieve
    compatibility between the sub-type and super-type
    methods

16
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 gt 0,
    sub-type can take a value gt0 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 gt arbitrary)

17
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
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 gt 0 adds it to this
  • // else does nothing

19
Back to the NonEmptySet Type
  • public 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
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
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
Whats 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-types invariant, the new methods in
    sub-type must do so as well

23
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
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
In-class exercise
  • public class Counter
  • // Overview Counter should never decrease
  • public Counter( )
  • // EFFECTS Makes this contain 0
  • public int get( )
  • // EFFECTS Returns the value of this
  • public void incr()
  • // MODIFIES this
  • // EFFECTS Increases the value of this

26
In class exercise (contd..)
  • Now consider a type Counter2 with the following
    methods. Can this be a valid sub-type of Counter?
  • public Counter2( )
  • // EFFECTS Makes this contain 0
  • public void incr( )
  • // MODIFIES this
  • // EFFECTS Makes this contain twice its value

27
In class exercise (contd..)
  • What if you had another sub-type Counter3 with
    two extra operations. Does it satisfy the LSP ?
  • public Counter3(int n)
  • // EFFECTS makes this contain n
  • public void incr(int n)
  • // MODIFIES this
  • // EFFECTS If n gt 0, add n to this

28
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

29
Objectives
  • Apply the Liskov Substitution Principle (LSP) to
    the design of type hierarchies
  • Decide when to favor composition over inheritance
    and vice versa

30
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 Lists
    methods
  • Modifiability of parent type
  • Client need not change if parent class
    implementation changes (if done through public
    interface)

31
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.
  • Subtypes 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 as we will soon see

32
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

33
Example Square Sub-type ?
  • Should we model a square as a sub-type of
    rectangle (isnt square a type of rectangle ?)
  • We wont 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

34
Example Square
  • public 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)

35
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) )

36
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.

37
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 ?

38
One Solution Common base class
  • public abstract class Quadrilateral
  • // Represents a generic square or rectangle
  • protected Quad() // do we need this ?
  • public int getHeight()
  • public int getWidth()
  • public abstract void setWidth()
  • public abstract void setHeight()

39
Class Exercise
  • Distributed as a handout in class

40
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 IntSet
  • public void addAll(Collection c)
  • // EFFECTS Adds all elements of c to IntSet
  • for (int i C ) this.add( i )

41
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

42
InstrumentedIntSet Inheritance
  • public class InstrumentedIntSet extends IntSet
  • private int addCount // The number of attempted
    element insertions
  • public InstrumentedIntSet() super() addCount
    0
  • 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

43
Whats the problem here ?
  • Consider the following code
  • IntSet s new InstrumentedIntSet()
  • // Assume that array a has 3 int elements
  • s.addAll( a )
  • 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)

44
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 to use one in place of
    another (why not use abstract base class ?).

45
InstrumentedIntSet Composition-1
  • public class InstrumentedIntSet implements Set
  • private IntSet s
  • private int addCount
  • public InstrumentedIntSet( )
  • addCount 0
  • s new IntSet()

46
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 )

47
Inheritance Vs. Composition
  • Inheritance
  • Composition
  • Every A-object is a B-object.
  • Calling A-objects methods automatically
    executes Bs code, unless overridden (implicit
    code reuse).
  • Call to overridden method from inherited method
    executes As version.
  • As method code can see Bs protected internals.
  • Every A-object has a B-object.
  • A must implement all methods, but may delegate
    actual work to the internal B-object (explicit
    code-reuse).
  • Call to non-delegated method from delegated
    method runs Bs version.
  • Bs internals hidden from A
  • Interface may help if you want to substitute one
    for another

48
Should B be a subtype of A ?
Do we need to use B in place of A ?
Start
NO
YES
Does B satisfy the LSP ?
Do B and A need to share any code ?
NO
NO
YES
YES
Make B and A independent types (common interface
if necessary)
Make B a sub-type of A, but try to use the public
interface of A in B if possible and efficient
Make B contain an object of type A (common
interface if necessary)
49
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 ?

50
Summary of Sub-typing
  • Inheritance is often over-used without regard for
    its consequences (e.g., Java class library)
  • Not always straightforward to ensure behavioral
    substitutability of parent-type with sub-type
  • Subtle dependencies of sub-type on parent types
    implementation details can cause fragility
  • Use composition instead of inheritance whenever
    possible (with interfaces if necessary)
Write a Comment
User Comments (0)
About PowerShow.com