Presentation is loading. Please wait.

Presentation is loading. Please wait.

Programming Styles.

Similar presentations


Presentation on theme: "Programming Styles."— Presentation transcript:

1 Programming Styles

2 Design Goals © Jim Shore 2004
DRY (Don't Repeat Yourself): There's little duplication. Explicit: Code clearly states its purpose, usually without needing comments. Simple: Specific approaches are preferred over generic ones. Design patterns support features, not extensibility. Cohesive: Related code and concepts are grouped together. Decoupled: Unrelated code and concepts can be changed independently. Isolated: Third-party code is isolated behind a single interface. Present-day: The design doesn't try to predict future features. No hooks: Interfaces, factory methods, events, and other extensibility "hooks" are left out unless they meet a current need.

3 Background: Static Typing
The compiler employs static typing Checks that every method, field, is indeed defined We can use this mechanism to make it difficult for the programmer to write incorrect code The process: Take a versatile type Decompose into specific types

4 Static Typing: I/O Services Example
// Design #1: one versatile class class InputOutputFile { public InputOutputFile(String fileName, String mode) { ... } public char read() { ... } public void write(char c) { ... } } // Design #2: two specific classes class InputFile { public File(String fileName) { ... } class OutputFile {

5 Puzzle: Find the Bug & Redesign
class Vec { private int sz = 0; private Object[] arr = new Object[100]; public int size() { return sz; } public int get(int i) { return arr[i]; } public void add(Object o) { arr[sz++] = o; } public void remove(int i) { while(++i < sz) arr[i-1] = arr[i]; --sz; } // Client code: Vec v = ...; for(int i = 0, top = v.size(); i < top; ++i) v.remove(i);

6 Compiler Enforced Contracts
Existence of members (unless receiver is null) Encapsulation Per-object sequencing: Constructor runs before methods Constructor runs once Reference Equality of final fields

7 Bad API The following example is from Eclipse's Launching Framework
From: Framework/launch.html

8 An Interface public interface ILaunchConfigurationTabGroup {
public void createTabs(ILaunchConfigurationDialog d, String mode); public ILaunchConfigurationTab[] getTabs(); public void dispose(); public void setDefaults(ILaunchConfigurationWorkingCopy c); public void initializeFrom(ILaunchConfiguration c); public void performApply(ILaunchConfigurationWorkingCopy c); public void launched(ILaunch launch); }

9 Implementing class #1 public class LocalJavaApplicationTabGroup extends AbstractLaunchConfigurationTabGroup { public void createTabs(ILaunchConfigurationDialog dialog, String mode) { ILaunchConfigurationTab[] tabs = new ILaunchConfigurationTab[] { new JavaMainTab(), new JavaArgumentsTab(), new JavaJRETab(), new JavaClasspathTab(), new SourceLookupTab(), new EnvironmentTab(), new CommonTab() }; setTabs(tabs); }

10 Implementing class #2 public class ProgramTabGroup extends AbstractLaunchConfigurationTabGroup { public void createTabs(ILaunchConfigurationDialog dialog, String mode) { ILaunchConfigurationTab[] tabs = new ILaunchConfigurationTab[] { new ProgramMainTab(), new RefreshTab(), new EnvironmentTab(), new CommonTab() }; setTabs(tabs); }

11 Bad API: Discussion “All tab groups should include org.eclipse.debug.ui.CommonTab. This tab contains UI that allows users to make their configs 'local' or 'shared', mark configs as 'favorites' and control perspective switching when launching. By convention, this tab is the last tab in the tab group.” The API is does not reflect this fact explicitly Easy to write incorrect code

12 Class Reader public class EndOfReader extends Exception { } public class Reader { private String s; private int pos = 0; public Reader(String s_) { s = s_; } public char next() throws EndOfReader { if(pos == s.length()) throw new EndOfReader(); return s.charAt(pos++); } }

13 Class Lifo public class Lifo { private char[] buf = new char[10]; private int top = buf.length; public void push(Reader r) throws EndOfReader { if(top > 0) buf[--top] = r.next(); } public char at(int i) { int x = top + i; return x < buf.length ? buf[x] : '?' } }

14 Exception Safety Keep invariants of objects even after an exception
Strategies Catch & Undo Check & Mutate Duplicate, Mutate, Assign (Don't forget dynamic memory In C++)

15 What's Next? Styles Immutability Interfaces vs. Classes
Message Chains vs. Middle Man Defensive Copies

16 Immutability public 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; } }

17 Immutability (cont.) Pros: Compiler-checked contract
Covariant sub-classing Thread safety Caching, Sampling Only the constructor throws Less exception handling on the client's side Better exception safety Performance: Less copying Cons: Mutations

18 Are Interfaces Equivalent to Classes?
public interface Nameable { public String getName(); } public abstract class Person implements Nameable { } // Are these two methods ineterchangeable? public void f(Person p) { g(p.getName(); } public void f(Nameable n) { g(n.getName(); } public void g(String s) { ... }

19 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

20 Interfaces vs. Classes (one more time)
public interface List { public int getHead(); public List getTail(); } public final class MyList implements List { public final int head; public final MyList tail; public MyList(int head_, MyList tail_) { head = head_; tail = tail_; } public int getHead() { return head; } public MyList getTail() { return tail; } } public static boolean exist(int n, MyList lst) { return lst == null ? false : (lst.getHead() == n ? true : exist(n, lst.getTail())); }

21 Class Bank, Account 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; }

22 Class Owner 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; }

23 Class Branch, Reporter public class Branch {
private final String address; private final 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 = new Date(); 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());

24 Smell: Message Chains “Watch out for long sequences of method calls...” (Derived from) The 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

25 Class Account (Demeterized)
public 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; }

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

27 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

28 Alternatives for the Law Of Demeter
Decorator pattern Separation of Data from logic We can also use: Defensive setters/getters But, these are not as useful as people think. (See next slide...)

29 Class Bank: Duplicated Data
public class Bank { private Map<Integer,Account> accounts = new HashMap<Integer,Account>(); private Map<String,Integer> ownerToId = new HashMap<String,Integer> public Account getAccount(int id){ return accounts.get(id); } public void addAddcount(int id, Account a) { accounts.put(id, a); ownerToId.put(a.getOwner().getName(), id); } public Account getAccount(String name) { return getAccount(ownerToId.get(name)); }

30 Huston, 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");

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

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

33 Defensive Copies: The Pitfall
public void g(Account a) { a.getOwner().setName("new-name"); a.getOwner().getBranch().setAddress("new-address"); }

34 Defensive Copies: Summary
Never underestimate the importance of DRY No duplicated data => no need for a defense 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


Download ppt "Programming Styles."

Similar presentations


Ads by Google