1 classes (part ii). 2 roadmap emulating overriding w/ state delegation vs. subclassing binary...

28
1 Classes (part II)

Upload: marsha-miles

Post on 17-Dec-2015

218 views

Category:

Documents


0 download

TRANSCRIPT

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