The Artima Developer Community

Designing with Patterns Workshop
Designing with Composition and Inheritance

Agenda


What Objects Are


An Account Class


Discussion


Using Composition


OverdraftAccount as Front-End Class



  1 package com.artima.examples.account.ex1;
  2
  3 /**
  4 * Represents a bank account with overdraft protection. Instances of this
  5 * class are instantiated with a specified maximum overdraft. If a client
  6 * attempts to withdraw more than the current account balance, the bank may
  7 * loan the amount in excess of the balance to the client. The overdraft
  8 * maximum passed to an <code>OverdraftAccount</code>'s constructor is the
  9 * maximum amount the bank will lend to the client in this manner. When a
 10 * client makes a deposit, the bank will pay itself back first before
 11 * increasing the account's balance.
 12 *
 13 * <p>
 14 * Money is stored in this account in integral units. Clients can use this
 15 * account to store any kind of value, such as money or points, etc. The
 16 * meaning of the integral units stored in this account is a decision of the
 17 * client that instantiates the account. The maximum amount of units that can
 18 * be stored as the current balance of an <code>Account</code> is
 19 * Long.MAX_VALUE.
 20 */
 21 public class OverdraftAccount {
 22
 23     /**
 24     * Helper back-end <code>Account</code> object
 25     */
 26     private Account account = new Account();
 27
 28     /**
 29     * The maximum amount the bank will loan to the client.
 30     */
 31     private final long overdraftMax;
 32
 33     /**
 34     * The current amount the bank has loaned to the client which has not yet
 35     * been repaid. This value must at all times be greater than or equal to
 36     * zero, and less than or equal to <code>overdraftMax</code>. If this
 37     * value is greater than zero, then the balance of the helper
 38     * <code>Account</code> (referenced from the <code>account</code>
 39     * instance variable) must be exactly zero.
 40     */
 41     private long overdraft;
 42
 43     /**
 44     * Constructs a new <code>OverdraftAccount</code> with the passed
 45     * overdraft maximum.
 46     *
 47     * @param overdraftMax the maximum amount the bank will loan to the
 48     *     client
 49     */
 50     public OverdraftAccount(long overdraftMax) {
 51         this.overdraftMax = overdraftMax;
 52     }
 53
 54     /**
 55     * Returns the current overdraft, the amount the bank has loaned to the
 56     * client that has not yet been repaid.
 57     *
 58     * @return the current overdraft
 59     */
 60     public long getOverdraft() {
 61         return overdraft;
 62     }
 63
 64     /**
 65     * Returns the overdraft maximum, the maximum amount the bank will allow
 66     * the client to owe it. For each instance of
 67     * <code>OverdraftAccount</code>, the overdraft maximum is constant.
 68     *
 69     * @return the overdraft maximum
 70     */
 71     public long getOverdraftMax() {
 72         return overdraftMax;
 73     }
 74
 75     /**
 76     * Gets the current balance of this <code>OverdraftAccount</code>.
 77     *
 78     * @return the current balance
 79     */
 80     public long getBalance() {
 81         return account.getBalance();
 82     }
 83
 84     /**
 85     * Withdraws the passed amount from this <code>OverdraftAccount</code>.
 86     * If the passed amount is less than or equal to the current balance, all
 87     * withdrawn funds will be taken from the balance, and the balance will
 88     * be decremented by the passed amount. If the passed amount exceeds the
 89     * current balance, the bank may loan the client the difference. The bank
 90     * will make the loan only if the difference between the passed amount
 91     * and the balance (the shortfall) is less than or equal to the available
 92     * overdraft. The available overdraft is equal to the current overdraft
 93     * (the amount already loaned to the client and not yet repaid),
 94     * subtracted from the overdraft maximum, which is passed to the
 95     * constructor of any <code>OverdraftAccount</code>.
 96     *
 97     * <p>
 98     * If the passed amount is less than or equal to the current balance, the
 99     * <code>withdraw</code> method decrements the current balance by the
100     * passed amount and returns the passed amount. If the passed amount is
101     * greater than the current balance, but the passed amount minus the
102     * current balance is less than or equal to the available overdraft, the
103     * <code>withdraw</code> method sets the current balance to zero, records
104     * the loan, and returns the requested amount. Otherwise, the passed
105     * amount minus the current balance exceeds the available overdraft, so
106     * the <code>withdraw</code> method throws
107     * <code>InsufficientFundsException</code>.
108     *
109     * Subclasses must withdraw at least the passed amount, but may
110     * effectively withdraw more. For example, if a subclass includes a
111     * notion of a withrawal fee, the subclass's implementation of this
112     * method may charge that fee by decrementing it from the account at the
113     * time of withdrawal.
114     *
115     * @param amount amount to withdraw
116     * @return amount withdrawn from the <code>OverdraftAccount</code>
117     * @throws InsufficientFundsException if the
118     *     <code>OverdraftAccount</code> contains insufficient funds for the
119     *     requested withdrawal
120     * @throws IllegalArgumentException if requested withdrawal amount is
121     *     less than or equal to zero.
122     */
123     public long withdraw(long amount)
124         throws InsufficientFundsException {
125
126         if (amount <= 0) {
127             throw new IllegalArgumentException();
128         }
129
130         long bal = account.getBalance();
131         if (bal >= amount) {
132
133             // Balance has sufficient funds, so just take the
134             // money from the balance.
135             return account.withdraw(amount);
136         }
137
138         long shortfall = amount - bal;
139         long extraAvailable = overdraftMax - overdraft;
140
141         if (shortfall > extraAvailable) {
142             throw new InsufficientFundsException(shortfall
143                 - extraAvailable);
144         }
145         overdraft += shortfall;
146         account.withdraw(amount - shortfall);
147
148         return amount;
149     }
150
151     /**
152     * Deposits the passed amount into the <code>OverdraftAccount</code>. If
153     * the current overdraft is zero, the balance will be increased by the
154     * passed amount. Otherwise, the bank will attempt to pay off the
155     * overdraft first, before increasing the current balance by the amount
156     * remaining after the overdraft is repaid, if any.
157     *
158     * <p>
159     * For example, if the balance is 0, the overdraft is 100, and the
160     * <code>deposit</code> method is invoked with a passed
161     * <code>amount</code> of 50, the bank would use all 50 of those monetary
162     * units to pay down the overdraft. The overdraft would be reduced to 50
163     * and the balance would remain at 0. If subsequently, the client
164     * deposits another 100 units, the bank would use 50 of those units to
165     * pay off the overdraft loan and direct the remaining 50 into the
166     * balance. The new overdraft would be 0 and the new balance would be
167     * 50.
168     *
169     * Subclasses may effectively deposit more or less than the passed amount
170     * into the <code>Account</code>. For example, if a subclass includes a
171     * notion of funds matching, the subclass implementation of this method
172     * may match some or all of the deposited amount at the time of deposit,
173     * effectively increasing the deposited amount. Or, if a subclass
174     * includes the notion of a deposit fee, the subclass's implementation of
175     * this method may charge that fee by decrementing it from the account at
176     * the time of deposit, effectively reducing the deposited amount.
177     *
178     * @param amount amount to deposit
179     * @throws ArithmeticException if requested deposit would cause the
180     *     balance of this <code>Account</code> to exceed Long.MAX_VALUE.
181     * @throws IllegalArgumentException if requested withdrawal amount is
182     *     less than or equal to zero.
183     */
184     public void deposit(long amount) {
185
186         if (amount <= 0) {
187             throw new IllegalArgumentException();
188         }
189
190         if (overdraft > 0) {
191             if (amount < overdraft) {
192                 overdraft -= amount;
193             }
194             else {
195                 long diff = amount - overdraft;
196                 overdraft = 0;
197                 account.deposit(diff);
198             }
199         }
200         else {
201             account.deposit(amount);
202         }
203     }
204 }

Discussion


Class Extension


OverdraftAccount as Subclass

  1 package com.artima.examples.account.ex2;
  2
  3 /**
  4 * Represents a bank account with overdraft protection. Instances of this
  5 * class are instantiated with a specified maximum overdraft. If a client
  6 * attempts to withdraw more than the current account balance, the bank may
  7 * loan the amount in excess of the balance to the client. The overdraft
  8 * maximum passed to an <code>OverdraftAccount</code>'s constructor is the
  9 * maximum amount the bank will lend to the client in this manner. When a
 10 * client makes a deposit, the bank will pay itself back first before
 11 * increasing the account's balance.
 12 *
 13 * <p>
 14 * As described in superclass <code>Account</code>, money is stored in this
 15 * account in integral units. Clients can use this account to store any kind
 16 * of value, such as money or points, etc. The meaning of the integral units
 17 * stored in this account is a decision of the client that instantiates the
 18 * account. The maximum amount of units that can be stored as the current
 19 * balance of an <code>OverdraftAccount</code> is Long.MAX_VALUE.
 20 */
 21 public class OverdraftAccount extends Account {
 22
 23     /**
 24     * The maximum amount the bank will loan to the client.
 25     */
 26     private final long overdraftMax;
 27
 28     /**
 29     * The current amount the bank has loaned to the client which has not yet
 30     * been repaid. This must be zero to overdraftMax.
 31     */
 32     private long overdraft;
 33
 34     /**
 35     * Constructs a new <code>OverdraftAccount</code> with the passed
 36     * <code>overdraftMax</code>.
 37     *
 38     * @param overdraftMax the maximum amount the bank will loan to the
 39     *     client
 40     * @throws IllegalArgumentException if the passed
 41     *     <code>overdraftMax</code> is less than zero
 42     */
 43     public OverdraftAccount(long overdraftMax) {
 44
 45         if (overdraftMax < 0) {
 46             throw new IllegalArgumentException();
 47         }
 48
 49         this.overdraftMax = overdraftMax;
 50     }
 51
 52     /**
 53     * Returns the current overdraft, the amount the bank has loaned to the
 54     * client that has not yet been repaid.
 55     *
 56     * @return the current overdraft
 57     */
 58     public long getOverdraft() {
 59         return overdraft;
 60     }
 61
 62     /**
 63     * Returns the overdraft maximum, the maximum amount the bank will allow
 64     * the client to owe it. For each instance of
 65     * <code>OverdraftAccount</code>, the overdraft maximum is constant.
 66     *
 67     * @return the overdraft maximum
 68     */
 69     public long getOverdraftMax() {
 70         return overdraftMax;
 71     }
 72
 73     /**
 74     * Withdraws exactly the passed amount from the
 75     * <code>OverdraftAccount</code>. If the passed amount is less than or
 76     * equal to the current balance, all withdrawn funds will be taken from
 77     * the balance, and the balance will be decremented by the passed amount.
 78     * If the passed amount exceeds the current balance, the bank may loan
 79     * the client the difference. The bank will make the loan only if the
 80     * difference between the passed amount and the balance is less than or
 81     * equal to the available overdraft. The available overdraft is equal to
 82     * the current overdraft (the amount already loaned to the client and not
 83     * yet repaid) subtracted from the overdraft maximum, that was passed to
 84     * the constructor of the <code>OverdraftAccount</code>.
 85     *
 86     * <p>
 87     * If the passed amount less the current balance is less than or equal to
 88     * the available overdraft, the <code>withdraw</code> method returns the
 89     * requested amount, sets the current balance to zero, and records the
 90     * loan. Otherwise, if the passed amount less the current balance exceeds
 91     * the available overdraft, the <code>withdraw</code> method throws
 92     * <code>InsufficientFundsException</code>.
 93     *
 94     * @param amount the amount to withdraw
 95     * @return amount withdrawn from the <code>OverdraftAccount</code>
 96     * @throws InsufficientFundsException if the
 97     *     <code>OverdraftAccount</code> contains insufficient funds for the
 98     *     requested withdrawal. For an <code>OverdraftAccount</code>,
 99     *     insufficient funds means the requested amount to withrdraw
100     *     exceeds the sum of the current balance and the available
101     *     overdraft.
102     * @throws IllegalArgumentException if the passed <code>amount</code> to
103     *    withdraw is less than or equal to zero.
104     */
105     public long withdraw(long amount) throws InsufficientFundsException {
106
107         if (amount <= 0) {
108             throw new IllegalArgumentException();
109         }
110
111         long bal = getBalance();
112         if (bal >= amount) {
113
114             // Balance has sufficient funds, just take the
115             // money from the balance.
116             return super.withdraw(amount);
117         }
118
119         long shortfall = amount - bal;
120         long extraAvailable = overdraftMax - overdraft;
121
122         if (shortfall > extraAvailable) {
123             throw new InsufficientFundsException(shortfall - extraAvailable);
124         }
125         overdraft += shortfall;
126         super.withdraw(amount - shortfall);
127
128         // Note that here I'm not checking that the return value of
129         // super.withdraw is equal to amount - shortfall, because I'm
130         // trusting class Account to fulfill its contract.
131         return amount;
132     }
133
134     /**
135     * Deposits exactly the passed amount into the <code>Account</code>. If
136     * the current overdraft is zero, the balance will be increased by the
137     * passed amount. Otherwise, the bank will attempt to pay off the
138     * overdraft first, before increasing the current balance by the amount
139     * remaining after the overdraft is repaid, if any.
140     *
141     * <p>
142     * For example, if the balance is 200, the overdraft is 100, and the
143     * <code>deposit</code> method is invoked with a passed
144     * <code>amount</code> of 50, the bank would use all 50 of those monetary
145     * units to pay down the overdraft. The overdraft would be reduced to 50
146     * and the balance would remain at 200. If subsequently, the client
147     * deposits another 100 units, the bank would use 50 of those units to
148     * pay off the overdraft loan and direct the remaining 50 into the
149     * balance. The new overdraft would be 0 and the new balance would be
150     * 250.
151     *
152     * @param amount amount to deposit
153     * @throws ArithmeticException if requested deposit would cause the
154     *     balance of this <code>OverdraftAccount</code> to exceed
155     *     Long.MAX_VALUE.
156     */
157     public void deposit(long amount) {
158         if (overdraft > 0) {
159             if (amount < overdraft) {
160                 overdraft -= amount;
161             }
162             else {
163                 long diff = amount - overdraft;
164                 overdraft = 0;
165                 super.deposit(diff);
166             }
167         }
168         else {
169             super.deposit(amount);
170         }
171     }
172 }

Roles and Attributes


State and Class


How do You Decide?


Why IS-A?


Discussion


Inheritance vs. Composition

Inheritance Yields (Slightly) Better Performance: Composition Yields Better Flexibility: But:

Discussion


Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use