0% found this document useful (0 votes)
10 views21 pages

4-Reusable in Design (Delegation - Inheritance and Behavioral Subtyping) Part I - 2016-12-01

Uploaded by

khuubaolong2k3
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
10 views21 pages

4-Reusable in Design (Delegation - Inheritance and Behavioral Subtyping) Part I - 2016-12-01

Uploaded by

khuubaolong2k3
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 21

2/8/2017

Principles of Software Construction


(NGUYÊN LÝ XÂY DỰNG PHẦN MỀM)

Software Engineering Dept., CICT,


Cantho Unversity
Lecturer: Trương Minh Thái
email: [email protected]
Phần dành cho đơn vị

Contents

• Delegation and inheritance and behavioral


subtyping
• Introduction to design patterns

1
2/8/2017

Contents

• Delegation and inheritance and behavioral


subtyping
– Delegation
– Inheritance
– Behavioral subtyping: Liskov's Substitution
Principle
• Introduction to design patterns

Class-level reuse with delegation and


inheritance
• The promise of reuse

2
2/8/2017

Recall our earlier sorting example


static void sort(int[] list, boolean ascending) {

boolean mustSwap;
Version A if (ascending) {
mustSwap = list[i] < list[j];
} else {
mustSwap = list[i] > list[j];
}

}

interface Comparator {
boolean compare(int i, int j);
}
final Comparator ASCENDING = (i, j) -> i < j;
Version B final Comparator DESCENDING = (i, j) -> i > j;
static void sort(int[] list, Comparator cmp) {

boolean mustSwap =
cmp.compare(list[i], list[j]);

}

Delegation

• Delegation is simply when one object relies on another


object for some subset of its functionality
– e.g. here, the Sorter is delegating functionality to
some Comparator
• Judicious delegation enables code reuse
interface Comparator {
boolean compare(int i, int j);
}
final Comparator ASCENDING = (i, j) -> i < j;
final Comparator DESCENDING = (i, j) -> i > j;
static void sort(int[] list, Comparator cmp) {

boolean mustSwap =
cmp.compare(list[i], list[j]);

}

3
2/8/2017

Delegation

• Delegation is simply when one object relies on another


object for some subset of its functionality
– e.g. here, the Sorter is delegating functionality to
some Comparator
• Judicious delegation enables code reuse
– Sorter can be reused with arbitrary sort orders
– Comparators can be reused with arbitrary client code
that needs to compare integers

Using delegation to extend functionality

• Consider the java.util.List:


public interface List<E> {
public boolean add(E e);
public E remove(int index);
public void clear();

}
• Suppose we want a list that logs its operations
to the console…

4
2/8/2017

Using delegation to extend functionality


A solution:
public class LoggingList<E> implements List<E> {
private final List<E> list; The LoggingList is composed
public LoggingList<E>(List<E> list) { of a List, and delegates (the
nonlogging) functionality to that
this.list = list; List
}
public boolean add(E e) {
System.out.println("Adding " + e);
return list.add(e);
}
public E remove(int index) {
System.out.println("Removing at " + index);
return list.remove(index);
}

Delegation and design

• Small interfaces with clear contracts


• Classes to encapsulate algorithms, behaviors
– e.g., the Comparator

5
2/8/2017

Contents

• Delegation and inheritance and behavioral


subtyping
– Delegation
– Inheritance
– Behavioral subtyping: Liskov's Substitution
Principle
• Introduction to design patterns

Variation in the real world: types of


bank accounts
public interface CheckingAccount {
public long getBalance();
public void deposit(long amount);
public boolean withdraw(long amount);
public boolean transfer(long amount, Account??? target);
public long getFee();
}

public interface SavingsAccount {


public long getBalance();
public void deposit(long amount);
public boolean withdraw(long amount);
public boolean transfer(long amount, Account??? target);
public double getInterestRate();
}

6
2/8/2017

Interface inheritance for an account


type hierarchy
public interface Account {
public long getBalance();
public void deposit(long amount);
public boolean withdraw(long amount);
public boolean transfer(long amount, Account target);
public void monthlyAdjustment();
}
public interface CheckingAccount extends Account {
public long getFee();
}
public interface SavingsAccount extends Account {
public double getInterestRate();
}
public interface InterestCheckingAccount
extends CheckingAccount, SavingsAccount {
}

The power of object-oriented interfaces

• Subtype polymorphism
– Different kinds of objects can be processed
uniformly by client code
– Each object behaves according to its type
• e.g., if you add new kind of account, client code does
not change:
If today is the last day of the month:
For each acct in allAccounts:
acct.monthlyAdjustment();

7
2/8/2017

Implementation inheritance for code reuse

public abstract class AbstractAccount implements Account {


protected long balance = 0; an abstract class is missing
the implementation of one
public long getBalance() {
or more methods
return balance;
protected elements are
}
visible in subclasses
abstract public void monthlyAdjustment();
// other methods… an abstract method is left to be
implemented in a subclass
}
public class CheckingAccountImpl extends AbstractAccount implements
CheckingAccount {
public void monthlyAdjustment() {
balance -= getFee();
}
public long getFee() { … }
} no need to define getBalance()
– the code is inherited from AbstractAccount

Inheritance: a glimpse at the hierarchy

• Examples from Java


– java.lang.Object
– Collections library

8
2/8/2017

Java Collections API (excerpt)

Benefits of inheritance

• Reuse of code
• Modeling flexibility
• Note in Java:
– Each class can directly extend only one parent
class
– A class can implement multiple interfaces

9
2/8/2017

Inheritance and subtyping

• Inheritance is for code reuse


– Write code once and only once
– Superclass features implicitly available
in subclass class A extends B
• Subtyping is for polymorphism
– Accessing objects the same way, but
getting different behavior
– Subtype is substitutable for supertype
class A implements I
class A extends B

Typical roles for interfaces and classes

• An interface defines expectations /


commitments for clients
• A class fulfills the expectations of an interface
– An abstract class is a convenient hybrid
– A subclass specializes a class's implementation

10
2/8/2017

Java details: extended reuse with super

public abstract class AbstractAccount implements Account {


protected long balance = 0;
public boolean withdraw(long amount) {
// withdraws money from account (code not shown)
}
}
public class ExpensiveCheckingAccountImpl extends AbstractAccount implements
CheckingAccount {
public boolean withdraw(long amount) {
balance -= HUGE_ATM_FEE;
boolean success = super.withdraw(amount)
if (!success)
balance += HUGE_ATM_FEE; Overrides withdraw but also uses
the superclass withdraw method
return success;
}
}

Java details: constructors with this and


super
public class CheckingAccountImpl extends AbstractAccount
implements CheckingAccount {
private long fee;
public CheckingAccountImpl(long initialBalance, long fee) {
super(initialBalance);
Invokes a constructor of the
this.fee = fee; superclass. Must be the first
} statement of the constructor.
public CheckingAccountImpl(long initialBalance) {
this(initialBalance, 500);
}
/* other methods… */ Invokes another constructor
} in this same class

11
2/8/2017

Java details: final

• A final field: prevents reassignment to the field


after initialization
• A final method: prevents overriding the method
• A final class: prevents extending the class
– e.g., public final class CheckingAccountImpl { …

Note: type-casting in Java

• Sometimes you want a different type than you have


– e.g., double pi = 3.14;
int indianaPi = (int) pi;
• Useful if you know you have a more specific subtype:
– e.g.,
Account acct = …;
CheckingAccount checkingAcct = (CheckingAccount) acct;
long fee = checkingAcct.getFee();
– Will get a ClassCastException if types are incompatible
• Advice: avoid downcasting types
– Never(?) downcast within superclass to a subclass

12
2/8/2017

Note: instanceof

• Operator that tests whether an object is of a given class


public void doSomething(Account acct) {
long adj = 0;
if (acct instanceof CheckingAccount) {
checkingAcct = (CheckingAccount) acct; Warning:
adj = checkingAcct.getFee(); This code is bad
} else if (acct instanceof SavingsAccount) {
savingsAcct = (SavingsAccount) acct;
adj = savingsAcct.getInterest();
}

}
• Advice: avoid instanceof if possible
– Never(?) use instanceof in a superclass to check type against subclass

Java details: Dynamic method dispatch

1. (Compile time) Determine which class to look in


2. (Compile time) Determine method signature to be executed
1. Find all accessible, applicable methods
2. Select most specific matching method
3. (Run time) Determine dynamic class of the receiver
4. (Run time) From dynamic class, locate method to invoke
1. Look for method with the same signature found in step 2
2. Otherwise search in superclass and etc.

13
2/8/2017

Design with inheritance (or not)

• Favor composition over inheritance


– Inheritance violates information hiding
• Design and document for inheritance, or prohibit
it
– Document requirements for overriding any method

Contents

• Delegation and inheritance and behavioral


subtyping
– Delegation
– Inheritance
– Behavioral subtyping: Liskov's Substitution
Principle
• Introduction to design patterns

14
2/8/2017

Behavioral subtyping

Let q(x) be a property provable about objects x of type T. Then q(y)


should be provable for objects y of type S where S is a subtype of T.
Barbara Liskov
• e.g., Compiler-enforced rules in Java:
– Subtypes can add, but not remove methods
– Concrete class must implement all undefined methods
– Overriding method must return same type or subtype
– Overriding method must accept the same parameter types
– Overriding method may not throw additional exceptions
• Also applies to specified behavior:
– Same or stronger invariants
– Same or stronger postconditions for all methods
– Same or weaker preconditions for all methods
This is called the Liskov
Substitution Principle

Homework

15
2/8/2017

Behavioral subtyping
(Liskov Substitution Principle)
abstract class Vehicle { class Car extends Vehicle {
int speed, limit; int fuel;
boolean engineOn;
//@ invariant speed < limit; //@ invariant speed < limit;
//@ invariant fuel >= 0;

//@ requires fuel > 0 && !engineOn;


//@ ensures engineOn;
void start() { … }

void accelerate() { … }

//@ requires speed != 0;


//@ requires speed != 0;
//@ ensures speed < \old(speed)
//@ ensures speed < \old(speed)
void brake() { … }
void brake();
}
} Subclass fulfills the same invariants (and additional ones)
Overridden method has the same pre and postconditions

Behavioral subtyping
(Liskov Substitution Principle)
class Hybrid extends Car {
class Car extends Vehicle { int charge;
int fuel; //@ invariant charge >= 0;
boolean engineOn;
//@ invariant fuel >= 0; //@ requires (charge > 0 || fuel > 0) &&
!engineOn;
//@ requires fuel > 0 && !engineOn; //@ ensures engineOn;
//@ ensures engineOn; void start() { … }
void start() { … }
void accelerate() { … }
void accelerate() { … }
//@ requires speed != 0;
//@ requires speed != 0; //@ ensures speed < \old(speed)
//@ ensures speed < old(speed) //@ ensures charge > \old(charge)
void brake() { … } void brake() { … }
} }
Subclass fulfills the same invariants (and additional ones)
Overridden method start has weaker precondition
Overridden method brake has stronger postcondition

16
2/8/2017

Behavioral subtyping
(Liskov Substitution Principle)

class Rectangle { class Square extends Rectangle {


int h, w; Square(int w) {
Rectangle(int h, int w) { super(w, w);
this.h=h; this.w=w; }
} }
//methods
}

Is this Square a behavioral subtype of Rectangle?

Behavioral subtyping
(Liskov Substitution Principle)

class Rectangle { class Square extends Rectangle {


int h, w; Square(int w) {
Rectangle(int h, int w) { super(w, w);
this.h=h; this.w=w; }
} }
//methods
}

Is this Square a behavioral subtype of Rectangle? Yes

17
2/8/2017

Behavioral subtyping
(Liskov Substitution Principle)

class Rectangle { class Square extends Rectangle {


//@ invariant h>0 && w>0; //@ invariant h>0 && w>0;
int h, w; //@ invariant h==w;
Rectangle(int h, int w) {
Square(int w) {
this.h=h; this.w=w;
super(w, w);
}
//methods
}
} }

Is this Square a behavioral subtype of Rectangle?

Behavioral subtyping
(Liskov Substitution Principle)

class Rectangle { class Square extends Rectangle {


//@ invariant h>0 && w>0; //@ invariant h>0 && w>0;
int h, w; //@ invariant h==w;
Rectangle(int h, int w) {
Square(int w) {
this.h=h; this.w=w;
super(w, w);
}
//methods
}
} }

Is this Square a behavioral subtype of Rectangle? Yes

18
2/8/2017

Behavioral subtyping
(Liskov Substitution Principle)
class Rectangle { class Square extends Rectangle {
//@ invariant h>0 && w>0; //@ invariant h>0 && w>0;
int h, w; //@ invariant h==w;
Rectangle(int h, int w) { Square(int w) {
this.h=h; this.w=w; super(w, w);
} }
//@ requires factor > 0; }
void scale(int factor) {
w=w*factor;
h=h*factor;
}
}
Is this Square a behavioral subtype of Rectangle?

Behavioral subtyping
(Liskov Substitution Principle)
class Rectangle { class Square extends Rectangle {
//@ invariant h>0 && w>0; //@ invariant h>0 && w>0;
int h, w; //@ invariant h==w;
Rectangle(int h, int w) { Square(int w) {
this.h=h; this.w=w; super(w, w);
} }
//@ requires factor > 0; }
void scale(int factor) {
w=w*factor;
h=h*factor;
}
}
Is this Square a behavioral subtype of Rectangle? Yes

19
2/8/2017

Behavioral subtyping
(Liskov Substitution Principle)
class Rectangle { class Square extends Rectangle {
//@ invariant h>0 && w>0; //@ invariant h>0 && w>0;
int h, w; //@ invariant h==w;
Rectangle(int h, int w) { Square(int w) {
this.h=h; this.w=w; super(w, w);
} }
//@ requires factor > 0; }
void scale(int factor) {
w=w*factor;
h=h*factor;
}
//@ requires neww > 0;
void setWidth(int neww) {
w=neww;
}
} Is this Square a behavioral subtype of Rectangle?

Behavioral subtyping
(Liskov Substitution Principle)
class Rectangle { class Square extends Rectangle {
//@ invariant h>0 && w>0; //@ invariant h>0 && w>0;
int h, w; //@ invariant h==w;
Rectangle(int h, int w) { Square(int w) {
this.h=h; this.w=w; super(w, w);
} }
//@ requires factor > 0; }
void scale(int factor) { class GraphicProgram {
w=w*factor; void scaleW(Rectangle r, int factor) {
h=h*factor; r.setWidth(r.getWidth() * factor);
} }
//@ requires neww > 0; }
void setWidth(int neww) { ← Invalidates stronger
w=neww; invariant (w==h) in subclass
}
} Is this Square a behavioral subtype of Rectangle?
Yes? (But the Square is not actually a square…)

20
2/8/2017

Summary: Designing reusable classes

• Reusable implementations with simple, clear


contracts
• Inheritance for reuse, its pipalls, and its
alternatives
• Liskov's Substitution Principle for behavioral
subtyping

21

You might also like