300 likes | 419 Views
Chapter 7. Designing Classes . Chapter Goals. To learn how to choose appropriate classes to implement To understand the concepts of cohesion and coupling To minimize the use of side effects
E N D
Chapter 7 Designing Classes
Chapter Goals • To learn how to choose appropriate classes to implement • To understand the concepts of cohesion and coupling • To minimize the use of side effects • To document the responsibilities of methods and their callers with preconditions and postconditions • To understand the difference between instance methods and static methods
To introduce the concept of static fields • To understand the scope rules for local variables and instance fields • To learn about packages
Choosing Classes • A class represents a single concept • Concepts from mathematics:PointRectangleEllipse • Concepts from real lifeBankAccountPurse • Actors (end in -er, -or)StringTokenizerRandom (better called RandomNumberGenerator) • Utility classes--no objects, only static methodsMath
Cohesion • Cohesive = public interface closely related to the single concept that the class represents • This class lacks cohesion: • public class Purse{ public Purse(){...} public void addNickels(int count){...} public void addDimes(int count){...} public void addQuarters(int count){...} public double getTotal(){...} public static final double NICKEL_VALUE =0.05; public static final double DIME_VALUE =0.1; public static final double QUARTER_VALUE =0.25; ... }
Cohesion • Two concepts:purse and coin • Solution: Make two classes: • public class Coin { public Coin(double aValue,String aName){...} public double getValue(){...} }public class Purse { public Purse(){...} public void add(Coin aCoin){...} public double getTotal(){...} }
Coupling • A class depends on another if it calls one of its methods • Purse depends on Coin because it calls getValue on coins • Coin does not depend on Purse • High Coupling = many class dependencies • Minimize coupling to minimize the impact of interface changes
Accessor and Mutator Classes • Accessor: does not change the state of the implicit parameter (e.g. getBalance) • Mutator: changes the state of the implicit parameter (e.g. deposit ) • Rule of thumb: Mutator should return void • Immutable class: all methods are accessors (e.g. String)
Side Effect • Side Effect: any observable change outside the implicit parameter Example: modify explicit parameter • public void transfer(double amount, BankAccount other){ balance = balance - amount; other.balance = other.balance + amount;}
Example: printing in method • public void deposit(double amount){ if (amount < 0) System.out.println("Bad value"); . . .} • Reprehensible: couples with System, PrintStrea
Common Error: Can't Modify Primitive Type Parameters • void transfer(double amount, double otherBalance){ balance = balance - amount; otherBalance = otherBalance + amount;} • Won't work Scenario: • double savingsBalance = 1000;harrysChecking.transfer(500, savingsBalance)
Preconditions • Publish preconditions so the caller won't call methods with bad parameters /** Deposits money into this account. @param amount the amount of money to deposit (Precondition: amount >= 0)*/ • Typical use: • To restrict the parameters of a method • To require that a method is only called when the object is in an appropriate state • Method can do anything if called when precondition not fulfilled
Preconditions • Method may throw exception if precondition violated • if (amount < 0) throw new IllegalArgumentException();balance = balance + amount; • Nicer to throw exception than to silently muddle through • if (amount < 0) return; // don't do thisbalance = balance + amount;
Method doesn't have to test for precondition. (Test may be costly) • // no test--that's ok// if this makes the balance negative, // it's the caller's faultbalance = balance + amount
Postconditions • Condition that's true after a method has completed. • /** Deposits money into this account. (Postcondition: getBalance() >= 0) @param amount the amount of money to deposit (Precondition: amount >= 0)*/ • Don't document trivial postconditions that repeat the @return clause • Contract: If caller fulfills precondition, method must fulfill postcondition
Static Methods • Every method must be in a class • Some methods have no implicit parameter • E.g. if all parameters are primitiveclass Numeric{ public static boolean approxEqual(double x, double y){ . . .}}
Call with class name instead of object:if (Numeric.approxEqual(a, b)) . . . • main is static--there aren't any objects yet • Too many static methods are a sign of too little OO
Static Fields • One field per class • public class BankAccount{ . . . private double balance; private int accountNumber; private static int lastAccountNumber;}
public BankAccount(){ lastAssignedNumber++; // increment static field accountNumber = lastAssignedNumber; // set instance field} • Minimize the use of static fields. (Static final fields are ok.)
Scope • Scope of variable: region of program where you can refer to the variable by its name • Local variable scope: from definition to end of block • Class scope: all methods of the class • Must qualify public members outside scope, e.g. Math.sqrt
Overlapping scope: local scope wins over class scope • public class Coin{ public void draw(Graphics2D g2) { String name = "SansSerif"; // local scope g2.setFont(new Font(name, . . .)); // local name g2.drawString(this.name, . . .); // field name } private String name; // class scope . . .}
Placing Classes into Packages • To place classes in package, start file withpackage packagename; • package com.horstmann.bigjava;public class Numeric{ . . .} • Default package has no name, no package statement
Importing Packages • Can always use class without importing • java.awt.Color backgroundColor = new java.awt.Color(. . .); • Tedious to use fully qualified name Import lets you use shorter class name • import java.awt.Color;. . .Color backgroundColor = new Color(. . .); • Can import all classes in a package • import java.awt.*; • Never need to import java.lang
Package Names • Package names should be unambiguous Recommendation: start with reversed domain nameorg.omg.CORBAcom.horstmann.bigjava Path name should match package namecom/horstmann/bigjava/Numeric.java Path name starts with class pathexport CLASSPATH=/home/walters:.