1 Inheritance & Delegation. 2 Our Starting Point We know OOP Objects are instances of classes...
-
date post
20-Dec-2015 -
Category
Documents
-
view
224 -
download
0
Transcript of 1 Inheritance & Delegation. 2 Our Starting Point We know OOP Objects are instances of classes...
1
Inheritance & Delegation
2
Our Starting Point
• We know OOP• Objects are instances of classes • Overriding• We have used inheritance
3
Terminology
• Per class– Forge: Signatures of accessible constructors– Mill: Bodies of constructor– Protocol: Signatures of accessible fields, methods– Behavior: Bodies of methods– Structure: Memory layout of an object
• Per object– State: Values held by fields of an object– Identity: Distinguishes two objects
4
The Two Effects of Subclassing
• Code Reuse– code of superclass is available in subclass
• Polymorphism– Instances of subclass can masquerade as instances of
superclass
• Next slide:– [Reuse] No upcasting of KShortestPathsIterator– [Reuse] No upcasting of ArrayList– [Polymorhism] PathWrapper is added to
ArrayList<GraphPath>
5public List<GraphPath<V, E>> getPaths(V endVertex) { KShortestPathsIterator<V, E> iter = new KShortestPathsIterator<V, E>(graph, startVertex, endVertex, nPaths);
for(int passNumber = 1; (passNumber <= nMaxHops) && iter.hasNext(); passNumber++) iter.next();
List<RankingPathElement<V, E>> list = iter.getPathElements(endVertex); if (list == null) return null;
ArrayList<GraphPath<V, E>> pathList = new ArrayList<GraphPath<V, E>>(); for (RankingPathElement<V, E> element : list) pathList.add(new PathWrapper(element));
return pathList;} // Taken from: org.jgrapht.alg.KShortestPaths
6
Polymorphic Messages• Same message will trigger different reactions
– Very powerful mechanism
• Previously (no polymorphic messages)for(Product x : products) { double price = x.fixedPrice ? x.price : x.price*0.7; System.out.println("New price is " + price);}
• W/ polymorphic messagesfor(Product x : products) { double price = x.discountPrice(0.7); ..}• Implementing classes: Product, FixedPriceProduct
7class Product {
double price;
public Product(double p) { price = p; }
public double discountPrice(double factor) {
return factor * price;
}
}
class FixedPriceProduct extends Product {
public FixedPriceProduct(double p) { super(p); }
public double discountPrice(double factor) {
return price;
}
}
8
<difficulties>
9
Subclass can Break Superclass
public class Range { protected int b, e;
void setBegin(int n) { b = n; } void setEnd(int n) { e = n; } void shift(int n) { setBegin(b + n); setEnd(e + n); }}
public class PositiveRange { private void check() { if(e < b) throw new RuntimeException(); }
void setBegin(int n) { super.setBegin(n); check(); } void setEnd(int n) { super.setEnd(n); check(); }}
10
Semantic Mismatch
class Product {
double price, cost;
Product(double p, double c) { price = p; cost = c; }
void getProfit() { return price – cost; }
double getPrice() { return p; }
void setPrice(double p) { price = p; }
}
class FixedPriceProduct extends Product {
FixedPriceProduct(double p, double c) { super(p, c); }
void setPrice(double p) { } // Price never changes!
}
11
Semantic Mismatch (cont.)
class Store {
Product[] products;
void increaeProfit(double factor) {
for(Product p : products) {
double diff = factor * p.getPrice();
p.setPrice(p.getPrice() + diff);
}
}
// Problem: increaseProfit(0.1) will not always lead
// to 10% increase in profit !!
12
Semantic Mismatch: Summary
• The author of increaseProfit expected setPrice to change the price of the product– This is true for class Product– This is not true for FixedPriceProduct
• Sometimes one can find a neutral implementation– Provides the semantics the caller expects– Does not break the invariants of the class – E.g., the discountPrice() method– (Sadly, this is not the case here)
• The standard solution: adjust the protocols– FixedPriceProduct will not offer a setPrice() method
13
Adjusting the Protocolsclass Product {
double price, cost;
Product(double p, double c) { price = p; cost = c; }
void getProfit() { return price – cost; }
double getPrice() { return p; }
}
class FixedPriceProduct extends Product {
FixedPriceProduct(double p, double c) { super(p, c); }
}
class NormalProduct extends Product {
NormalProduct(double p, double c) { super(p, c); }
void setPrice(double p) { price = p; }
}
14
Adjusting the Protocols (cont.)class Store {
Product[] products = ...;
NormalProduct[] normalProducts = ...;
void increaeProfit(double factor) {
for(NormalProduct p : normalProducts) {
double diff = factor * p.getPrice();
p.setPrice(p.getPrice() + diff);
}
}
// Problem: Distinction between NormalProduct and
// FixedPriceProduct has leaked into class Store.
// We lost the benefits of polymorphic messages
15
</difficulties>
16
Altering Behavior
• Behavior is defined by bodies of methods• Overriding allows a programmer to change a
method’s body• => Altered behavior can be obtained by means of
overriding
17
// First example, a Car class
public class Car {
private int speed;
public void setSpeed(int s) {
speed = s;
}
public int getSpeed() { return speed; }
}
public class TalkingCar extends Car {
public void setSpeed(int s) {
super.setSpeed(s);
System.out.println("My new speed is " + s);
}
}
18
// Second example, a scrabble game
public class Letter {
private char ch;
public Letter(char c) { ch = c; }
public boolean matches(char c) { return ch == c; }
}
public class WildCard extends Letter {
public boolean matches(char c) { return true; }
}
19
// Third example, scrabble game again
public interface Letter {
boolean matches(char c);
}
public class StandardLetter implements Letter {
private char ch;
public Letter(char c) { ch = c; }
public boolean matches(char c) { return ch == c; }
}
public class WildCard implements Letter {
public boolean matches(char c) { return true; }
}
20
• Is overriding the only way to alter behavior?
• No!• One can use state to alter behavior
21
// Varying a car behavior via statepublic class Car { private int speed; private String message;
private Car(String m) { message = m; } public Car() { this(""); }
public void setSpeed(int s) { speed = s; System.out.format(message, s); }
public int getSpeed() { return speed; }
public static Car newTalkingCar() { return new Car("My new speed is %s\n"); }}
22
// Scrabble: Emulating the wild-card via state
public class Letter {
private char ch;
public Letter(char c) { ch = c; }
public boolean matches(char c) {
return ch == '\0' || ch == c; }
public static newWildCard() {
return new Letter('\0'); }
}
23
Varying State vs. Overriding
• Is state as powerful as overriding?• Yes!• The general solution: Delegation
24
Delegation
• The receiver object forwards the incoming message to another object– (Respectively: delegating, delegated)– Delegated performs the “actual” work– Delegating holds the delegated in a field
• New behavior: replace the delegated-to object– Overriding effect via varying state
• Delegated object can offer only a single method– Function Object/Functor/Block/Closure/Command
• A delegating object can hold several delegated-to objects• A delegated object can in turn delegate to another object• Following two slide show the general recipe for replacing
overriding w/ delegation
25
public class C1 {
public void f() {
// do something
}
}
public class C2 extends C1 {
public void f() {
// do something else
}
}
26
public interface I { public void f(); }
public class X1 implements I { public void f() { // do something }}
public class X2 implements I { public void f() { // do something else }}
public class C { private I i; public void f() { i.f(); }
private C(I i) { this.i = i; } public static newC1() { return new C(new X1()); } public static newC2() { return new C(new X2()); }}
27
Delegation in Practice
• Delegating object does some extra work– Wraps the call (synchronize, catch, …)– Caching– Calls a method with a different name– Passes some extra parameters– Computes some other result– …
• Here’s a realistic delegation scenario
28// No delegation
public class ReportGenerator {
private String[] names;
protected int compareNames(String s1, String s2) {
return s1.trim().compareToIgnoreCase(s2.trim());
}
protected void sort() { ... compareNames(...) ... }
public void printReport(PrintWriter out) {
sort();
for(String name : names)
printName(out, name);
}
protected void printName(PrintWriter out, String name) {
out.println(name);
}
}
29// With delegation
public class ReportGenerator {
private String[] names;
private Comparator<String> comparator;
private NameFormatter formatter;
public void setFormatter(NameFormatter nf) { ... }
public void setComparator(Comparator<String> c) { ... }
protected void sort() { ... comparator.compare(...) ... }
public void printReport(PrintWriter out) {
sort();
for(String name : names)
printName(out, name);
}
protected void printName(PrintWriter out, String name) {
out.println(formatter.format(name));
}
}
30
Delegation: Pros
• Modify behavior of an existing object– Change the formatter of a ReportGenerator
• (After it was created!)
• Eliminate combinatorial explosion– 4 name formats– 4 name comparison policies– Total of 16 combinations– Overriding: 16 subclasses – Delegation: 4 formatters, 4 comparators
• Assists in increasing coherency– Dismantling a class into unrelated parts
31
Creation Point Issues
• Subclassing requires access to creation point– (The class is determined at creation time)
• Creation point may not be accessible– Objects created by a library
• Logic of choosing between subclasses may temper coherency
32
Delegation: Cons
• Harder to understand the code– Even the dynamic type does not specify the behavior– => Harder to debug
• In simple cases requires more code
• Difficult to prevent illegal combinations– E.g.: A formatter that requires a specific comparator
• Multiple identities