47
1 Class-level Design

1 Class-level Design. 2 Reminder: Software is Recursive Civil engineering: –Room, Flat, building, Street, City, … Software –Class, Class, Class,... –Or:

  • View
    214

  • Download
    0

Embed Size (px)

Citation preview

Page 1: 1 Class-level Design. 2 Reminder: Software is Recursive Civil engineering: –Room, Flat, building, Street, City, … Software –Class, Class, Class,... –Or:

1

Class-level Design

Page 2: 1 Class-level Design. 2 Reminder: Software is Recursive Civil engineering: –Room, Flat, building, Street, City, … Software –Class, Class, Class,... –Or:

2

Reminder: Software is Recursive

• Civil engineering:– Room, Flat, building, Street, City, …

• Software– Class, Class, Class, ...– Or:– Object, Object, Object, …

• => A class can represent both low level concerns and high level concerns

• => Design of classes ~ design of a program

Page 3: 1 Class-level Design. 2 Reminder: Software is Recursive Civil engineering: –Room, Flat, building, Street, City, … Software –Class, Class, Class,... –Or:

3

Interfaces: Visibility

• PublicThe group of methods (of a class) that any other class in the system can invoke

• PublishedA class interface that is used outside the code base that it is defined in

Source: Martin Fowler; Public versus Published interfaces; IEEE Software 2002

Page 4: 1 Class-level Design. 2 Reminder: Software is Recursive Civil engineering: –Room, Flat, building, Street, City, … Software –Class, Class, Class,... –Or:

4

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

Page 5: 1 Class-level Design. 2 Reminder: Software is Recursive Civil engineering: –Room, Flat, building, Street, City, … Software –Class, Class, Class,... –Or:

5

Humane Interfacepublic class HumaneList<T> {

public T get(int i);

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);

}

Page 6: 1 Class-level Design. 2 Reminder: Software is Recursive Civil engineering: –Room, Flat, building, Street, City, … Software –Class, Class, Class,... –Or:

6

Thin Interfacepublic class ThinList<T> {

public T get(int i);

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);

Page 7: 1 Class-level Design. 2 Reminder: Software is Recursive Civil engineering: –Room, Flat, building, Street, City, … Software –Class, Class, Class,... –Or:

7

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) {

list.get(list.size() – 1);

}

public static<T> void sort(...)

public static<T> int IndexOf (...)

...

}

Page 8: 1 Class-level Design. 2 Reminder: Software is Recursive Civil engineering: –Room, Flat, building, Street, City, … Software –Class, Class, Class,... –Or:

8

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

Page 9: 1 Class-level Design. 2 Reminder: Software is Recursive Civil engineering: –Room, Flat, building, Street, City, … Software –Class, Class, Class,... –Or:

9

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

Page 10: 1 Class-level Design. 2 Reminder: Software is Recursive Civil engineering: –Room, Flat, building, Street, City, … Software –Class, Class, Class,... –Or:

10

Mission Statement

A program which manipulates the location of furniture in a room. A furniture can be a chair a table or a lamp. Bounding rectangles of furniture should not intersect and should be contained inside the room. Doors should be located along the boundaries of the room.

Page 11: 1 Class-level Design. 2 Reminder: Software is Recursive Civil engineering: –Room, Flat, building, Street, City, … Software –Class, Class, Class,... –Or:

11

Problem Space Classes

• Room• Chair, Table, Lamp, Door• Rectangle• …

Page 12: 1 Class-level Design. 2 Reminder: Software is Recursive Civil engineering: –Room, Flat, building, Street, City, … Software –Class, Class, Class,... –Or:

12

Program Space Classes

• Range• Placement

Page 13: 1 Class-level Design. 2 Reminder: Software is Recursive Civil engineering: –Room, Flat, building, Street, City, … Software –Class, Class, Class,... –Or:

13

Problem Space

• Characteristics are dictated externally• Some may change in unanticipated ways

– See the axioms

• Limited reuse opportunities

Page 14: 1 Class-level Design. 2 Reminder: Software is Recursive Civil engineering: –Room, Flat, building, Street, City, … Software –Class, Class, Class,... –Or:

14

Program Space

• Characteristics chosen by programmer• High degree of reuse: The Lego principle• => Difficult to change

Page 15: 1 Class-level Design. 2 Reminder: Software is Recursive Civil engineering: –Room, Flat, building, Street, City, … Software –Class, Class, Class,... –Or:

15

Furniture – Design #1

Page 16: 1 Class-level Design. 2 Reminder: Software is Recursive Civil engineering: –Room, Flat, building, Street, City, … Software –Class, Class, Class,... –Or:

16

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; } }

Page 17: 1 Class-level Design. 2 Reminder: Software is Recursive Civil engineering: –Room, Flat, building, Street, City, … Software –Class, Class, Class,... –Or:

17

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); } }

Page 18: 1 Class-level Design. 2 Reminder: Software is Recursive Civil engineering: –Room, Flat, building, Street, City, … Software –Class, Class, Class,... –Or:

18

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); }}

Page 19: 1 Class-level Design. 2 Reminder: Software is Recursive Civil engineering: –Room, Flat, building, Street, City, … Software –Class, Class, Class,... –Or:

19

Furniture – Design #2

Page 20: 1 Class-level Design. 2 Reminder: Software is Recursive Civil engineering: –Room, Flat, building, Street, City, … Software –Class, Class, Class,... –Or:

20

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)); }}

Page 21: 1 Class-level Design. 2 Reminder: Software is Recursive Civil engineering: –Room, Flat, building, Street, City, … Software –Class, Class, Class,... –Or:

21

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; } }

Page 22: 1 Class-level Design. 2 Reminder: Software is Recursive Civil engineering: –Room, Flat, building, Street, City, … Software –Class, Class, Class,... –Or:

22

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); }}

Page 23: 1 Class-level Design. 2 Reminder: Software is Recursive Civil engineering: –Room, Flat, building, Street, City, … Software –Class, Class, Class,... –Or:

23

Furniture – Design #3

Page 24: 1 Class-level Design. 2 Reminder: Software is Recursive Civil engineering: –Room, Flat, building, Street, City, … Software –Class, Class, Class,... –Or:

24

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; }}

Page 25: 1 Class-level Design. 2 Reminder: Software is Recursive Civil engineering: –Room, Flat, building, Street, City, … Software –Class, Class, Class,... –Or:

25

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

Page 26: 1 Class-level Design. 2 Reminder: Software is Recursive Civil engineering: –Room, Flat, building, Street, City, … Software –Class, Class, Class,... –Or:

26

Immutabilitypublic class Driver { ... }

// Mutable:public class Car { private Driver d; public Driver getDriver() { return d; } public void setDriver(Driver d_) { d = d_; }}

// Immutable:public class Car { private final Driver d; public Car(Driver d_) { d = d_; } public Driver getDriver() { return d; }}

Page 27: 1 Class-level Design. 2 Reminder: Software is Recursive Civil engineering: –Room, Flat, building, Street, City, … Software –Class, Class, Class,... –Or:

27

Immutability (cont.)

• Pros:– Compiler-checked contract– Covariant sub-classing– Thread safety– Caching, Sampling– Only the constructor throws (usually)

• Less exception handling on the client's side• Better exception safety

– Performance: Less copying

• Cons:– Mutations

Page 28: 1 Class-level Design. 2 Reminder: Software is Recursive Civil engineering: –Room, Flat, building, Street, City, … Software –Class, Class, Class,... –Or:

28

An Interface or a Classes?

public void g(List<Nameable> list) { for(Nameable n : list) System.out.println(n.getName());}

// Opt.1: Person is an interfacepublic interface Nameable { public String getName();}

// Opt.2: Person is a classpublic abstract class Nameable {

public abstract String getName(); }

Page 29: 1 Class-level Design. 2 Reminder: Software is Recursive Civil engineering: –Room, Flat, building, Street, City, … Software –Class, Class, Class,... –Or:

29

Interfaces Vs. Classes

• Interfaces– Do not fill the inheritance spot– Easier to provide alternative implementations – Promise less – client is less coupled

• => Client is more reusable• => Client is more complicated

• Classes– Easier to read the code– Promise more

Page 30: 1 Class-level Design. 2 Reminder: Software is Recursive Civil engineering: –Room, Flat, building, Street, City, … Software –Class, Class, Class,... –Or:

30

Interfaces vs. Classes (one more time)

public interface MyList { public int getHead(); public List getTail();}

public class MyList implements List { private final int head; private final MyList tail;

public MyList(int head_, MyList tail_) { head = head_; tail = tail_; }

public final int getHead() { return head; } public final MyList getTail() { return tail; } }

public static boolean exist(int n, MyList lst) { return lst == null ? false : (lst.getHead() == n ? true : exist(n, lst.getTail())); }

Page 31: 1 Class-level Design. 2 Reminder: Software is Recursive Civil engineering: –Room, Flat, building, Street, City, … Software –Class, Class, Class,... –Or:

31

<bank.classes>

Page 32: 1 Class-level Design. 2 Reminder: Software is Recursive Civil engineering: –Room, Flat, building, Street, City, … Software –Class, Class, Class,... –Or:

32

public class Bank { private Map<Integer,Account> accounts = new HashMap<Integer,Account>(); public Account getAccount(int id){ return accounts.get(id); } } public class Account { private final Owner owner; private int amount; public Account(Owner o) { owner = o; } public Owner getOwner() { return owner; } public void deposit(int n) { amount += n; } public int getAmount() { return amount; } }

Page 33: 1 Class-level Design. 2 Reminder: Software is Recursive Civil engineering: –Room, Flat, building, Street, City, … Software –Class, Class, Class,... –Or:

33

public class Owner { private final String name; private final Branch branch; public Owner(String name_, Branch branch_) { branch = branch_; name = name_; }

public String getName() { return name; } public Branch getBranch() { return branch; } }

Page 34: 1 Class-level Design. 2 Reminder: Software is Recursive Civil engineering: –Room, Flat, building, Street, City, … Software –Class, Class, Class,... –Or:

34

public class Branch { String address; Map<Date,String> meetings = new HashMap<Date,String>(); public Branch(String address_) { address = address_; } public String getAddress() { return address; }

public Date requestMeeting(String subject) { Date d = ... // Choose a date for the meeting meetings.put(d, subject); return d; } } public class Reporter { public void printAddress(Bank b, int id) { System.out.println(id + ": " + b.getAccount(id).getOwner().getBranch().getAddress()); } }

Page 35: 1 Class-level Design. 2 Reminder: Software is Recursive Civil engineering: –Room, Flat, building, Street, City, … Software –Class, Class, Class,... –Or:

35

Tell vs. Ask• Ask

Account a = ...; a.getOwner().getBranch().requestMeeting("…");

• TellAccount a = ...; a.requestMeeting("…");

• Guideline: Tell, Don’t ask– Better encapsulation– Let Account control the behaviour of requestMeeting

• Related Smell: Long Message Chains– “Watch out for long sequences of method calls...”

Page 36: 1 Class-level Design. 2 Reminder: Software is Recursive Civil engineering: –Room, Flat, building, Street, City, … Software –Class, Class, Class,... –Or:

36

Law of Demeter• A method M of an object O may only invoke the

methods of the following kinds of objects– 1. O itself– 2. M's parameters– 3. any objects created within M– 4. O's direct fields

• Embodies the "tell, don’t ask" guideline

Page 37: 1 Class-level Design. 2 Reminder: Software is Recursive Civil engineering: –Room, Flat, building, Street, City, … Software –Class, Class, Class,... –Or:

37

Class Account (Demeterized) public class Account { private final Owner owner; private int amount; public Account(Owner o) { owner = o; } public void deposit(int delta) { amount += delta; } public int getAmount() { return amount; } public String getOwnerName() { return owner.getName(); } public String getBranchAddress() {

return owner.getBranchAddress(); }

public Date requestMeeting(String subject) { return owner.requestMeeting(subject) : null; } }

Page 38: 1 Class-level Design. 2 Reminder: Software is Recursive Civil engineering: –Room, Flat, building, Street, City, … Software –Class, Class, Class,... –Or:

38

Class Owner (Demeterized) public class Owner { private final String name; private final Branch branch; public Owner(String name_, Branch branch_) { branch = branch_; name = name_; }

public String getName() { return name; } public Branch getBranch() { return branch; }

public String getBranchAddress() { return branch.getAddress(); }

public Date requestMeeting(String subject) { return branch.requestMeeting(subject); } }

Page 39: 1 Class-level Design. 2 Reminder: Software is Recursive Civil engineering: –Room, Flat, building, Street, City, … Software –Class, Class, Class,... –Or:

39

Benefits of the "Tell" Approach// Class Account can control the requestMeeting behaviorpublic class Account { private final Owner owner; private int amount; private int limit = 5; public Account(Owner o) { owner = o; } public void deposit(int delta) { amount += delta; } public int getAmount() { return amount; } public String getOwnerName() { return owner.getName(); } public String getBranchAddress() { return owner.getBranchAddress(); }

public Date requestMeeting(String subject) { return limit-- > 0 ? owner.requestMeeting(subject) : null; }}

Page 40: 1 Class-level Design. 2 Reminder: Software is Recursive Civil engineering: –Room, Flat, building, Street, City, … Software –Class, Class, Class,... –Or:

40

Law Of Demeter• Pros

– Better Encapsulation– Easy to intercept/modify requests

• Cons– Low Cohesion– High Coupling– Smells:

• Combinatorial explosion• Divergent change (lack of cohesion)• Shotgun surgery• Middle man

Page 41: 1 Class-level Design. 2 Reminder: Software is Recursive Civil engineering: –Room, Flat, building, Street, City, … Software –Class, Class, Class,... –Or:

41

Defensive Setters/Getters

• (Relevant under the “Ask” approach)

• Instead of returning an object…• …Return a copy thereof

• Expected to increase encapsulation

Page 42: 1 Class-level Design. 2 Reminder: Software is Recursive Civil engineering: –Room, Flat, building, Street, City, … Software –Class, Class, Class,... –Or:

42

Class Bank: Duplicated Datapublic class Bank { private Map<Integer,Account> accountFromId = new HashMap<Integer,Account>();

private Map<String,Integer> idFromName = new HashMap<String,Integer> public Account getAccount(int id){ return accountFromId.get(id); }

public void addAddcount(int id, Account a) { accountFromId.put(id, a); idFromName.put(a.getOwner().getName(), id); }

public Account getAccount(String name) { return getAccount(idFromName.get(name)); }}

Page 43: 1 Class-level Design. 2 Reminder: Software is Recursive Civil engineering: –Room, Flat, building, Street, City, … Software –Class, Class, Class,... –Or:

43Huston, We Have a Consistency Problem

//// Let's assume Owner has a setName() method...//

public void f1(Bank b) { b.getAccount(1).getOwner().setName("new-name"); }

public void f2(Owner o) { o.setName("new-name"); }

Page 44: 1 Class-level Design. 2 Reminder: Software is Recursive Civil engineering: –Room, Flat, building, Street, City, … Software –Class, Class, Class,... –Or:

44

Defensive Copies public class Account { private final Owner owner; private int amount; public Account(Owner o) { owner = o; } public Owner getOwner() { return owner.clone(); } public void deposit(int delta) { amount += delta; } public int getAmount() { return amount; } }

Page 45: 1 Class-level Design. 2 Reminder: Software is Recursive Civil engineering: –Room, Flat, building, Street, City, … Software –Class, Class, Class,... –Or:

45

Class Owner (Defensive Copies)public class Owner implements Cloneable { private String name; private final Branch branch;

public Owner(String name_, Branch branch_) { branch = branch_; name = name_; }

@Override protected Owner clone() { try { return (Owner) super.clone(); } catch(CloneNotSupportedException e) { throw new AssertionError(); } } public void setName(String arg) { name = arg; } public String getName() { return name; } public Branch getBranch() { return branch; }}

Page 46: 1 Class-level Design. 2 Reminder: Software is Recursive Civil engineering: –Room, Flat, building, Street, City, … Software –Class, Class, Class,... –Or:

46

Defensive Copies: The Pitfall

public void g(Account a) { a.getOwner().setName("new-name"); a.getOwner().getBranch().setAddress("new-address"); }

Page 47: 1 Class-level Design. 2 Reminder: Software is Recursive Civil engineering: –Room, Flat, building, Street, City, … Software –Class, Class, Class,... –Or:

47

Defensive Copies: Summary

• Never underestimate the importance of DRY– No duplicated data => no need to be defensive– E.g.: caching, data computed from raw data

• Useful when returning objects that were not passed in from the outside– E.g.: Collections– Textbook example: Class.getMethods()

• Can be used in either setters or getters