1 / 28

Classes (part II)

Classes (part II). 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

newman
Télécharger la présentation

Classes (part II)

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation 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 Interface public 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 Interface public 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

More Related