Designing with Patterns Workshop
Designing with Interfaces
Agenda
-
Appreciate the significance of the
interface.
-
Use
interfaces to define large families or to say "What objects can do."
-
Use
interface extension to decouple services or restrict the semantic contract.
-
Consider creating convenience implementations of your interfaces.
-
Use abstract classes to provide default behavior or define small families.
Java's Interface
-
Guideline: Appreciate the significance of the
interface.
-
Understood the mechanics, but not the point
-
"How were Java's interfaces an improvement over C++'s multiple inheritance
mechanism?"
-
Clue: I was using interfaces a lot more than I ever used multiple inheritance
in C++
Code Reuse with Composition
-
Code Reuse: YES --
OverdraftAccount reuses all methods in Account
and OverdraftEventManager
-
Polymorphism: NO
-- Can't pass an
OverdraftAccount to a method that expects an Account
or OverdraftEventManager
1 package com.artima.examples.account.ex3;
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 * Helper back-end <code>OverdraftEventManager</code> object
30 */
31 private OverdraftEventManager eventMan
32 = new OverdraftEventManager();
33
34 /**
35 * The maximum amount the bank will loan to the client.
36 */
37 private final long overdraftMax;
38
39 /**
40 * The current amount the bank has loaned to the client which has not yet
41 * been repaid. This value must at all times be greater than or equal to
42 * zero, and less than or equal to <code>overdraftMax</code>. If this
43 * value is greater than zero, then the balance of the helper
44 * <code>Account</code> (referenced from the <code>account</code>
45 * instance variable) must be exactly zero.
46 */
47 private long overdraft;
48
49 /**
50 * Constructs a new <code>OverdraftAccount</code> with the passed
51 * overdraft maximum.
52 *
53 * @param overdraftMax the maximum amount the bank will loan to the
54 * client
55 */
56 public OverdraftAccount(long overdraftMax) {
57 this.overdraftMax = overdraftMax;
58 }
59
60 /**
61 * Returns the current overdraft, the amount the bank has loaned to the
62 * client that has not yet been repaid.
63 *
64 * @return the current overdraft
65 */
66 public long getOverdraft() {
67 return overdraft;
68 }
69
70 /**
71 * Returns the overdraft maximum, the maximum amount the bank will allow
72 * the client to owe it. For each instance of
73 * <code>OverdraftAccount</code>, the overdraft maximum is constant.
74 *
75 * @return the overdraft maximum
76 */
77 public long getOverdraftMax() {
78 return overdraftMax;
79 }
80
81 /**
82 * Gets the current balance of this <code>OverdraftAccount</code>.
83 *
84 * @return the current balance
85 */
86 public long getBalance() {
87 return account.getBalance();
88 }
89
90 /**
91 * Withdraws the passed amount from this <code>OverdraftAccount</code>.
92 * If the passed amount is less than or equal to the current balance, all
93 * withdrawn funds will be taken from the balance, and the balance will
94 * be decremented by the passed amount. If the passed amount exceeds the
95 * current balance, the bank may loan the client the difference. The bank
96 * will make the loan only if the difference between the passed amount
97 * and the balance (the shortfall) is less than or equal to the available
98 * overdraft. The available overdraft is equal to the current overdraft
99 * (the amount already loaned to the client and not yet repaid),
100 * subtracted from the overdraft maximum, which is passed to the
101 * constructor of any <code>OverdraftAccount</code>.
102 *
103 * <p>
104 * If the passed amount is less than or equal to the current balance, the
105 * <code>withdraw</code> method decrements the current balance by the
106 * passed amount and returns the passed amount. If the passed amount is
107 * greater than the current balance, but the passed amount minus the
108 * current balance is less than or equal to the available overdraft, the
109 * <code>withdraw</code> method sets the current balance to zero, records
110 * the loan, and returns the requested amount. Otherwise, the passed
111 * amount minus the current balance exceeds the available overdraft, so
112 * the <code>withdraw</code> method throws
113 * <code>InsufficientFundsException</code>.
114 *
115 * Subclasses must withdraw at least the passed amount, but may
116 * effectively withdraw more. For example, if a subclass includes a
117 * notion of a withrawal fee, the subclass's implementation of this
118 * method may charge that fee by decrementing it from the account at the
119 * time of withdrawal.
120 *
121 * @param amount amount to withdraw
122 * @return amount withdrawn from the <code>OverdraftAccount</code>
123 * @throws InsufficientFundsException if the
124 * <code>OverdraftAccount</code> contains insufficient funds for the
125 * requested withdrawal
126 * @throws IllegalArgumentException if requested withdrawal amount is
127 * less than or equal to zero.
128 */
129 public long withdraw(long amount) throws InsufficientFundsException {
130
131 if (amount <= 0) {
132 throw new IllegalArgumentException();
133 }
134
135 long bal = account.getBalance();
136 if (bal >= amount) {
137
138 // Balance has sufficient funds, so just take the
139 // money from the balance.
140 return account.withdraw(amount);
141 }
142
143 long shortfall = amount - bal;
144 long extraAvailable = overdraftMax - overdraft;
145
146 if (shortfall > extraAvailable) {
147 throw new InsufficientFundsException(shortfall
148 - extraAvailable);
149 }
150 overdraft += shortfall;
151 account.withdraw(amount - shortfall);
152
153 OverdraftEvent event = new OverdraftEvent(this, overdraft,
154 shortfall);
155 eventMan.fireOverdraftOccurred(event);
156
157 return amount;
158 }
159
160 /**
161 * Deposits the passed amount into the <code>OverdraftAccount</code>. If
162 * the current overdraft is zero, the balance will be increased by the
163 * passed amount. Otherwise, the bank will attempt to pay off the
164 * overdraft first, before increasing the current balance by the amount
165 * remaining after the overdraft is repaid, if any.
166 *
167 * <p>
168 * For example, if the balance is 0, the overdraft is 100, and the
169 * <code>deposit</code> method is invoked with a passed
170 * <code>amount</code> of 50, the bank would use all 50 of those monetary
171 * units to pay down the overdraft. The overdraft would be reduced to 50
172 * and the balance would remain at 0. If subsequently, the client
173 * deposits another 100 units, the bank would use 50 of those units to
174 * pay off the overdraft loan and direct the remaining 50 into the
175 * balance. The new overdraft would be 0 and the new balance would be
176 * 50.
177 *
178 * Subclasses may effectively deposit more or less than the passed amount
179 * into the <code>Account</code>. For example, if a subclass includes a
180 * notion of funds matching, the subclass implementation of this method
181 * may match some or all of the deposited amount at the time of deposit,
182 * effectively increasing the deposited amount. Or, if a subclass
183 * includes the notion of a deposit fee, the subclass's implementation of
184 * this method may charge that fee by decrementing it from the account at
185 * the time of deposit, effectively reducing the deposited amount.
186 *
187 * @param amount amount to deposit
188 * @throws ArithmeticException if requested deposit would cause the
189 * balance of this <code>Account</code> to exceed Long.MAX_VALUE.
190 * @throws IllegalArgumentException if requested withdrawal amount is
191 * less than or equal to zero.
192 */
193 public void deposit(long amount) {
194
195 if (amount <= 0) {
196 throw new IllegalArgumentException();
197 }
198
199 if (overdraft > 0) {
200
201 long amountRepaid = 0;
202
203 if (amount < overdraft) {
204 amountRepaid = amount;
205 overdraft -= amount;
206 }
207 else {
208 long diff = amount - overdraft;
209 amountRepaid = diff;
210 overdraft = 0;
211 account.deposit(diff);
212 }
213
214 OverdraftEvent event = new OverdraftEvent(this, overdraft,
215 amountRepaid);
216 eventMan.fireOverdraftRepaid(event);
217 }
218 else {
219 account.deposit(amount);
220 }
221 }
222
223 /**
224 * Adds the specified overdraft listener to receive overdraft events from
225 * this <code>OverdraftAccount</code>. If <code>l</code> is
226 * <code>null</code>, no exception is thrown and no action is performed.
227 * If <code>l</code> is already registered as a listener, no action is
228 * performed.
229 *
230 * @param l the <code>OverdraftEventListener</code> to add
231 */
232 public void addOverdraftListener(OverdraftListener l) {
233
234 eventMan.addOverdraftListener(l);
235 }
236
237 /**
238 * Removes the specified overdraft listener so that it no longer receives
239 * overdraft events from this <code>OverdraftAccount</code>. This method
240 * performs no function, nor does it throw an exception, if the listener
241 * specified by the argument was not previously added to this component.
242 * If <code>l</code> is <code>null</code>, no exception is thrown and no
243 * action is performed.
244 *
245 * @param l the <code>OverdraftEventListener</code> to remove
246 */
247 public void removeOverdraftListener(OverdraftListener l) {
248
249 eventMan.removeOverdraftListener(l);
250 }
251 }
1 package com.artima.examples.account.ex3;
2
3 /**
4 * Represents a bank account. Money is stored in this account in integral
5 * units. Clients can use this account to store any kind of value, such as
6 * money or points, etc. The meaning of the integral units stored in this
7 * account is a decision of the client that instantiates the account. The
8 * maximum amount of units that can be stored as the current balance of an
9 * <code>Account</code> is Long.MAX_VALUE.
10 */
11 public class Account {
12
13 /**
14 * The current balance
15 */
16 private long balance;
17
18 /**
19 * Withdraws exactly the passed amount from the <code>Account</code>.
20 * Subclasses must withdraw at least the passed amount, but may
21 * effectively withdraw more. For example, if a subclass includes a
22 * notion of a withrawal fee, the subclass's implementation of this
23 * method may charge that fee by decrementing it from the account at the
24 * time of withdrawal.
25 *
26 * @param amount amount to withdraw
27 * @return amount withdrawn from the <code>Account</code>
28 * @throws InsufficientFundsException if the <code>Account</code>
29 * contains insufficient funds for the requested withdrawal
30 * @throws IllegalArgumentException if the passed <code>amount</code> to
31 * withdraw is less than or equal to zero.
32 */
33 public long withdraw(long amount)
34 throws InsufficientFundsException {
35
36 if (amount <= 0) {
37 throw new IllegalArgumentException();
38 }
39
40 if (amount > balance) {
41 throw new InsufficientFundsException(amount - balance);
42 }
43
44 balance -= amount;
45 return amount;
46 }
47
48 /**
49 * Deposits exactly the passed amount into the <code>Account</code>.
50 * Subclasses may effectively deposit more or less than the passed amount
51 * into the <code>Account</code>. For example, if a subclass includes a
52 * notion of funds matching, the subclass implementation of this method
53 * may match some or all of the deposited amount at the time of deposit,
54 * effectively increasing the deposited amount. Or, if a subclass
55 * includes the notion of a deposit fee, the subclass's implementation of
56 * this method may charge that fee by decrementing it from the account at
57 * the time of deposit, effectively reducing the deposited amount.
58 *
59 * @param amount amount to deposit
60 * @throws ArithmeticException if requested deposit would cause the
61 * balance of this <code>Account</code> to exceed Long.MAX_VALUE.
62 * @throws IllegalArgumentException if requested deposit is less than
63 * or equal to zero.
64 */
65 public void deposit(long amount) {
66
67 if (amount <= 0) {
68 throw new IllegalArgumentException();
69 }
70
71 long newBal = balance + amount;
72
73 if (newBal < 0) {
74 throw new ArithmeticException();
75 }
76
77 balance = newBal;
78 }
79
80 /**
81 * Gets the current balance of this <code>Account</code>
82 *
83 * @return the current balance
84 */
85 public long getBalance() {
86 return balance;
87 }
88 }
89
1 package com.artima.examples.account.ex3;
2
3 import java.util.Set;
4 import java.util.Iterator;
5 import java.util.HashSet;
6
7 /**
8 * A class that manages registration and unregistration of
9 * <code>OverdraftListener</code>s and the firing of
10 * <code>OverdraftEvent</code>s.
11 *
12 * @author Bill Venners
13 */
14 class OverdraftEventManager {
15
16 /**
17 * Unsynchronized <code>HashSet</code> to which listeners are added and
18 * removed via the synchronized methods <code>addOverdraftListener</code>
19 * and <code>removeOverdraftListener</code> methods.
20 */
21 private HashSet listeners = new HashSet();
22
23 /**
24 * Clone of the <code>listeners</code> <code>HashSet</code>, which is
25 * used by the <code>fireOverdraftOccurred</code> and
26 * <code>fireOverdraftRepaid</code> methods to propagate events. This
27 * <code>HashSet</code> always contains a most recent snapshot of the
28 * <code>listeners</code> <code>HashSet</code>, but this
29 * <code>HashSet</code> is never modified, only replaced. Because this
30 * <code>HashSet</code> is never modified, the fire methods can iterate
31 * through the set without synchronization. This implementation approach
32 * is geared towards providing optimum performance for the expected run
33 * time usage in which adding and removing listeners happens less
34 * frequently than firing events to those listeners.
35 */
36 private Set listenersClone = new HashSet();
37
38 /**
39 * Constructs a new <code>OverdraftEventManager</code>. The
40 * <code>OverdraftEventManager</code> starts its life with an empty
41 * listeners list.
42 */
43 public OverdraftEventManager() {
44 }
45
46 /**
47 * Adds the specified overdraft listener to receive overdraft events. If
48 * <code>l</code> is <code>null</code>, no exception is thrown and no
49 * action is performed. If <code>l</code> is already registered as a
50 * listener, no action is performed.
51 *
52 * @param l the <code>OverdraftEventListener</code> to add
53 */
54 public synchronized void addOverdraftListener(OverdraftListener l) {
55
56 listeners.add(l);
57 listenersClone = (Set) listeners.clone();
58 }
59
60 /**
61 * Removes the specified overdraft listener so that it no longer receives
62 * overdraft events. This method performs no function, nor does it throw
63 * an exception, if the listener specified by the argument was not
64 * previously added to this component. If <code>l</code> is
65 * <code>null</code>, no exception is thrown and no action is performed.
66 *
67 * @param l the <code>OverdraftEventListener</code> to remove
68 */
69 public synchronized void removeOverdraftListener(OverdraftListener l) {
70
71 listeners.remove(l);
72 listenersClone = (Set) listeners.clone();
73 }
74
75 /**
76 * Fires overdraftOccurred events to registered listeners.
77 *
78 * @param event the <code>OverdraftEvent</code> to propagate
79 */
80 public void fireOverdraftOccurred(OverdraftEvent event) {
81
82 Iterator it = listenersClone.iterator();
83 while (it.hasNext()) {
84 OverdraftListener l = (OverdraftListener) it.next();
85 l.overdraftOccurred(event);
86 }
87 }
88
89 /**
90 * Fires overdraftRepaid events to registered listeners.
91 *
92 * @param event the <code>OverdraftEvent</code> to propagate
93 */
94 public void fireOverdraftRepaid(OverdraftEvent event) {
95
96 Iterator it = listenersClone.iterator();
97 while (it.hasNext()) {
98 OverdraftListener l = (OverdraftListener) it.next();
99 l.overdraftRepaid(event);
100 }
101 }
102 }
Full Blown Multiple Inheritance
-
Code Reuse: YES --
OverdraftAccount inherits or invokes all methods in Account
and OverdraftEventManager
-
Polymorphism: YES
-- Can pass an
OverdraftAccount to a method that expects a Account
or OverdraftEventManager
Polymorphism with Pure ABCs
-
Abstract Base Classes (ABCs) are a multiple inheritance special case
-
Polymorphism: YES
-- Can pass an
OverdraftAccount to a method that expects a Account
or OverdraftEventManager
-
Code Reuse: NO --
OverdraftAccount can't reuse methods from Account
and OverdraftEventManager, because none exist
-
(ABCs give you the opposite of composition)
Multiple Inheritance Usefulness
-
Full blown multiple inheritance gives you more code reuse and polymorphism
Useful in 5% of design situations
-
ABC special case of multiple inheritance gives you more polymorphism, but
no code reuse
Useful in 50% of design situations
-
James Gosling took the 50% case and called it the
interface
-
But left the 5% case out. (Can mimic full blown multiple inheritance with
interfaces and composition)
-
Gosling also addressed the diamond problem
Composition with interfaces
-
Polymorphism: YES
-- Can pass an
OverdraftAccount to a method that expects a Account
or OverdraftEventGenerator
-
Code Reuse: YES --
OverdraftAccount reuses all methods in BasicAccount
and OverdraftEventManager
1 package com.artima.examples.account.ex4;
2
3 /**
4 * Represents a bank account. Money is stored in this account in integral
5 * units. Clients can use this account to store any kind of value, such as
6 * money or points, etc. The meaning of the integral units stored in this
7 * account is a decision of the client that instantiates the account. The
8 * maximum amount of units that can be stored as the current balance of an
9 * <code>Account</code> is Long.MAX_VALUE.
10 */
11 public interface Account {
12
13 /**
14 * Withdraws exactly the passed amount from the <code>Account</code>.
15 * Subclasses must withdraw at least the passed amount, but may
16 * effectively withdraw more. For example, if a subclass includes a
17 * notion of a withrawal fee, the subclass's implementation of this
18 * method may charge that fee by decrementing it from the account at the
19 * time of withdrawal.
20 *
21 * @param amount amount to withdraw
22 * @return amount withdrawn from the <code>Account</code>
23 * @throws InsufficientFundsException if the <code>Account</code>
24 * contains insufficient funds for the requested withdrawal
25 * @throws IllegalArgumentException if the passed <code>amount</code> to
26 * withdraw is less than or equal to zero.
27 */
28 long withdraw(long amount) throws InsufficientFundsException;
29
30 /**
31 * Deposits exactly the passed amount into the <code>Account</code>.
32 * Subclasses may effectively deposit more or less than the passed amount
33 * into the <code>Account</code>. For example, if a subclass includes a
34 * notion of funds matching, the subclass implementation of this method
35 * may match some or all of the deposited amount at the time of deposit,
36 * effectively increasing the deposited amount. Or, if a subclass
37 * includes the notion of a deposit fee, the subclass's implementation of
38 * this method may charge that fee by decrementing it from the account at
39 * the time of deposit, effectively reducing the deposited amount.
40 *
41 * @param amount amount to deposit
42 * @throws ArithmeticException if requested deposit would cause the
43 * balance of this <code>Account</code> to exceed Long.MAX_VALUE.
44 * @throws IllegalArgumentException if requested deposit is less than
45 * or equal to zero.
46 */
47 void deposit(long amount);
48
49 /**
50 * Gets the current balance of this <code>Account</code>.
51 *
52 * @return the current balance
53 */
54 long getBalance();
55 }
56
1 package com.artima.examples.account.ex4;
2
3 /**
4 * A generator of <code>OverdraftEvent</code>s. Classes that implement this
5 * interface manages the registration and unregistration of
6 * <code>OverdraftListener</code>s and the firing of
7 * <code>OverdraftEvent</code>s.
8 *
9 * @author Bill Venners
10 */
11 public interface OverdraftEventGenerator {
12
13 /**
14 * Adds the specified overdraft listener to receive overdraft events. If
15 * <code>l</code> is <code>null</code>, no exception is thrown and no
16 * action is performed. If <code>l</code> is already registered as a
17 * listener, no action is performed.
18 *
19 * @param l the <code>OverdraftEventListener</code> to add
20 */
21 void addOverdraftListener(OverdraftListener l);
22
23 /**
24 * Removes the specified overdraft listener so that it no longer receives
25 * overdraft events. This method performs no function, nor does it throw
26 * an exception, if the listener specified by the argument was not
27 * previously added to this component. If <code>l</code> is
28 * <code>null</code>, no exception is thrown and no action is performed.
29 *
30 * @param l the <code>OverdraftEventListener</code> to remove
31 */
32 void removeOverdraftListener(OverdraftListener l);
33 }
1 package com.artima.examples.account.ex4;
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>OverdraftAccount</code> is
19 * Long.MAX_VALUE.
20 */
21 public class OverdraftAccount implements Account, OverdraftEventGenerator {
22
23 /**
24 * Helper back-end <code>BasicAccount</code> object
25 */
26 private BasicAccount account = new BasicAccount();
27
28 /**
29 * Helper back-end <code>OverdraftEventManager</code> object
30 */
31 private OverdraftEventManager eventMan
32 = new OverdraftEventManager();
33
34 /**
35 * The maximum amount the bank will loan to the client.
36 */
37 private final long overdraftMax;
38
39 /**
40 * The current amount the bank has loaned to the client which has not yet
41 * been repaid. This value must at all times be greater than or equal to
42 * zero, and less than or equal to <code>overdraftMax</code>. If this
43 * value is greater than zero, then the balance of the helper
44 * <code>BasicAccount</code> (referenced from the <code>account</code>
45 * instance variable) must be exactly zero.
46 */
47 private long overdraft;
48
49 /**
50 * Constructs a new <code>OverdraftAccount</code> with the passed
51 * overdraft maximum.
52 *
53 * @param overdraftMax the maximum amount the bank will loan to the
54 * client
55 */
56 public OverdraftAccount(long overdraftMax) {
57 this.overdraftMax = overdraftMax;
58 }
59
60 /**
61 * Returns the current overdraft, the amount the bank has loaned to the
62 * client that has not yet been repaid.
63 *
64 * @return the current overdraft
65 */
66 public long getOverdraft() {
67 return overdraft;
68 }
69
70 /**
71 * Returns the overdraft maximum, the maximum amount the bank will allow
72 * the client to owe it. For each instance of
73 * <code>OverdraftAccount</code>, the overdraft maximum is constant.
74 *
75 * @return the overdraft maximum
76 */
77 public long getOverdraftMax() {
78 return overdraftMax;
79 }
80
81 /**
82 * Gets the current balance of this <code>OverdraftAccount</code>.
83 *
84 * @return the current balance
85 */
86 public long getBalance() {
87 return account.getBalance();
88 }
89
90 /**
91 * Withdraws the passed amount from this <code>OverdraftAccount</code>.
92 * If the passed amount is less than or equal to the current balance, all
93 * withdrawn funds will be taken from the balance, and the balance will
94 * be decremented by the passed amount. If the passed amount exceeds the
95 * current balance, the bank may loan the client the difference. The bank
96 * will make the loan only if the difference between the passed amount
97 * and the balance (the shortfall) is less than or equal to the available
98 * overdraft. The available overdraft is equal to the current overdraft
99 * (the amount already loaned to the client and not yet repaid),
100 * subtracted from the overdraft maximum, which is passed to the
101 * constructor of any <code>OverdraftAccount</code>.
102 *
103 * <p>
104 * If the passed amount is less than or equal to the current balance, the
105 * <code>withdraw</code> method decrements the current balance by the
106 * passed amount and returns the passed amount. If the passed amount is
107 * greater than the current balance, but the passed amount minus the
108 * current balance is less than or equal to the available overdraft, the
109 * <code>withdraw</code> method sets the current balance to zero, records
110 * the loan, and returns the requested amount. Otherwise, the passed
111 * amount minus the current balance exceeds the available overdraft, so
112 * the <code>withdraw</code> method throws
113 * <code>InsufficientFundsException</code>.
114 *
115 * Subclasses must withdraw at least the passed amount, but may
116 * effectively withdraw more. For example, if a subclass includes a
117 * notion of a withrawal fee, the subclass's implementation of this
118 * method may charge that fee by decrementing it from the account at the
119 * time of withdrawal.
120 *
121 * @param amount amount to withdraw
122 * @return amount withdrawn from the <code>OverdraftAccount</code>
123 * @throws InsufficientFundsException if the
124 * <code>OverdraftAccount</code> contains insufficient funds for the
125 * requested withdrawal
126 * @throws IllegalArgumentException if requested withdrawal amount is
127 * less than or equal to zero.
128 */
129 public long withdraw(long amount) throws InsufficientFundsException {
130
131 if (amount <= 0) {
132 throw new IllegalArgumentException();
133 }
134
135 long bal = account.getBalance();
136 if (bal >= amount) {
137
138 // Balance has sufficient funds, so just take the
139 // money from the balance.
140 return account.withdraw(amount);
141 }
142
143 long shortfall = amount - bal;
144 long extraAvailable = overdraftMax - overdraft;
145
146 if (shortfall > extraAvailable) {
147 throw new InsufficientFundsException(shortfall
148 - extraAvailable);
149 }
150 overdraft += shortfall;
151 account.withdraw(amount - shortfall);
152
153 OverdraftEvent event = new OverdraftEvent(this, overdraft,
154 shortfall);
155 eventMan.fireOverdraftOccurred(event);
156
157 return amount;
158 }
159
160 /**
161 * Deposits the passed amount into the <code>OverdraftAccount</code>. If
162 * the current overdraft is zero, the balance will be increased by the
163 * passed amount. Otherwise, the bank will attempt to pay off the
164 * overdraft first, before increasing the current balance by the amount
165 * remaining after the overdraft is repaid, if any.
166 *
167 * <p>
168 * For example, if the balance is 0, the overdraft is 100, and the
169 * <code>deposit</code> method is invoked with a passed
170 * <code>amount</code> of 50, the bank would use all 50 of those monetary
171 * units to pay down the overdraft. The overdraft would be reduced to 50
172 * and the balance would remain at 0. If subsequently, the client
173 * deposits another 100 units, the bank would use 50 of those units to
174 * pay off the overdraft loan and direct the remaining 50 into the
175 * balance. The new overdraft would be 0 and the new balance would be
176 * 50.
177 *
178 * Subclasses may effectively deposit more or less than the passed amount
179 * into the <code>OverdraftAccount</code>. For example, if a subclass
180 * includes a notion of funds matching, the subclass implementation of
181 * this method may match some or all of the deposited amount at the time
182 * of deposit, effectively increasing the deposited amount. Or, if a
183 * subclass includes the notion of a deposit fee, the subclass's
184 * implementation of this method may charge that fee by decrementing it
185 * from the account at the time of deposit, effectively reducing the
186 * deposited amount.
187 *
188 * @param amount amount to deposit
189 * @throws ArithmeticException if requested deposit would cause the
190 * balance of this <code>OverdraftAccount</code> to exceed
191 * Long.MAX_VALUE.
192 * @throws IllegalArgumentException if requested withdrawal amount is
193 * less than or equal to zero.
194 */
195 public void deposit(long amount) {
196
197 if (amount <= 0) {
198 throw new IllegalArgumentException();
199 }
200
201 if (overdraft > 0) {
202
203 long amountRepaid = 0;
204
205 if (amount < overdraft) {
206 amountRepaid = amount;
207 overdraft -= amount;
208 }
209 else {
210 long diff = amount - overdraft;
211 amountRepaid = diff;
212 overdraft = 0;
213 account.deposit(diff);
214 }
215
216 OverdraftEvent event = new OverdraftEvent(this, overdraft,
217 amountRepaid);
218 eventMan.fireOverdraftRepaid(event);
219 }
220 else {
221 account.deposit(amount);
222 }
223 }
224
225 /**
226 * Adds the specified overdraft listener to receive overdraft events from
227 * this <code>OverdraftAccount</code>. If <code>l</code> is
228 * <code>null</code>, no exception is thrown and no action is performed.
229 * If <code>l</code> is already registered as a listener, no action is
230 * performed.
231 *
232 * @param l the <code>OverdraftEventListener</code> to add
233 */
234 public void addOverdraftListener(OverdraftListener l) {
235
236 eventMan.addOverdraftListener(l);
237 }
238
239 /**
240 * Removes the specified overdraft listener so that it no longer receives
241 * overdraft events from this <code>OverdraftAccount</code>. This method
242 * performs no function, nor does it throw an exception, if the listener
243 * specified by the argument was not previously added to this component.
244 * If <code>l</code> is <code>null</code>, no exception is thrown and no
245 * action is performed.
246 *
247 * @param l the <code>OverdraftEventListener</code> to remove
248 */
249 public void removeOverdraftListener(OverdraftListener l) {
250
251 eventMan.removeOverdraftListener(l);
252 }
253 }
1 package com.artima.examples.account.ex4;
2
3 /**
4 * Represents a bank account. Money is stored in this account in integral
5 * units. Clients can use this account to store any kind of value, such as
6 * money or points, etc. The meaning of the integral units stored in this
7 * account is a decision of the client that instantiates the account. The
8 * maximum amount of units that can be stored as the current balance of an
9 * <code>BasicAccount</code> is Long.MAX_VALUE.
10 */
11 public class BasicAccount implements Account {
12
13 /**
14 * The current balance
15 */
16 private long balance;
17
18 /**
19 * Withdraws exactly the passed amount from the
20 * <code>BasicAccount</code>. Subclasses must withdraw at least the
21 * passed amount, but may effectively withdraw more. For example, if a
22 * subclass includes a notion of a withrawal fee, the subclass's
23 * implementation of this method may charge that fee by decrementing it
24 * from the account at the time of withdrawal.
25 *
26 * @param amount amount to withdraw
27 * @return amount withdrawn from the <code>BasicAccount</code>
28 * @throws InsufficientFundsException if the <code>BasicAccount</code>
29 * contains insufficient funds for the requested withdrawal
30 * @throws IllegalArgumentException if the passed <code>amount</code> to
31 * withdraw is less than or equal to zero.
32 */
33 public long withdraw(long amount) throws InsufficientFundsException {
34
35 if (amount <= 0) {
36 throw new IllegalArgumentException();
37 }
38
39 if (amount > balance) {
40 throw new InsufficientFundsException(amount - balance);
41 }
42
43 balance -= amount;
44 return amount;
45 }
46
47 /**
48 * Deposits exactly the passed amount into the <code>BasicAccount</code>.
49 * Subclasses may effectively deposit more or less than the passed amount
50 * into the <code>BasicAccount</code>. For example, if a subclass
51 * includes a notion of funds matching, the subclass implementation of
52 * this method may match some or all of the deposited amount at the time
53 * of deposit, effectively increasing the deposited amount. Or, if a
54 * subclass includes the notion of a deposit fee, the subclass's
55 * implementation of this method may charge that fee by decrementing it
56 * from the account at the time of deposit, effectively reducing the
57 * deposited amount.
58 *
59 * @param amount amount to deposit
60 * @throws ArithmeticException if requested deposit would cause the
61 * balance of this <code>BasicAccount</code> to exceed
62 * Long.MAX_VALUE.
63 * @throws IllegalArgumentException if requested deposit is less than
64 * or equal to zero.
65 */
66 public void deposit(long amount) {
67
68 if (amount <= 0) {
69 throw new IllegalArgumentException();
70 }
71
72 long newBal = balance + amount;
73
74 if (newBal < 0) {
75 throw new ArithmeticException();
76 }
77
78 balance = newBal;
79 }
80
81 /**
82 * Gets the current balance of this <code>BasicAccount</code>.
83 *
84 * @return the current balance
85 */
86 public long getBalance() {
87 return balance;
88 }
89 }
90
Why are interfaces so useful?
-
Often, semantic contract of classes to some extent involves implementation (contract is less abstract)
-
ABCs (interfaces): allow you to say what services an object provides, without
saying what that object is (contract is more abstract)
-
Full-blown MI: what an object does intermingles
with what an object is
-
Interfaces give you implementation choices: an abstract method inherited from an
interface can be implemented in many ways:
- explicitly
- inherited from a superclass
- delegated to back-end objects in a composition relationship
-
Interface method parameter and return types allow you to program to interfaces,
not implementations -- makes code very versatile
Interfaces and C++
-
If interfaces are so useful in Java, why is it that you did
not make use of pure virtual base classes in C++?
-
Is there some difference between them which make the Java
interface easier to use?
-
Has Java, by disallowing multiple implementation inheritance,
made you take the step of separating implementation from
interface in a way you would not have done without being
forced to?
-
Also, if you now went back to C++ would you now make much
more use of virtual base classes than you did before?
Discussion
-
Guideline: Understand the significance of the
interface.
How to use interfaces
-
Use
interfaces to define large families or to say "What objects can do."
-
Noun
interfaces say "What objects are," without burdening subclasses with
any implementation
-
Adjective
interfaces say what objects can do, no matter what
they are
-
Tag
interfaces say what you can can do to objects, no matter what
they are
A Noun interface
-
Collection means: an object is a collection of other objects
-
Defines a set of services offered by all collections, syntax and semantics
-
Silent on implementation, because no default implementation makes sense
-
"Collection" is a broad concept that represents a big family
An Adjective interface
-
Comparable means: an object can compare
itself with another object
-
Adjective
interfaces don't say what objects are
-
They say what objects can do, no matter what those objects are
An Tag interface
-
Serializable means: I can serialize an object
-
Tag interfaces don't say what objects can do for you
-
They say what you can do to or with objects
-
Meta-semantics: not a contract, more like a permission
-
After 1.5, probably better accomplished with an annotation
Pulling interfaces out of Thin Air
-
So interfaces are great. They separate implementation from
class signatures, allowing us more freedom to change our
implementation. So should I use an interface to wrap every
class whose implementation might change in the future?
Depending on your level of paranoia about implementation changes,
this could include almost every class of a program.
-
Don't look at classes and try to figure out what
method subsets should form
interfaces
|
where n is the number of methods
|
-
Discover noun
interfaces when the family is so broad that
no default implementation makes sense
-
Often discover adjective
interfaces when thinking about
parameter and return types for method signatures
-
Tag interfaces are, and should be, rare
Discussion
-
Use
interfaces to define large families or to say "What objects can do."
Service Decoupling
-
Guideline: Use
interface extension to decouple services or restrict the semantic contract.
-
Can decouple services from one another, to encourage clients code be as
type-precise as possible
-
List adds void add(int, Object), Object get(int),
ListIterator listIterator(), etc...
Restricting Semantics
-
Subinterface semantics can be more concrete (more restrictive) than superinterface semantics
-
Set doesn't declare any new methods compared to Collection
-
Semantic contract of
boolean add(Object):
Collection - Ensures that this collection contains the specified element.
Set - Adds the specified element to this set if it is not already present.
-
Can't just slap interfaces together -- semantics must be compatible (though not necessarily identical)
Discussion
-
Guideline: Use
interface extension to decouple services or restrict the semantic contract.
Convenience Implementations
-
Guideline: Consider creating convenience implementations of your interfaces.
-
Convenience implementations can help make common things easy
-
Can provide default implementations that are optional
-
MouseAdapter is a convenience implementation of MouseListener
Has empty mouseClicked(), mousePressed(), etc...
-
AbstractEntry is a convenience implementation of Entry
Has default implementations of equals(), hashCode(), and toString()
-
AbstractCollection, AbstractList, AbstractSet,
AbstractMap, AbstractTableModel, ...
Discussion
-
Guideline: Consider creating convenience implementations of your interfaces.
Abstract Classes
-
Guideline: Use abstract classes to provide default behavior or define a family.
-
How do you choose between an abstract class and an
interface?
-
If you have default behavior, then you must use an abstract class
-
Otherwise, use an interface unless you want to communicate limits
-
Type
Canine has one method, howl(), that is abstract
-
If
Canine is an abstract class, then expect subclasses like Dog, Wolf, etc...
-
If
Canine is an interface, then could get implementations like Blender
Discussion
-
Guideline: Use abstract classes to provide default behavior or define a family.