1
Classes (part II)
2
Roadmap
• Emulating overriding w/ state• Delegation vs. Subclassing• Binary Methods• LSP• Immutability• Interfaces vs. Classes• Law of Demeter: “Tell, don’t ask”• Message chains• Defensive copies• Bad API• Exception safety• Origins of classes – nouns• Problem vs. Program space• Origins of classes – Kent Beck• Fat vs. thin interfaces• Furniture - subclassing alternatives
3
Origins of Classes
• Grouping of data and logic• Rate of change• Parameter objects
• Overall approach: start writing, discover the classes from the code
• Source: “Implementation Patterns”, Kent Beck
4
Grouping of data and logic
Use a class to say, “This data goes together and this logic goes with it”
“Implementation Patterns”, Kent Beck
5
Grouping – Before
if (ClassReader.SIGNATURES && signature != 0) { ++attributeCount; size += 8; newUTF8("Signature"); } if (sourceFile != 0) { ++attributeCount; size += 8; newUTF8("SourceFile"); }
...
6
Grouping – After
AttributeWriter aw = new AttributeWriter(...);
aw.put(ClassReader.SIGNATURES && signature != 0, 8, "Signature");
aw.put(sourceFile != 0, 8, "SourceFile");
...
7
Rate of Change
Put logic or data that changes at the same rate together and separate logic or data that
changes at different rates
“Implementation Patterns”, Kent Beck
8
Rate of Change - Example
// Before
void setAmount(int value, String currency) {
this.value = value;
this.currency = currency;
}
// After
void setAmount(int value, String currency) {
this.value = new Money(value, currency);
}
9
Parameter Object
Consolidate frequently used long parameter lists into an object
“Implementation Patterns”, Kent Beck
10
Parameter Object - Example
// Before
setOuterBounds(x, y, width, height);
setInneerBounds(x + 2, y + 2, width - 4, height - 4);
// After
setOuterBounds(bounds);
setInnerBounds(bounds.expand(-2));
11
Interfaces: Width• Thin
– Support minimal set of necessary services
• Humane– Support common usage
• Fat– Support every imaginable service
• Textbook example:– Java’s List inteface has 25 methods– Ruby’s Array class has 78 methods
Source: http://martinfowler.com/bliki/HumaneInterface.html
12
Humane Interfacepublic class HumaneList<T> {
public T get();
public void remove(int n);
public int size();
public void set(int index, T value);
public void add(T value);
public T first();
public T last();
public void sort();
public int indexOf(T value);
}
13
Thin Interfacepublic class ThinList<T> {
public T get();
public void remove(int n);
public int size();
public void set(int index, T value);
}
// Client code – add a value
list.set(list.size(), newValue);
// Client code – get last
List.get(list.size() – 1);
14
Thin Interfaces Encourage Foreign Methods
public class Lists {
public static<T> void add(List<T> list, T value) {
list.set(list.size(), value);
}
public static<T> T getLast(List<T> list, T value) {
list.get(list.size() – 1);
}
public static<T> void sort(...)
public static<T> int IndexOf (...)
...
}
15
Thin vs. Humane• Thin interfaces – pros:
– Coherency •of implementing class•of the interface itself
– Easier subclassing– Client code is less coupled
• Humane Interface – cons:– Less client code
16
Thin/Humane – Final Thoughts
• A spectrum, not a binary issue– An interface can be slightly humane and very
much thin
• Inside project boundaries?– Start with thin, let it evolve
• Unknown users?– Humane seems more useful– OTOH, more difficult to fight coupling
17
Furniture – Design #1
18
Furniture – Subclassing Alternatives
19
public class Rect { public final int top; public final int left; public final int bottom; public final int right;
public Rect(int top, int left, int bottom, int right) { this.top = top; this.left = left; this.bottom = bottom; this.right = right; } }
public class Point { public final int x; public final int y; public Point(int x, int y) { this.x = x; this.y = y; } }
20
public abstract class Furniture { public abstract Rect getBoundingRect();}
public class Table extends Furniture {
private int width; private int height; public Table(int width, int height) { this.width = width; this.height = height; }
@Override public Rect getBoundingRect() { return new Rect(0, 0, width, height); } }
21
public class Lamp extends Furniture {
private int base; private int height; public Lamp(int base, int height) { this.base = base; this.height = height; }
@Override public Rect getBoundingRect() { return new Rect(0, 0, base, height); }}
22
Furniture – Design #2
23
public class Furniture { final List<Point> points = new ArrayList<Point>(); protected void add(int x ,int y) { points.add(new Point(x, y)); } public Rect getBoundingRect() { List<Integer> xs = new ArrayList<Integer>(); List<Integer> ys = new ArrayList<Integer>(); for(Point p : points) { xs.add(p.x); ys.add(p.y); } return new Rect(min(ys), min(xs), max(ys), max(xs)); }}
24
public class Furniture { ...
static int max(List<Integer> values) { int result = Integer.MIN_VALUE; for(int n : values) result = Math.max(result, n); return result; } static int min(List<Integer> values) { int result = Integer.MAX_VALUE; for(int n : values) result = Math.min(result, n); return result; } }
25
public class Table extends Furniture {
public Table(int width, int height) { add(0, 0); add(width, 0); add(width, height); add(0, height); }}
public class Lamp extends Furniture {
public Lamp(int base, int height) { add(0, 0); add(base, 0); add(base / 2, height); }}
26
Furniture – Design #3
27
Public class Furniture { ... // Same code as design #2
public Furniture newTable(int width, int height) { Furniture result = new Furniture(); result.add(0, 0); result.add(width, 0); result.add(width, height); result.add(0, height); return result; } public Furniture newLamp(int base, int height) { Furniture result = new Furniture(); result.add(0, 0); result.add(base, 0); result.add(base / 2, height); return result; }}
28
Summary
• Design #1:– getBoundingRect() over-ridden – Subclasses do most of the work– Elegant if computations vary greatly across subclasses
• Design #2, #3: – Superclass does most/all of the work– Variation among subclasses expressed by state– Elegant if computations can be generalized
• Without too many special cases crippling it
– More efficient as number of variants grows