OOD: Designing classes: Purse and Coin

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
• A cohesive class has a public interface closely related to the single concept that the class represents
• Cohesion is good.
• This class lacks cohesion:
[spoiler title=’Purse and Coin’]

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; ... 
}
•   It has 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(){...} 
   ... 
} 


[/spoiler]

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

Screen Shot 2016-03-15 at 11.37.10 PM

High and Low Coupling between Classes

Screen Shot 2016-03-15 at 11.38.59 PM

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


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)

Why is the CashRegister Class Not Cohesive?

[spoiler title=’CashRegister’]

/**
   A cash register totals up sales and computes change due.
*/
public class CashRegister
{
   public static final double QUARTER_VALUE = 0.25;
   public static final double DIME_VALUE = 0.1;
   public static final double NICKEL_VALUE = 0.05;
   public static final double PENNY_VALUE = 0.01;

   private double purchase;
   private double payment;

   /**
      Constructs a cash register with no money in it.
   */
   public CashRegister()
   {
      purchase = 0;
      payment = 0;
   }

   /**
      Records the purchase price of an item.
      @param amount the price of the purchased item
   */
   public void recordPurchase(double amount)
   {
      purchase = purchase + amount;
   }
   
   /**
      Enters the payment received from the customer.
      @param dollars the number of dollars in the payment
      @param quarters the number of quarters in the payment
      @param dimes the number of dimes in the payment
      @param nickels the number of nickels in the payment
      @param pennies the number of pennies in the payment
   */
   public void enterPayment(int dollars, int quarters, 
         int dimes, int nickels, int pennies)
   {
      payment = dollars + quarters * QUARTER_VALUE + dimes * DIME_VALUE
            + nickels * NICKEL_VALUE + pennies * PENNY_VALUE;
   }
   
   /**
      Computes the change due and resets the machine for the next customer.
      @return the change due to the customer
   */
   public double giveChange()
   {
      double change = payment - purchase;
      purchase = 0;
      payment = 0;
      return change;
   }
}

[/spoiler]

[spoiler title=’CashRegisterTester’]

/**
   This class tests the CashRegister class.
*/
public class CashRegisterTester
{
   public static void main(String[] args)
   {
      CashRegister register = new CashRegister();

      register.recordPurchase(0.75);
      register.recordPurchase(1.50);
      register.enterPayment(2, 0, 5, 0, 0);
      System.out.print("Change: ");
      System.out.println(register.giveChange());
      System.out.println("Expected: 0.25");

      register.recordPurchase(2.25);
      register.recordPurchase(19.25);
      register.enterPayment(23, 2, 0, 0, 0);
      System.out.print("Change: ");
      System.out.println(register.giveChange());
      System.out.println("Expected: 2.0");
   }
}



[/spoiler]

[spoiler title=’CashRegisterSimulator’]

import java.util.Scanner;

/**
   This program simulates a transaction in which a user pays for an item
   and receives change.
*/
public class CashRegisterSimulator
{
   public static void main(String[] args)
   {
      Scanner in = new Scanner(System.in);

      CashRegister register = new CashRegister();

      System.out.print("Enter price: ");
      double price = in.nextDouble();
      register.recordPurchase(price);

      System.out.print("Enter dollars: ");
      int dollars = in.nextInt();
      System.out.print("Enter quarters: ");
      int quarters = in.nextInt();
      System.out.print("Enter dimes: ");
      int dimes = in.nextInt();
      System.out.print("Enter nickels: ");
      int nickels = in.nextInt();
      System.out.print("Enter pennies: ");
      int pennies = in.nextInt();
      register.enterPayment(dollars, quarters, dimes, nickels, pennies);

      System.out.print("Your change: ");
      System.out.println(register.giveChange());
   }
}

[/spoiler]