Horstmann: Big Java
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
Choosing Classes
- A class represents a single concept
- Concepts from mathematics:
Point
Rectangle
Ellipse
- Concepts from real life
BankAccount
Purse
- Actors (end in -er, -or)
StringTokenizer
Random (better called RandomNumberGenerator)
- Utility classes--no objects, only static methods
Math
Cohesion
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 = a bad thing
- Minimize coupling to minimize the impact of interface changes
Dependency Relationship between Purse and Coin
Classes
High and Low Coupling between Classes
Accessor and Mutator Classes
- Accessor: does not change the state of the implicit parameter
(e.g. getBalance for a bank account)
- 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
(i.e. the object calling the method)
- Example: modify explicit parameter (in this case, another object)
public void transfer(double amount, BankAccount other)
{
balance = balance - amount;
other.balance = other.balance + amount;
}
- Example: printing in method is a side effect, and should be
avoided:
public void deposit(double amount)
{
if (amount < 0)
System.out.println("Bad value");
. . .
}
- Reprehensible: couples with System, PrintStream
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
- Better: method throws 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 this
balance = 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 fault
balance = balance + amount;
Postconditions