The Artima Developer Community

Designing with Patterns Workshop
Designing with Object Styles

Agenda


Significance of the Object


Managing Complexity


Managing Change


Discussion


Object as Service


Move the Code to the Data

  1 package com.artima.examples.matrix.ex1;
  2
  3 import java.io.Serializable;
  4
  5 /**
  6 * Represents a matrix each of whose elements is an <CODE>int</CODE>.
  7 *
  8 * @author Bill Venners
  9 */
 10 public class Matrix implements Serializable, Cloneable {
 11
 12     private int[][] elements;
 13     private int rowCount;
 14     private int colCount;
 15
 16     /**
 17     * Construct a new <EM>square zero matrix</EM> whose order is determined
 18     * by the passed number of rows. (The matrix is square. It has the same
 19     * number of rows and columns.) All elements of the new
 20     * <CODE>Matrix</CODE> will be initialized to zero.
 21     *
 22     * @param rows The number of rows and columns in the new
 23     *     <CODE>Matrix</CODE>
 24     * @exception IllegalArgumentException if <code>rows</code> is less than
 25     *     zero
 26     */
 27     public Matrix(int rows) {
 28
 29         if (rows < 1) {
 30             throw new IllegalArgumentException();
 31         }
 32
 33         elements = new int[rows][rows];
 34         rowCount = rows;
 35         colCount = rows;
 36     }
 37
 38     /**
 39     * Construct a new <EM>zero matrix</EM> whose order is determined by the
 40     * passed number of rows and columns. The order is (rows by columns).
 41     * All elements of the new <CODE>Matrix</CODE> will be initialized to
 42     * zero.
 43     *
 44     * @param rows The number of rows in the new <CODE>Matrix</CODE>
 45     * @param cols The number of columns in the new <CODE>Matrix</CODE>
 46     * @exception IllegalArgumentException if <code>rows</code> or
 47     *     <code>cols</code> is less than zero
 48     */
 49     public Matrix(int rows, int cols) {
 50
 51         if (rows < 1 || cols < 1) {
 52             throw new IllegalArgumentException();
 53         }
 54
 55         elements = new int[rows][cols];
 56         rowCount = rows;
 57         colCount = cols;
 58     }
 59
 60     /**
 61     * Construct a new <CODE>Matrix</CODE> whose elements will be initialized
 62     * with values from the passed two-dimensional array of
 63     * <CODE>int</CODE>s. The order of the matrix will be determined by the
 64     * sizes of the passed arrays. For example, a two dimensional array
 65     * constructed with <CODE>new int[4][9]</CODE>, would yield a matrix
 66     * whose order is 4 by 9. The lengths of each of the arrays held from the
 67     * initial array must be the same. The two-dimensional array passed as
 68     * <CODE>init</CODE> will not be used as part of the state of the newly
 69     * constructed <CODE>Matrix</CODE> object.
 70     *
 71     * @param init A two-dimensional array of <code>int</code>s with which
 72     *     to initialize the new <code>Matrix</code>
 73     * @exception IllegalArgumentException if the length of any passed array
 74     *     is zero, or if the length of all the secondary arrays are not
 75     *     equivalent.
 76     */
 77     public Matrix(int[][] init) {
 78
 79         checkValidity(init);
 80
 81         elements = (int[][]) init.clone();
 82         rowCount = init.length;
 83         colCount = init[0].length;
 84     }
 85
 86     /**
 87     * Returns the element value at the specified row and column.
 88     *
 89     * @param row The row of the element whose value is to be returned
 90     * @param col The column of the element whose value is to be returned
 91     * @return value of element at specified row and column
 92     * @exception IndexOutOfBoundsException if <code>row</code> is less than
 93     *    zero or greater than the number of rows minus 1, or if
 94     *    <code>col</code> is less than 0 or greater than the number of
 95     *    columns minus 1.
 96     */
 97     public int get(int row, int col) {
 98         checkIndices(row, col);
 99         return elements[row][col];
100     }
101
102     /**
103     * Sets the element value at the specified row and column to the
104     * passed value.
105     *
106     * @param row The row of the element whose value is to be set
107     * @param col The column of the element whose value is to be set
108     * @param value The new value of the element indicated by row and col
109     * @exception IndexOutOfBoundsException if <code>row</code> is less than
110     *    zero or greater than the number of rows minus 1, or if
111     *    <code>col</code> is less than 0 or greater than the number of
112     *    columns minus 1.
113     */
114     public void set(int row, int col, int value) {
115         checkIndices(row, col);
116         elements[row][col] = value;
117     }
118
119     /**
120     * Returns the number of rows in this matrix.
121     *
122     * @return number of rows in this <code>Matrix</code>
123     */
124     public int getRows() {
125         return rowCount;
126     }
127
128     /**
129     * Returns the number of cols in this matrix.
130     *
131     * @return number of columns in this <code>Matrix</code>
132     */
133     public int getCols() {
134         return colCount;
135     }
136
137     /**
138     * Clones this object.
139     *
140     * @return A clone of this <code>Matrix</code>
141     */
142     public Object clone() {
143         try {
144             Matrix clone = (Matrix) super.clone();
145             clone.elements = (int[][]) elements.clone();
146             return clone;
147         }
148         catch (CloneNotSupportedException e) {
149             // Can't happen
150             throw new InternalError();
151         }
152     }
153
154     /**
155     * Compares passed <CODE>Matrix</CODE> to this <code>Matrix</code> for
156     * equality. Two <code>Matrix</code> objects are semantically equal if
157     * they have the same order (i.e., same number of rows and columns), and
158     * the <code>int</code> value of each element in this <code>Matrix</code>
159     * is equal to the corresponding <code>int</code> value in the passed
160     * <code>Matrix</code>.
161     *
162     * @param An object to compare to this <code>Matrix</code>
163     * @return <code>true</code> if this <code>Matrix</code> is semantically
164     *     equal to the passed <code>Matrix</code>
165     */
166     public boolean equals(Object o) {
167
168         if ((o == null) || (getClass() != o.getClass())) {
169             return false;
170         }
171
172         // This can't fail because the class of o is exactly Matrix,
173         // as was proven when (getClass() != o.getClass()) returned false
174         Matrix m = (Matrix) o;
175
176         // Because this class extends Object, don't call super.equals()
177
178         // To be semantically equal, both matrices must have the same order
179         if ((rowCount != m.rowCount) || (colCount != m.colCount)) {
180             return false;
181         }
182
183         // To be semantically equal, corresponding elements of both
184         // matrices must be equal
185         for (int row = 0; row < rowCount; ++row) {
186             for (int col = 0; col < colCount; ++col) {
187
188                 if (elements[row][col] != m.elements[row][col]) {
189                     return false;
190                 }
191             }
192         }
193
194         return true;
195     }
196
197     /**
198     * Computes the hash code for this <code>Matrix</code>.
199     *
200     * @return a hashcode value for this <code>Matrix</code>
201     */
202     public int hashcode() {
203
204         int retVal = rowCount * colCount;
205
206         for (int row = 0; row < rowCount; ++row) {
207             for (int col = 0; col < colCount; ++col) {
208
209                     retVal ^= elements[row][col];
210             }
211         }
212
213         return retVal;
214     }
215     /**
216     * Ensures passed two-dimensional array is valid for initializing a
217     * <CODE>Matrix</CODE> object.
218     */
219     private static void checkValidity(int[][] val) {
220
221         try {
222             int rows = val.length;
223             if (rows == 0) {
224                 throw new IllegalArgumentException();
225             }
226             int cols = val[0].length;
227             if (cols == 0) {
228                 throw new IllegalArgumentException();
229             }
230             for (int i = 1; i < rows; ++i) {
231                 if (val[i].length != cols) {
232                     throw new IllegalArgumentException();
233                 }
234             }
235         }
236         catch (NullPointerException e) {
237             throw new IllegalArgumentException();
238         }
239     }
240
241     /**
242     * Ensures passed row and column represent valid indices into this
243     * <CODE>Matrix</CODE>.
244     */
245     private void checkIndices(int row, int col) {
246         if (row >= rowCount || row < 0 || col >= colCount || col < 0) {
247             throw new IndexOutOfBoundsException();
248         }
249     }
250 }

 1 package com.artima.examples.matrix.ex1;
 2
 3 class Example1 {
 4
 5     public static void main(String[] args) {
 6
 7         int[][] init1 = { {2, 2}, {2, 2} };
 8         int[][] init2 = { {1, 2}, {3, 4} };
 9
10         Matrix m1 = new Matrix(init1);
11         Matrix m2 = new Matrix(init2);
12
13         // Add m1 & m2, store result in a new Matrix object
14         Matrix sum = new Matrix(2, 2);
15         for (int i = 0; i < 2; ++i) {
16             for (int j = 0; j < 2; ++j) {
17                 int addend1 = m1.get(i, j);
18                 int addend2 = m2.get(i, j);
19                 sum.set(i, j, addend1 +  addend2);
20             }
21         }
22
23         // Print out the sum
24         System.out.print("Sum: {");
25         for (int i = 0; i < 2; ++i) {
26             for (int j = 0; j < 2; ++j) {
27                 int val = sum.get(i, j);
28                 System.out.print(val);
29                 if (i == 0 || j == 0) {
30                     System.out.print(", ");
31                 }
32             }
33         }
34         System.out.println("}");
35
36     }
37 }
  1 package com.artima.examples.matrix.ex2;
  2
  3 import java.io.Serializable;
  4
  5 /**
  6 * A two-dimensional matrix of <CODE>int</CODE>s.
  7 *
  8 * <P>
  9 * The <em>order</em> of the matrix is its number of rows and columns. For
 10 * example, the order of a matrix with 5 rows and 4 columns is "5 by 4." A
 11 * matrix with the same number of rows and columns, such as a 3 by 3 matrix,
 12 * is a <em>square matrix</em>. A matrix all of whose elements is zero is a
 13 * <em>zero matrix</em>.
 14 *
 15 * <P>
 16 * Instances of <CODE>Matrix</CODE> are immutable.
 17 *
 18 * @author Bill Venners
 19 */
 20 public class Matrix implements Serializable, Cloneable {
 21
 22     private int[][] elements;
 23     private int rowCount;
 24     private int colCount;
 25
 26     /**
 27     * Construct a new square <code>Matrix</code> whose order is determined
 28     * by the passed number of rows. Yields a zero matrix, i.e., all elements
 29     * of the new <CODE>Matrix</CODE> will be initialized to zero.
 30     *
 31     * @param rows The number of rows and columns in the new square
 32     *     <CODE>Matrix</CODE>
 33     * @exception IllegalArgumentException if <code>rows</code> is less than
 34     *     one
 35     */
 36     public Matrix(int rows) {
 37
 38         if (rows < 1) {
 39             throw new IllegalArgumentException();
 40         }
 41
 42         elements = new int[rows][rows];
 43         rowCount = rows;
 44         colCount = rows;
 45     }
 46
 47     /**
 48     * Construct a new <EM>zero matrix</EM> whose order is determined by the
 49     * passed number of rows and columns. The order is (rows by columns).
 50     * Yields a zero matrix, i.e., all elements of the new
 51     * <CODE>Matrix</CODE> will be initialized to zero.
 52     *
 53     * @param rows The number of rows in the new <CODE>Matrix</CODE>
 54     * @param cols The number of columns in the new <CODE>Matrix</CODE>
 55     * @exception IllegalArgumentException if <code>rows</code> or
 56     *     <code>cols</code> is less than 1
 57     */
 58     public Matrix(int rows, int cols) {
 59
 60         if (rows < 1 || cols < 1) {
 61             throw new IllegalArgumentException();
 62         }
 63
 64         elements = new int[rows][cols];
 65         rowCount = rows;
 66         colCount = cols;
 67     }
 68
 69     /**
 70     * Construct a new <CODE>Matrix</CODE> whose elements will be initialized
 71     * with values from the passed two-dimensional array of
 72     * <CODE>int</CODE>s. The order of the matrix will be determined by the
 73     * sizes of the passed arrays. For example, a two dimensional array
 74     * constructed with <CODE>new int[4][9]</CODE>, would yield a matrix
 75     * whose order is 4 by 9. The lengths of each of the arrays held from the
 76     * initial array must be the same. The two-dimensional array passed as
 77     * <CODE>init</CODE> will not be used as part of the state of the newly
 78     * constructed <CODE>Matrix</CODE> object.
 79     *
 80     * @param init A two-dimensional array of <code>int</code>s with which
 81     *     to initialize the new <code>Matrix</code>
 82     * @param cols The number of columns in the new <CODE>Matrix</CODE>
 83     * @exception IllegalArgumentException if the length of any passed array
 84     *     is zero, or if the length of all the secondary arrays are not
 85     *     equivalent.
 86     */
 87     public Matrix(int[][] init) {
 88
 89         checkValidity(init);
 90
 91         elements = (int[][]) init.clone();
 92         rowCount = init.length;
 93         colCount = init[0].length;
 94     }
 95
 96     /**
 97     * Returns the element value at the specified row and column.
 98     *
 99     * @param row The row of the element whose value is to be returned
100     * @param col The column of the element whose value is to be returned
101     * @return value of element at specified row and column
102     * @exception IndexOutOfBoundsException if <code>row</code> is less than
103     *    zero or greater than the number of rows minus 1, or if
104     *    <code>col</code> is less than 0 or greater than the number of
105     *    columns minus 1.
106     */
107     public int get(int row, int col) {
108
109         if (row >= rowCount || row < 0 || col >= colCount || col < 0) {
110             throw new IndexOutOfBoundsException();
111         }
112
113         return elements[row][col];
114     }
115
116     /**
117     * Returns the number of rows in this <code>Matrix</code>.
118     *
119     * @return number of rows in this <code>Matrix</code>
120     */
121     public int getRows() {
122         return rowCount;
123     }
124
125     /**
126     * Returns the number of columns in this <code>Matrix</code>.
127     *
128     * @return number of columns in this <code>Matrix</code>
129     */
130     public int getCols() {
131         return colCount;
132     }
133
134     /**
135     * Adds the passed <code>Matrix</code> to this one. The order of the
136     * passed <code>Matrix</code> must be identical to the order of this
137     * <code>Matrix</code>.
138     *
139     * <P>
140     * The sum of two <code>Matrix</code> objects is a <code>Matrix</code> of
141     * the same order of the two addends. Each element of the sum
142     * <code>Matrix</code> is equal to the sum of the corresponding elements
143     * in the <code>Matrix</code> addends. For example:
144     *
145     * <PRE>
146     * | 1 2 3 |   |  9 -8  7 |   | 10 -6 10 |
147     * | 4 5 6 | + | -6  5 -4 | = | -2 10  2 |
148     * | 7 8 9 |   | -3  2 -1 |   |  4 10  8 |
149     * </PRE>
150     *
151     * <P>
152     * This method does not throw any exception on overflow.
153     *
154     * @param addend the <code>Matrix</code> to add to this one
155     * @return The sum of this <code>Matrix</code> and the passed
156     *     <code>Matrix</code>
157     * @exception IllegalArgumentException if the order of the passed
158     *     <code>Matrix</code> object differs from the order of this
159     *     <code>Matrix</code>
160     */
161     public Matrix add(Matrix addend) {
162
163         // Make sure addend has the same order as this matrix
164         if ((addend.rowCount != rowCount)
165             || (addend.colCount != colCount)) {
166
167             throw new IllegalArgumentException();
168         }
169
170         Matrix retVal = new Matrix(elements);
171         for (int row = 0; row < rowCount; ++row) {
172             for (int col = 0; col < colCount; ++col) {
173                 retVal.elements[row][col] += addend.elements[row][col];
174             }
175         }
176         return retVal;
177     }
178
179     /**
180     * Subtracts the passed <code>Matrix</code> from this one. The order of
181     * the passed <code>Matrix</code> must be identical to the order of this
182     * <code>Matrix</code>. Returned <code>Matrix</code> equals the sum of
183     * this <code>Matrix</code> and the negation of the passed
184     * <code>Matrix</code>.
185     *
186     * <P>
187     * The difference of two <code>Matrix</code> objects is a
188     * <code>Matrix</code> of the same order of the minuend and subtrahend.
189     * Each element of the sum <code>Matrix</code> is equal to the difference
190     * of the corresponding elements in the minuend (this) and subtrahend
191     * (passed) <code>Matrix</code> objects. For example:
192     *
193     * <PRE>
194     * | 1 2 3 |   |  9 -8  7 |   | -8 10 -4 |
195     * | 4 5 6 | - | -6  5 -4 | = | 10  0 10 |
196     * | 7 8 9 |   | -3  2 -1 |   | 10  6 10 |
197     * </PRE>
198     *
199     * <P>
200     * This method does not throw any exception on overflow.
201     *
202     * @param subtrahend the <code>Matrix</code> to subtract from this one
203     * @return The difference of this <code>Matrix</code> and the passed
204     *     <code>Matrix</code>
205     * @exception IllegalArgumentException if the order of the passed
206     *     <code>Matrix</code> object differs from the order of this
207     *     <code>Matrix</code>
208     */
209     public Matrix subtract(Matrix subtrahend) {
210
211         // To be subtracted, subtrahend must have the same order
212         if ((subtrahend.rowCount != rowCount)
213             || (subtrahend.colCount != colCount)) {
214
215             throw new IllegalArgumentException();
216         }
217
218         Matrix retVal = new Matrix(elements);
219         for (int row = 0; row < rowCount; ++row) {
220             for (int col = 0; col < colCount; ++col) {
221                 retVal.elements[row][col] -= subtrahend.elements[row][col];
222             }
223         }
224         return retVal;
225     }
226
227     /**
228     * Multiplies this matrix by the passed scalar. Returns a new matrix
229     * representing the result of the multiplication. To negate a matrix, for
230     * example, just multiply it by -1.
231     *
232     * <P>
233     * The product of a <code>Matrix</code> and a scalar is a
234     * <code>Matrix</code> of the same order as the <code>Matrix</code>
235     * multiplicand. Each element of the product <code>Matrix</code> is equal
236     * to the product of the corresponding element in the <code>Matrix</code>
237     * multiplicand and the scalar multiplier. For example:
238     *
239     * <PRE>
240     *      | 1 2 3 |   |  -2  -4  -6 |
241     * -2 * | 4 5 6 | = |  -8 -10 -12 |
242     *      | 7 8 9 |   | -14 -16 -18 |
243     * </PRE>
244     *
245     * <P>
246     * This method does not throw any exception on overflow.
247     *
248     * @param addend the <code>Matrix</code> to add to this one
249     * @return The sum of this <code>Matrix</code> and the passed
250     *     <code>Matrix</code>
251     * @exception IllegalArgumentException if the order of the passed
252     *     <code>Matrix</code> object differs from the order of this
253     *     <code>Matrix</code>
254     */
255     public Matrix multiply(int scalar) {
256
257         Matrix retVal = new Matrix(elements);
258         for (int row = 0; row < rowCount; ++row) {
259             for (int col = 0; col < colCount; ++col) {
260                 retVal.elements[row][col] *= scalar;
261             }
262         }
263         return retVal;
264     }
265
266     /**
267     * Multiplies this <code>Matrix</code> (the multiplicand) by the passed
268     * <code>Matrix</code> (the multiplier). The number of columns in this
269     * multiplicand <code>Matrix</code> must equal the number rows in the
270     * passed multiplier <code>Matrix</code>.
271     *
272     * <P>
273     * The product of two <code>Matrix</code> objects is a
274     * <code>Matrix</code> that has the same number of rows as the
275     * multiplicand (this <code>Matrix</code>) and the same number of columns
276     * as the multiplier (passed <code>Matrix</code>). Each element of the
277     * product <code>Matrix</code> is equal to sum of the products of the
278     * elements of corresponding multiplicand row and multiplier column.
279     * For example:
280     *
281     * <PRE>
282     * | 0 1 |   | 6 7 |   | (0*6 + 1*8) (0*7 + 1*9) |   |  8  9 |
283     * | 2 3 | * | 8 9 | = | (2*6 + 3*8) (2*7 + 3*9) | = | 36 41 |
284     * | 4 5 |             | (4*6 + 5*8) (4*7 + 5*9) |   | 64 73 |
285     * </PRE>
286     *
287     * <P>
288     * This method does not throw any exception on overflow.
289     *
290     * @param multiplier the <code>Matrix</code> to multiply to this one
291     * @return A new <code>Matrix</code> representing the product of this
292     *    <code>Matrix</code> and the passed <code>Matrix</code>
293     * @exception IllegalArgumentException if the number of rows of the
294     *     passed <code>Matrix</code> object differs from the number of
295     *     columns of this <code>Matrix</code>
296     */
297     public Matrix multiply(Matrix multiplier) {
298
299         // To do a matrix multiplication, the number of columns in this
300         // matrix must equal the number of rows of the passed multiplicand.
301         if (colCount != multiplier.rowCount) {
302             throw new IllegalArgumentException();
303         }
304
305         // Calculate order of result
306         int resultRows = rowCount;
307         int resultCols = multiplier.colCount;
308
309         // Create array for result
310         int[][] resultArray = new int[resultRows][resultCols];
311
312         Matrix retVal = new Matrix(elements);
313
314         for (int row = 0; row < resultRows; ++row) {
315             for (int col = 0; col < resultCols; ++col) {
316                 for (int i = 0; i < colCount; ++i) {
317
318                     resultArray[row][col] += elements[row][i]
319                         * multiplier.elements[i][col];
320                 }
321             }
322         }
323         return retVal;
324     }
325
326     /**
327     * Returns a <code>String</code> that contains the integer values of the
328     * elements of this <code>Matrix</code>. Each row of element values is
329     * enclosed in parentheses and separated by commas, and the entire result
330     * is enclosed in a set of parentheses. For example, for the matrix:
331     *
332     * <PRE>
333     * | 1 2 3 |
334     * | 4 5 6 |
335     * | 7 8 9 |
336     * </PRE>
337     *
338     * This method would return the string:
339     *
340     * <PRE>
341     * ((1, 2, 3), (4, 5, 6), (7, 8, 9))
342     * </PRE>
343     *
344     * @return A new <code>String</code> representation of the state of this
345     *    <code>Matrix</code>
346     */
347     public String toString() {
348
349         StringBuffer retVal = new StringBuffer("(");
350
351         for (int row = 0; row < rowCount; ++row) {
352             retVal.append("(");
353             for (int col = 0; col < colCount; ++col) {
354                 retVal.append(elements[row][col]);
355                 if (col != colCount - 1) {
356                     retVal.append(", ");
357                 }
358             }
359             retVal.append(")");
360             if (row != rowCount - 1) {
361                 retVal.append(", ");
362             }
363         }
364         retVal.append(")");
365         return retVal.toString();
366     }
367
368     /**
369     * Clones this object.
370     *
371     * @return A clone of this <code>Matrix</code>
372     */
373     public Object clone() {
374         try {
375             Matrix clone = (Matrix) super.clone();
376             clone.elements = (int[][]) elements.clone();
377             return clone;
378         }
379         catch (CloneNotSupportedException e) {
380             // Can't happen
381             throw new InternalError();
382         }
383     }
384
385     /**
386     * Compares passed <CODE>Matrix</CODE> to this <code>Matrix</code> for
387     * equality. Two <code>Matrix</code> objects are semantically equal if
388     * they have the same order (i.e., same number of rows and columns), and
389     * the <code>int</code> value of each element in this <code>Matrix</code>
390     * is equal to the corresponding <code>int</code> value in the passed
391     * <code>Matrix</code>.
392     *
393     * @param An object to compare to this <code>Matrix</code>
394     * @return <code>true</code> if this <code>Matrix</code> is semantically
395     *     equal to the passed <code>Matrix</code>
396     */
397     public boolean equals(Object o) {
398
399         if ((o == null) || (getClass() != o.getClass())) {
400             return false;
401         }
402
403         Matrix m = (Matrix) o;
404
405         // Because this class extends Object, don't
406         // call super.equals()
407
408         // To be semantically equal, both matrices must
409         // have the same order
410         if ((rowCount != m.rowCount) || (colCount != m.colCount)) {
411             return false;
412         }
413
414         // To be semantically equal, corresponding
415         // elements of both matrices must be equal
416         for (int row = 0; row < rowCount; ++row) {
417             for (int col = 0; col < colCount; ++col) {
418
419                 if (elements[row][col] != m.elements[row][col]) {
420                     return false;
421                 }
422             }
423         }
424
425         return true;
426     }
427
428     /**
429     * Computes the hash code for this <code>Matrix</code>.
430     *
431     * @return a hashcode value for this <code>Matrix</code>
432     */
433     public int hashcode() {
434
435         int retVal = rowCount * colCount;
436
437         for (int row = 0; row < rowCount; ++row) {
438             for (int col = 0; col < colCount; ++col) {
439
440                 retVal *= elements[row][col];
441             }
442         }
443
444         return retVal;
445     }
446
447     /**
448     * Ensures passed two-dimensional array is valid for initializing a
449     * <CODE>Matrix</CODE> object.
450     */
451     private static void checkValidity(int[][] val) {
452
453         try {
454             int rows = val.length;
455             if (rows == 0) {
456                 throw new IllegalArgumentException();
457             }
458             int cols = val[0].length;
459             if (cols == 0) {
460                 throw new IllegalArgumentException();
461             }
462             for (int i = 1; i < rows; ++i) {
463                 if (val[i].length != cols) {
464                     throw new IllegalArgumentException();
465                 }
466             }
467         }
468         catch (NullPointerException e) {
469             throw new IllegalArgumentException();
470         }
471     }
472 }

 1 package com.artima.examples.matrix.ex2;
 2
 3 class Example2 {
 4
 5     public static void main(String[] args) {
 6
 7         int[][] init1 = { {2, 2}, {2, 2} };
 8         int[][] init2 = { {1, 2}, {3, 4} };
 9
10         Matrix m1 = new Matrix(init1);
11         Matrix m2 = new Matrix(init2);
12
13         // Add m1 & m2, store result in a new matrix object
14         Matrix sum = m1.add(m2);
15
16         // Print out the sum
17         System.out.println("Sum: " + sum.toString());
18     }
19 }

Services and Properties


Why a Bundle of Services?


Discussion


State, Behavior, and Identity


Stamp Dispenser Requirements


Service-Oriented Stamp Dispenser

  1 package com.artima.examples.stampdispenser.ex1;
  2
  3 import java.util.Set;
  4 import java.util.Iterator;
  5 import java.util.HashSet;
  6
  7 /**
  8 * A stamp dispenser that accepts nickels and dimes and dispenses twenty cent
  9 * stamps.
 10 *
 11 * @author Bill Venners
 12 */
 13 public class StampDispenser {
 14
 15     private final static int STAMP_VALUE = 20;
 16     private int balance;
 17     private Set listeners = new HashSet();
 18
 19     /**
 20     * Constructs a new stamp dispenser with a starting balance of zero.
 21     */
 22     public StampDispenser() {
 23     }
 24
 25     /**
 26     * Adds the specified stamp dispenser listener to receive stamp dispenser
 27     * events from this stamp dispenser. If <code>l</code> is
 28     * <code>null</code>, no exception is thrown and no action is performed.
 29     * If <code>l</code> is already registered as a listener, no action is
 30     * performed.
 31     */
 32     public synchronized void addStampDispenserListener(
 33         StampDispenserListener l) {
 34
 35         listeners.add(l);
 36     }
 37
 38     /**
 39     * Removes the specified stamp dispenser listener so that it no longer
 40     * receives stamp dispenser events from this stamp dispenser. This method
 41     * performs no function, nor does it throw an exception, if the listener
 42     * specified by the argument was not previously added to this component.
 43     * If <code>l</code> is <code>null</code>, no exception is thrown and no
 44     * action is performed.
 45     */
 46     public synchronized void removeStampDispenserListener(
 47         StampDispenserListener l) {
 48
 49         listeners.remove(l);
 50     }
 51
 52     /**
 53     * Add either 5 or 10 cents to the stamp dispenser. If the amount added
 54     * causes the balance to become or exceed 20 cents, the price of a stamp,
 55     * the stamp will be automatically dispensed. If the stamp is dispensed,
 56     * the amount of the balance after the stamp is dispensed is returned to
 57     * the client.
 58     *
 59     * @throws IllegalArgumentException if passed <code>amount</code> doesn't
 60     *    equal either 5 or 10
 61     */
 62     public synchronized void add(int amount) {
 63
 64         if ((amount != 5) && (amount != 10)) {
 65             throw new IllegalArgumentException();
 66         }
 67
 68         balance += amount;
 69
 70         if (balance >= STAMP_VALUE) {
 71
 72             // Dispense a stamp and return any change
 73             // balance - STAMP_VALUE is amount in excess of twenty cents
 74             // (the stamp price) to return as change. After dispensing the
 75             // stamp and returning any change, the new balance will be zero.
 76             StampDispenserEvent event = new StampDispenserEvent(this,
 77                 balance - STAMP_VALUE, 0);
 78             balance = 0;
 79             fireStampDispensed(event, listeners);
 80         }
 81         else {
 82
 83             // Fire an event to indicate the balance has increased
 84             StampDispenserEvent event = new StampDispenserEvent(this,
 85                 amount, balance);
 86             fireCoinAccepted(event, listeners);
 87         }
 88     }
 89
 90     /**
 91     * Returns coins. If the balance is zero, no action is performed.
 92     */
 93     public synchronized void returnCoins() {
 94
 95         // Make sure balance is greater than zero, because no event should
 96         // be fired if the coin return lever is pressed when the stamp
 97         // dispenser has a zero balance
 98         if (balance > 0) {
 99
100             // Return the entire balance to the client
101             StampDispenserEvent event = new StampDispenserEvent(this,
102                 balance, 0);
103             balance = 0;
104             fireCoinsReturned(event, listeners);
105         }
106     }
107
108     /**
109     * Helper method that fires coinAccepted events.
110     *
111     * @param event <code>StampDispenserEvent</code> to propagate
112     * @param listeners <code>Set</code> containing zero to many (and only)
113     *     <code>StampDispenserListener</code>s
114     */
115     private static void fireCoinAccepted(StampDispenserEvent event,
116         Set listeners) {
117
118         Iterator it = listeners.iterator();
119         while (it.hasNext()) {
120             StampDispenserListener l = (StampDispenserListener) it.next();
121             l.coinAccepted(event);
122         }
123     }
124
125     /**
126     * Helper method that fires stampDispensed events.
127     *
128     * @param event <code>StampDispenserEvent</code> to propagate
129     * @param listeners <code>Set</code> containing zero to many (and only)
130     *     <code>StampDispenserListener</code>s
131     */
132     private static void fireStampDispensed(StampDispenserEvent event,
133         Set listeners) {
134
135         Iterator it = listeners.iterator();
136         while (it.hasNext()) {
137             StampDispenserListener l = (StampDispenserListener) it.next();
138             l.stampDispensed(event);
139         }
140     }
141
142     /**
143     * Helper method that fires coinsReturned events.
144     *
145     * @param event <code>StampDispenserEvent</code> to propagate
146     * @param listeners <code>Set</code> containing zero to many (and only)
147     *     <code>StampDispenserListener</code>s
148     */
149     private static void fireCoinsReturned(StampDispenserEvent event,
150         Set listeners) {
151
152         Iterator it = listeners.iterator();
153         while (it.hasNext()) {
154             StampDispenserListener l = (StampDispenserListener) it.next();
155             l.coinsReturned(event);
156         }
157     }
158 }

 1 package com.artima.examples.stampdispenser.ex1;
 2
 3 import java.util.EventObject;
 4
 5 /**
 6 * Event that indicates a stamp dispenser has performed an action. The three
 7 * kinds of actions that cause a stamp dispenser event to be propagated are:
 8 * (1) accepting a coin, (2) dispensing a stamp, (3) returning coins.
 9 *
10 * @author Bill Venners
11 */
12 public class StampDispenserEvent extends java.util.EventObject {
13
14     private int amountReturned;
15     private int balance;
16
17     /**
18     * Constructs a <code>StampDispenserEvent</code> with
19     * <code>amountReturned</code>, and <code>balance</code>.
20     *
21     * @param amountReturned the amount of money, if any, returned to the
22     *     client, either as the result of a coin return or as change when
23     *     dispensing a stamp.
24     * @param balance the amount of money, if any, remaining as the current
25     *     balance of the stamp dispenser after this event has occurred.
26     * @throws IllegalArgumentException if balance is not one of 0, 5, 10, or
27     *     15; or if amountReturned is not one of 0, 5, 10, or 15.
28     */
29     public StampDispenserEvent(StampDispenser source, int amountReturned,
30         int balance) {
31
32         super(source);
33
34         if (balance != 0 && balance != 5 && balance != 10
35             && balance != 15) {
36
37             throw new IllegalArgumentException();
38         }
39
40         if (amountReturned != 0 && amountReturned != 5
41             && amountReturned != 10 && amountReturned != 15) {
42
43             throw new IllegalArgumentException();
44         }
45
46         this.amountReturned = amountReturned;
47         this.balance = balance;
48     }
49
50     /**
51     * Returns the amount of money returned to the client, expressed in units
52     * of American pennies.
53     */
54     public int getAmountReturned() {
55         return amountReturned;
56     }
57
58     /**
59     * Returns the current balance: the amount of money that has been
60     * inserted into the stamp dispenser, but not returned via a coin return
61     * or consumed in exchange for a dispensed stamp. For example, if the
62     * <code>balance</code> is zero and a nickel is added, the
63     * <code>balance</code> in the resulting stamp dispenser event will be 5.
64     * If another dime is added, the <code>balance</code> in the resulting
65     * stamp dispenser event will be 15. If the <code>returnCoins</code>
66     * method is then invoked on the stamp dispenser, the
67     * <code>balance</code> of the resulting stamp dispenser event will be 0.
68     */
69     public int getBalance() {
70         return balance;
71     }
72 }

 1 package com.artima.examples.stampdispenser.ex1;
 2
 3 /**
 4 * Listener interface for receiving stamp dispenser events.
 5 *
 6 * @author Bill Venners
 7 */
 8 public interface StampDispenserListener {
 9
10     /**
11     * Invoked when a stamp has been dispensed. If coins have also been
12     * returned as change, the amount is indicated by the return value of
13     * <CODE>getReturnedAmount</CODE> method of the passed
14     * <code>StampDispenserEvent</code>.
15     */
16     void stampDispensed(StampDispenserEvent e);
17
18     /**
19     * Invoked when coins have been returned as the result of the
20     * <code>returnCoins</code> method being invoked on a
21     * <code>StampDispenser</code>. Coins that are returned as change when a
22     * stamp is dispensed are reported via the event passed to
23     * <code>stampDispensed</code>.
24     */
25     void coinsReturned(StampDispenserEvent e);
26
27     /**
28     * Invoked when coins have been accepted but no stamp has been dispensed.
29     * A coin that causes a stamp to be dispensed does not generate a
30     * <code>coinsAccepted</code> method invocation, just a
31     * <code>stampDispensed</code> method invocation.
32     */
33     void coinAccepted(StampDispenserEvent e);
34 }

The Service-Oriented Object


Discussion


The Messenger


An Exception

package com.artima.examples.account.ex1;

/**
* Exception thrown by Accounts to indicate that a requested
* withdrawal has failed because of insufficient funds.
*/
public class InsufficientFundsException extends Exception {

    /**
    * Minimum additional balance required for the requested withdrawal to
    * succeed.
    */
    private long shortfall;

    /**
    * Constructs an InsufficientFundsException with the passed
    * shortfall and no specified detail message.
    *
    * @param shortfall the amount in excess of available funds that caused a
    *     withdrawal request to fail.
    * @throws IllegalArgumentException if passed shortfall is less than or
    *     equal to zero
    */
    public InsufficientFundsException(long shortfall) {

        if (shortfall <= 0) {
            throw new IllegalArgumentException();
        }

        this.shortfall = shortfall;
    }

    /**
    * Constructs an InsufficientFundsException with the passed
    * detail message and shortfall.
    *
    * @param message the detail message
    * @param shortfall the amount in excess of available funds that caused a
    *     withdrawal request to fail.
    * @throws IllegalArgumentException if passed shortfall is less than or
    *     equal to zero
    */
    public InsufficientFundsException(String message, long shortfall) {

        super(message);

        if (shortfall <= 0) {
            throw new IllegalArgumentException();
        }

        this.shortfall = shortfall;
    }

    /**
    * Returns the shortfall that caused a withrawal request to fail. The
    * shortfall is the minimum additional balance required for the requested
    * withdrawal to succeed.
    *
    * @returns shortfall the amount in excess of available funds that caused
    *     a withdrawal request to fail.
    */
    public long getShortfall() {
        return shortfall;
    }
}

An Event

 1 package com.artima.examples.stampdispenser.ex1;
 2
 3 import java.util.EventObject;
 4
 5 /**
 6 * Event that indicates a stamp dispenser has performed an action. The three
 7 * kinds of actions that cause a stamp dispenser event to be propagated are:
 8 * (1) accepting a coin, (2) dispensing a stamp, (3) returning coins.
 9 *
10 * @author Bill Venners
11 */
12 public class StampDispenserEvent extends java.util.EventObject {
13
14     private int amountReturned;
15     private int balance;
16
17     /**
18     * Constructs a <code>StampDispenserEvent</code> with
19     * <code>amountReturned</code>, and <code>balance</code>.
20     *
21     * @param amountReturned the amount of money, if any, returned to the
22     *     client, either as the result of a coin return or as change when
23     *     dispensing a stamp.
24     * @param balance the amount of money, if any, remaining as the current
25     *     balance of the stamp dispenser after this event has occurred.
26     * @throws IllegalArgumentException if balance is not one of 0, 5, 10, or
27     *     15; or if amountReturned is not one of 0, 5, 10, or 15.
28     */
29     public StampDispenserEvent(StampDispenser source, int amountReturned,
30         int balance) {
31
32         super(source);
33
34         if (balance != 0 && balance != 5 && balance != 10
35             && balance != 15) {
36
37             throw new IllegalArgumentException();
38         }
39
40         if (amountReturned != 0 && amountReturned != 5
41             && amountReturned != 10 && amountReturned != 15) {
42
43             throw new IllegalArgumentException();
44         }
45
46         this.amountReturned = amountReturned;
47         this.balance = balance;
48     }
49
50     /**
51     * Returns the amount of money returned to the client, expressed in units
52     * of American pennies.
53     */
54     public int getAmountReturned() {
55         return amountReturned;
56     }
57
58     /**
59     * Returns the current balance: the amount of money that has been
60     * inserted into the stamp dispenser, but not returned via a coin return
61     * or consumed in exchange for a dispensed stamp. For example, if the
62     * <code>balance</code> is zero and a nickel is added, the
63     * <code>balance</code> in the resulting stamp dispenser event will be 5.
64     * If another dime is added, the <code>balance</code> in the resulting
65     * stamp dispenser event will be 15. If the <code>returnCoins</code>
66     * method is then invoked on the stamp dispenser, the
67     * <code>balance</code> of the resulting stamp dispenser event will be 0.
68     */
69     public int getBalance() {
70         return balance;
71     }
72 }

A Multi-Valued Return Object

  1 /*
  2  * This source code (.java) file is Copyright (C) 2000 Artima Software, Inc.
  3  * All rights reserved. This file accompanies the Jini Place Service Draft
  4  * Specification, written by Bill Venners and published on the World Wide
  5  * Web at:
  6  *
  7  *     http://www.artima.com/jini/cyberspace/DraftSpec.html,
  8  *
  9  * This source file may not be copied, modified, or redistributed EXCEPT as
 10  * allowed by the following statements: From August 11, 2000 through
 11  * December 31, 2000, you may copy and/or modify these files to test and
 12  * experiment with the Place API, described in the Jini Place Service Draft
 13  * Specification. Any bug fixes must be given back to Artima Software, Inc.
 14  * You may not redistribute this file or any binary (such as .class) files
 15  * generated from this file. You may not distribute modified versions this
 16  * files or any binary (such as .class) files generated from modified
 17  * versions of this file. You may not remove this copyright notice. You may
 18  * not use this file in printed media without the express permission of Bill
 19  * Venners. And if that weren't enough, you must destroy all copies of this
 20  * file, and any binary (such as .class) files generated from this file, by
 21  * December 31, 2000.
 22  *
 23  * ARTIMA SOFTWARE, INC. MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE
 24  * SUITABILITY OF THIS SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT
 25  * NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR
 26  * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. BILL VENNERS SHALL NOT BE LIABLE
 27  * FOR ANY DAMAGES SUFFERED BY A LICENSEE AS A RESULT OF USING, MODIFYING OR
 28  * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
 29 */
 30 package net.artima.place;
 31
 32 import java.io.Serializable;
 33
 34 /**
 35 * Associates a <CODE>Link</CODE> to a <CODE>ResourceInfo</CODE>. Keeping the
 36 * <CODE>ResourceInfo</CODE> outside of the <CODE>Link</CODE>, enables
 37 * modifiable places to more easily change the <CODE>ResourceInfo</CODE> data
 38 * for a particular <CODE>Link</CODE>.
 39 *
 40 * @author Bill Venners
 41 */
 42 public class LinkItem implements Serializable {
 43
 44     private Link link;
 45     private ResourceInfo resourceInfo;
 46
 47     /**
 48     * Constructs a <CODE>LinkItem</CODE> with passed <CODE>Link</CODE> and
 49     * <CODE>ResourceInfo</CODE>.
 50     *
 51     * @param link The <code>Link</code> to hold in this
 52     *     <code>LinkItem</code>
 53     * @param resourceInfo The <code>ResourceInfo</code> to hold in this
 54     *     <code>LinkItem</code>
 55     * @throws NullPointerException if the <code>Link</code> or
 56     *     <code>ResourceInfo</code> reference passed to this constructor is
 57     *     <code>null</code>
 58     */
 59     public LinkItem(Link link, ResourceInfo resourceInfo) {
 60
 61         if (link == null || resourceInfo == null) {
 62             throw new NullPointerException();
 63         }
 64
 65         this.link = link;
 66         this.resourceInfo = resourceInfo;
 67     }
 68
 69     /**
 70     * Returns the <CODE>Link</CODE> object that's stored in this
 71     * <CODE>LinkItem</CODE>.
 72     *
 73     * @return the <code>Link</code> held in this <code>LinkItem</code>
 74     */
 75     public Link getLink() {
 76         return link;
 77     }
 78
 79     /**
 80     * Returns the <CODE>ResourceInfo</CODE> object that's stored in this
 81     * <CODE>LinkItem</CODE>. The <CODE>ResourceInfo</CODE> provides
 82     * information, suitable for display to users, about the linked-to
 83     * resource.
 84     *
 85     * @return the <code>ResourceInfo</code> held in this
 86     *     <code>LinkItem</code>
 87     */
 88     public ResourceInfo getResourceInfo() {
 89         return resourceInfo;
 90     }
 91
 92     /**
 93     * Compares the specified <CODE>Object</CODE> with this
 94     * <CODE>LinkItem</CODE> for equality. Two <code>LinkItem</code>s are
 95     * semantically equal if their <code>Link</code>s are semantically equal
 96     * and their <code>ResourceInfo</code>s are semantically equal.
 97     */
 98     public boolean equals(Object o) {
 99
100         if (o == null || (o.getClass() != getClass())) {
101             return false;
102         }
103
104         // This can't fail because the class o is exactly LinkItem,
105         // as was proven when (getClass() != o.getClass()) returned false
106         LinkItem li = (LinkItem) o;
107
108         // Both Link and ResourceInfo must be semantically equal
109         if (!link.equals(li.link) || !resourceInfo.equals(li.resourceInfo)) {
110             return false;
111         }
112
113         return true;
114     }
115
116     /**
117     * Returns the hash code value for this <CODE>LinkItem</CODE>. The
118     * hashcode for a <code>LinkItem</code> is the exclusive OR of the
119     * hashcodes for the <code>LinkItem</code>'s constituent
120     * <code>Link</code> and <code>ResourceInfo</code>.
121     */
122     public int hashCode() {
123
124         return link.hashCode() ^ resourceInfo.hashCode();
125     }
126 }
127

Why Use Messengers?


Discussion


Stamp Dispenser State Machine


State-Transition Table

Current State Message Action Next State
HAS_0 add5   HAS_5
HAS_0 add10   HAS_10
HAS_0 returnCoins   HAS_0
HAS_5 add5   HAS_10
HAS_5 add10   HAS_15
HAS_5 returnCoins ret5 HAS_0
HAS_10 add5   HAS_15
HAS_10 add10 dispenseStamp HAS_0
HAS_10 returnCoins ret10 HAS_0
HAS_15 add5 dispenseStamp HAS_0
HAS_15 add10 dispenseStamp,ret5 HAS_0
HAS_15 returnCoins ret15 HAS_0
Initial State: HAS_0


State Transition Diagram


Mapping State Machine to Object


Code-Heavy Stamp Dispenser

  1 package com.artima.examples.stampdispenser.ex2;
  2
  3 import java.util.Set;
  4 import java.util.Iterator;
  5 import java.util.HashSet;
  6
  7 /**
  8 * A stamp dispenser that accepts nickels and dimes and dispenses twenty cent
  9 * stamps.
 10 *
 11 * @author Bill Venners
 12 */
 13 public class StampDispenser {
 14
 15     private static final int HAS_0 = 0;
 16     private static final int HAS_5 = 1;
 17     private static final int HAS_10 = 2;
 18     private static final int HAS_15 = 3;
 19
 20     private int currentState = HAS_0;
 21     private Set listeners = new HashSet();
 22
 23     /**
 24     * Constructs a new stamp dispenser with a starting balance of zero.
 25     */
 26     public StampDispenser() {
 27     }
 28
 29     /**
 30     * Adds the specified stamp dispenser listener to receive stamp dispenser
 31     * events from this stamp dispenser. If <code>l</code> is
 32     * <code>null</code>, no exception is thrown and no action is performed.
 33     * If <code>l</code> is already registered as a listener, no action is
 34     * performed.
 35     */
 36     public synchronized void addStampDispenserListener(
 37         StampDispenserListener l) {
 38
 39         listeners.add(l);
 40     }
 41
 42     /**
 43     * Removes the specified stamp dispenser listener so that it no longer
 44     * receives stamp dispenser events from this stamp dispenser. This method
 45     * performs no function, nor does it throw an exception, if the listener
 46     * specified by the argument was not previously added to this component.
 47     * If <code>l</code> is <code>null</code>, no exception is thrown and no
 48     * action is performed.
 49     */
 50     public synchronized void removeStampDispenserListener(
 51         StampDispenserListener l) {
 52
 53         listeners.remove(l);
 54     }
 55
 56     /**
 57     * Add 5 cents to the stamp dispenser. If the amount added causes the
 58     * current value to become or exceed 20 cents, the price of a stamp, the
 59     * stamp will be automatically dispensed.
 60     */
 61     public synchronized void add5() {
 62
 63         switch (currentState) {
 64
 65         case HAS_0:
 66             StampDispenserEvent event = new StampDispenserEvent(this, 0, 5);
 67             fireCoinAccepted(event, listeners);
 68             currentState = HAS_5;
 69             break;
 70
 71         case HAS_5:
 72             event = new StampDispenserEvent(this, 0, 10);
 73             fireCoinAccepted(event, listeners);
 74             currentState = HAS_10;
 75             break;
 76
 77         case HAS_10:
 78             event = new StampDispenserEvent(this, 0, 15);
 79             fireCoinAccepted(event, listeners);
 80             currentState = HAS_15;
 81             break;
 82
 83         case HAS_15:
 84
 85             event = new StampDispenserEvent(this, 0, 0);
 86             fireStampDispensed(event, listeners);
 87             currentState = HAS_0;
 88             break;
 89         }
 90     }
 91
 92     /**
 93     * Add 10 cents to the stamp dispenser. If the amount added causes the
 94     * current value to become or exceed 20 cents, the price of a stamp, the
 95     * stamp will be automatically dispensed.
 96     */
 97     public synchronized void add10() {
 98
 99         switch (currentState) {
100
101         case HAS_0:
102             StampDispenserEvent event = new StampDispenserEvent(this,
103                 0, 10);
104             fireCoinAccepted(event, listeners);
105             currentState = HAS_10;
106             break;
107
108         case HAS_5:
109             event = new StampDispenserEvent(this, 0, 15);
110             fireCoinAccepted(event, listeners);
111             currentState = HAS_15;
112             break;
113
114         case HAS_10:
115             event = new StampDispenserEvent(this, 0, 0);
116             fireStampDispensed(event, listeners);
117             currentState = HAS_0;
118             break;
119
120         case HAS_15:
121
122             event = new StampDispenserEvent(this, 5, 0);
123             fireStampDispensed(event, listeners);
124             currentState = HAS_0;
125             break;
126         }
127     }
128
129     /**
130     * Returns coins. If the balance is zero, no action is performed.
131     */
132     public synchronized void returnCoins() {
133
134         switch (currentState) {
135
136         case HAS_0:
137             currentState = HAS_0;
138             break;
139
140         case HAS_5:
141             StampDispenserEvent event = new StampDispenserEvent(this, 5, 0);
142             fireCoinsReturned(event, listeners);
143             currentState = HAS_0;
144             break;
145
146         case HAS_10:
147             event = new StampDispenserEvent(this, 10, 0);
148             fireCoinsReturned(event, listeners);
149             currentState = HAS_0;
150             break;
151
152         case HAS_15:
153
154             event = new StampDispenserEvent(this, 15, 0);
155             fireCoinsReturned(event, listeners);
156             currentState = HAS_0;
157             break;
158         }
159     }
160
161     /**
162     * Helper method that fires coinAccepted events.
163     *
164     * @param event <code>StampDispenserEvent</code> to propagate
165     * @param listeners <code>Set</code> containing zero to many (and only)
166     *     <code>StampDispenserListener</code>s
167     */
168     private static void fireCoinAccepted(StampDispenserEvent event,
169         Set listeners) {
170
171         Iterator it = listeners.iterator();
172         while (it.hasNext()) {
173             StampDispenserListener l = (StampDispenserListener) it.next();
174             l.coinAccepted(event);
175         }
176     }
177
178     /**
179     * Helper method that fires stampDispensed events.
180     *
181     * @param event <code>StampDispenserEvent</code> to propagate
182     * @param listeners <code>Set</code> containing zero to many (and only)
183     *     <code>StampDispenserListener</code>s
184     */
185     private static void fireStampDispensed(StampDispenserEvent event,
186         Set listeners) {
187
188         Iterator it = listeners.iterator();
189         while (it.hasNext()) {
190             StampDispenserListener l = (StampDispenserListener) it.next();
191             l.stampDispensed(event);
192         }
193     }
194
195     /**
196     * Helper method that fires coinsReturned events.
197     *
198     * @param event <code>StampDispenserEvent</code> to propagate
199     * @param listeners <code>Set</code> containing zero to many (and only)
200     *     <code>StampDispenserListener</code>s
201     */
202     private static void fireCoinsReturned(StampDispenserEvent event,
203         Set listeners) {
204
205         Iterator it = listeners.iterator();
206         while (it.hasNext()) {
207             StampDispenserListener l = (StampDispenserListener) it.next();
208             l.coinsReturned(event);
209         }
210     }
211 }

The State Pattern


A State Pattern Stamp Dispenser

 1 package com.artima.examples.stampdispenser.ex3;
 2
 3 import java.util.Set;
 4 import java.util.HashSet;
 5
 6 /**
 7 * A stamp dispenser that accepts nickels and dimes and dispenses twenty cent
 8 * stamps.
 9 *
10 * @author Bill Venners
11 */
12 public class StampDispenser {
13
14     private State currentState = Has0State.getState();
15     private Set listeners = new HashSet();
16
17     /**
18     * Constructs a new stamp dispenser with a starting balance of zero.
19     */
20     public StampDispenser() {
21     }
22
23     /**
24     * Adds the specified stamp dispenser listener to receive stamp dispenser
25     * events from this stamp dispenser. If <code>l</code> is
26     * <code>null</code>, no exception is thrown and no action is performed.
27     * If <code>l</code> is already registered as a listener, no action is
28     * performed.
29     */
30     public synchronized void addStampDispenserListener(
31         StampDispenserListener l) {
32
33         listeners.add(l);
34     }
35
36     /**
37     * Removes the specified stamp dispenser listener so that it no longer
38     * receives stamp dispenser events from this stamp dispenser. This method
39     * performs no function, nor does it throw an exception, if the listener
40     * specified by the argument was not previously added to this component.
41     * If <code>l</code> is <code>null</code>, no exception is thrown and no
42     * action is performed.
43     */
44     public synchronized void removeStampDispenserListener(
45         StampDispenserListener l) {
46
47         listeners.remove(l);
48     }
49
50     /**
51     * Add 5 cents to the stamp dispenser. If the amount added causes the
52     * current value to become or exceed 20 cents, the price of a stamp, the
53     * stamp will be automatically dispensed.
54     */
55     public void add5() {
56
57         currentState = currentState.add5(this, listeners);
58     }
59
60     /**
61     * Add 10 cents to the stamp dispenser. If the amount added causes the
62     * current value to become or exceed 20 cents, the price of a stamp, the
63     * stamp will be automatically dispensed.
64     */
65     public void add10() {
66
67         currentState = currentState.add10(this, listeners);
68     }
69
70     /**
71     * Returns coins. If the balance is zero, no action is performed.
72     */
73     public void returnCoins() {
74
75         currentState = currentState.returnCoins(this, listeners);
76     }
77 }

  1 package com.artima.examples.stampdispenser.ex3;
  2
  3 import java.util.Iterator;
  4 import java.util.Set;
  5
  6 /**
  7 * Abstract superclass for all stamp dispenser state classes.
  8 *
  9 * The state machine that embodies the behavior of the stamp dispenser (which
 10 * determines the behavior of the <CODE>State</code> subclasses) is described
 11 * in this <a href="../../../../../statemachine.html">state transition
 12 * table</a>.
 13 *
 14 * @author Bill Venners
 15 */
 16 abstract class State {
 17
 18     /**
 19     * Performs actions appropriate for the state represented by the class of
 20     * this object for the add5 message. Returns a reference to the state
 21     * object that represents the next state to transition to as a result of
 22     * the arrival of this add5 message.
 23     *
 24     * @param stampDispenser the <code>StampDispenser</code> whose state this
 25     *     object represents
 26     * @param listeners <code>Set</code> containing zero to many (and only)
 27     *     <code>StampDispenserListener</code>s
 28     */
 29     abstract State add5(StampDispenser stampDispenser, Set listeners);
 30
 31     /**
 32     * Performs actions appropriate for the state represented by the class of
 33     * this object for the add10 message. Returns a reference to the state
 34     * object that represents the next state to transition to as a result of
 35     * the arrival of this add10 message.
 36     *
 37     * @param stampDispenser the <code>StampDispenser</code> whose state this
 38     *     object represents
 39     * @param listeners <code>Set</code> containing zero to many (and only)
 40     *     <code>StampDispenserListener</code>s
 41     */
 42     abstract State add10(StampDispenser stampDispenser, Set listeners);
 43
 44     /**
 45     * Performs actions appropriate for the state represented by the class of
 46     * this object for the returnCoins message. Returns a reference to the
 47     * state object that represents the next state to transition to as a
 48     * result of the arrival of this returnCoins message.
 49     *
 50     * @param stampDispenser the <code>StampDispenser</code> whose state this
 51     *     object represents
 52     * @param listeners <code>Set</code> containing zero to many (and only)
 53     *     <code>StampDispenserListener</code>s
 54     */
 55     abstract State returnCoins(StampDispenser stampDispenser,
 56         Set listeners);
 57
 58     /**
 59     * Helper method that fires coinAccepted events.
 60     *
 61     * @param event <code>StampDispenserEvent</code> to propagate
 62     * @param listeners <code>Set</code> containing zero to many (and only)
 63     *     <code>StampDispenserListener</code>s
 64     */
 65     static void fireCoinAccepted(StampDispenserEvent event, Set listeners) {
 66
 67         Iterator it = listeners.iterator();
 68         while (it.hasNext()) {
 69             StampDispenserListener l = (StampDispenserListener) it.next();
 70             l.coinAccepted(event);
 71         }
 72     }
 73
 74     /**
 75     * Helper method that fires stampDispensed events.
 76     *
 77     * @param event <code>StampDispenserEvent</code> to propagate
 78     * @param listeners <code>Set</code> containing zero to many (and only)
 79     *     <code>StampDispenserListener</code>s
 80     */
 81     static void fireStampDispensed(StampDispenserEvent event,
 82         Set listeners) {
 83
 84         Iterator it = listeners.iterator();
 85         while (it.hasNext()) {
 86             StampDispenserListener l = (StampDispenserListener) it.next();
 87             l.stampDispensed(event);
 88         }
 89     }
 90
 91     /**
 92     * Helper method that fires coinsReturned events.
 93     *
 94     * @param event <code>StampDispenserEvent</code> to propagate
 95     * @param listeners <code>Set</code> containing zero to many (and only)
 96     *     <code>StampDispenserListener</code>s
 97     */
 98     static void fireCoinsReturned(StampDispenserEvent event,
 99         Set listeners) {
100
101         Iterator it = listeners.iterator();
102         while (it.hasNext()) {
103             StampDispenserListener l = (StampDispenserListener) it.next();
104             l.coinsReturned(event);
105         }
106     }
107 }

 1 package com.artima.examples.stampdispenser.ex3;
 2
 3 import java.util.Set;
 4
 5 /**
 6 * <code>State</code> subclass that represents the <em>Has0</em> stamp
 7 * dispenser state. See the documentation for class <code>State</code> for a
 8 * state transition table that specifies the required behavior of instances
 9 * of this class.
10 *
11 * @author Bill Venners
12 */
13 class Has0State extends State {
14
15     /**
16     * The single instance of <code>Has0State</code>
17     */
18     private static Has0State singleton = new Has0State();
19
20     /**
21     * Constructs a <code>Has0State</code> instance. This constructor is
22     * private to enable this class to restrict the number of instances of
23     * <code>Has0State</code> to one. I.e., <code>Has0State</code> is a
24     * singleton.
25     */
26     private Has0State() {
27     }
28
29     /**
30     * Factory method that returns the single instance of
31     * <code>Has0State</code>.
32     */
33     static State getState() {
34         return singleton;
35     }
36
37     /**
38     * Performs actions appropriate for the state represented by the class of
39     * this object for the add5 message. Returns a reference to the state
40     * object that represents the next state to transition to as a result of
41     * the arrival of this add5 message.
42     *
43     * @param stampDispenser the <code>StampDispenser</code> whose state this
44     *     object represents
45     * @param listeners <code>Set</code> containing zero to many (and only)
46     *     <code>StampDispenserListener</code>s
47     */
48     State add5(StampDispenser stampDispenser, Set listeners) {
49
50         StampDispenserEvent event = new StampDispenserEvent(stampDispenser,
51             0, 5);
52         fireCoinAccepted(event, listeners);
53         return Has5State.getState();
54     }
55
56     /**
57     * Performs actions appropriate for the state represented by the class of
58     * this object for the add10 message. Returns a reference to the state
59     * object that represents the next state to transition to as a result of
60     * the arrival of this add10 message.
61     *
62     * @param stampDispenser the <code>StampDispenser</code> whose state this
63     *     object represents
64     * @param listeners <code>Set</code> containing zero to many (and only)
65     *     <code>StampDispenserListener</code>s
66     */
67     State add10(StampDispenser stampDispenser, Set listeners) {
68
69         StampDispenserEvent event = new StampDispenserEvent(stampDispenser,
70             0, 10);
71         fireCoinAccepted(event, listeners);
72         return Has10State.getState();
73     }
74
75     /**
76     * Performs actions appropriate for the state represented by the class of
77     * this object for the returnCoins message. Returns a reference to the
78     * state object that represents the next state to transition to as a
79     * result of the arrival of this returnCoins message.
80     *
81     * @param stampDispenser the <code>StampDispenser</code> whose state this
82     *     object represents
83     * @param listeners <code>Set</code> containing zero to many (and only)
84     *     <code>StampDispenserListener</code>s
85     */
86     State returnCoins(StampDispenser stampDispenser, Set listeners) {
87         return this;
88     }
89 }

 1 package com.artima.examples.stampdispenser.ex3;
 2
 3 import java.util.Set;
 4
 5 /**
 6 * <code>State</code> subclass that represents the <em>Has5</em> stamp
 7 * dispenser state. See the documentation for class <code>State</code> for a
 8 * state transition table that specifies the required behavior of instances
 9 * of this class.
10 *
11 * @author Bill Venners
12 */
13 class Has5State extends State {
14
15     /**
16     * The single instance of <code>Has5State</code>
17     */
18     private static Has5State singleton = new Has5State();
19
20     /**
21     * Constructs a <code>Has5State</code> instance. This constructor is
22     * private to enable this class to restrict the number of instances of
23     * <code>Has5State</code> to one. I.e., <code>Has5State</code> is a
24     * singleton.
25     */
26     private Has5State() {
27     }
28
29     /**
30     * Factory method that returns the single instance of
31     * <code>Has5State</code>.
32     */
33     static State getState() {
34         return singleton;
35     }
36
37     /**
38     * Performs actions appropriate for the state represented by the class of
39     * this object for the add5 message. Returns a reference to the state
40     * object that represents the next state to transition to as a result of
41     * the arrival of this add5 message.
42     *
43     * @param stampDispenser the <code>StampDispenser</code> whose state this
44     *     object represents
45     * @param listeners <code>Set</code> containing zero to many (and only)
46     *     <code>StampDispenserListener</code>s
47     */
48     State add5(StampDispenser stampDispenser, Set listeners) {
49
50         StampDispenserEvent event = new StampDispenserEvent(stampDispenser,
51             0, 10);
52         fireCoinAccepted(event, listeners);
53         return Has10State.getState();
54     }
55
56     /**
57     * Performs actions appropriate for the state represented by the class of
58     * this object for the add10 message. Returns a reference to the state
59     * object that represents the next state to transition to as a result of
60     * the arrival of this add10 message.
61     *
62     * @param stampDispenser the <code>StampDispenser</code> whose state this
63     *     object represents
64     * @param listeners <code>Set</code> containing zero to many (and only)
65     *     <code>StampDispenserListener</code>s
66     */
67     State add10(StampDispenser stampDispenser, Set listeners) {
68
69         StampDispenserEvent event = new StampDispenserEvent(stampDispenser,
70             0, 15);
71         fireCoinAccepted(event, listeners);
72         return Has15State.getState();
73     }
74
75     /**
76     * Performs actions appropriate for the state represented by the class of
77     * this object for the returnCoins message. Returns a reference to the
78     * state object that represents the next state to transition to as a
79     * result of the arrival of this returnCoins message.
80     *
81     * @param stampDispenser the <code>StampDispenser</code> whose state this
82     *     object represents
83     * @param listeners <code>Set</code> containing zero to many (and only)
84     *     <code>StampDispenserListener</code>s
85     */
86     State returnCoins(StampDispenser stampDispenser, Set listeners) {
87
88         StampDispenserEvent event = new StampDispenserEvent(stampDispenser,
89             5, 0);
90         fireCoinsReturned(event, listeners);
91         return Has0State.getState();
92     }
93 }

 1 package com.artima.examples.stampdispenser.ex3;
 2
 3 import java.util.Set;
 4
 5 /**
 6 * <code>State</code> subclass that represents the <em>Has10</em> stamp
 7 * dispenser state. See the documentation for class <code>State</code> for a
 8 * state transition table that specifies the required behavior of instances
 9 * of this class.
10 *
11 * @author Bill Venners
12 */
13 class Has10State extends State {
14
15     /**
16     * The single instance of <code>Has10State</code>
17     */
18     private static Has10State singleton = new Has10State();
19
20     /**
21     * Constructs a <code>Has10State</code> instance. This constructor is
22     * private to enable this class to restrict the number of instances of
23     * <code>Has10State</code> to one. I.e., <code>Has10State</code> is a
24     * singleton.
25     */
26     private Has10State() {
27     }
28
29     /**
30     * Factory method that returns the single instance of
31     * <code>Has10State</code>.
32     */
33     static State getState() {
34         return singleton;
35     }
36
37     /**
38     * Performs actions appropriate for the state represented by the class of
39     * this object for the add5 message. Returns a reference to the state
40     * object that represents the next state to transition to as a result of
41     * the arrival of this add5 message.
42     *
43     * @param stampDispenser the <code>StampDispenser</code> whose state this
44     *     object represents
45     * @param listeners <code>Set</code> containing zero to many (and only)
46     *     <code>StampDispenserListener</code>s
47     */
48     State add5(StampDispenser stampDispenser, Set listeners) {
49
50         StampDispenserEvent event = new StampDispenserEvent(stampDispenser, 0, 15);
51         fireCoinAccepted(event, listeners);
52         return Has15State.getState();
53     }
54
55     /**
56     * Performs actions appropriate for the state represented by the class of
57     * this object for the add10 message. Returns a reference to the state
58     * object that represents the next state to transition to as a result of
59     * the arrival of this add10 message.
60     *
61     * @param stampDispenser the <code>StampDispenser</code> whose state this
62     *     object represents
63     * @param listeners <code>Set</code> containing zero to many (and only)
64     *     <code>StampDispenserListener</code>s
65     */
66     State add10(StampDispenser stampDispenser, Set listeners) {
67
68         StampDispenserEvent event = new StampDispenserEvent(stampDispenser,
69             0, 0);
70         fireStampDispensed(event, listeners);
71         return Has0State.getState();
72     }
73
74     /**
75     * Performs actions appropriate for the state represented by the class of
76     * this object for the returnCoins message. Returns a reference to the
77     * state object that represents the next state to transition to as a
78     * result of the arrival of this returnCoins message.
79     *
80     * @param stampDispenser the <code>StampDispenser</code> whose state this
81     *     object represents
82     * @param listeners <code>Set</code> containing zero to many (and only)
83     *     <code>StampDispenserListener</code>s
84     */
85     State returnCoins(StampDispenser stampDispenser, Set listeners) {
86
87         StampDispenserEvent event = new StampDispenserEvent(stampDispenser,
88             10, 0);
89         fireCoinsReturned(event, listeners);
90         return Has0State.getState();
91     }
92 }

 1 package com.artima.examples.stampdispenser.ex3;
 2
 3 import java.util.Set;
 4
 5 /**
 6 * <code>State</code> subclass that represents the <em>Has15</em> stamp
 7 * dispenser state. See the documentation for class <code>State</code> for a
 8 * state transition table that specifies the required behavior of instances
 9 * of this class.
10 *
11 * @author Bill Venners
12 */
13 class Has15State extends State {
14
15     /**
16     * The single instance of <code>Has15State</code>
17     */
18     private static Has15State singleton = new Has15State();
19
20     /**
21     * Constructs a <code>Has15State</code> instance. This constructor is
22     * private to enable this class to restrict the number of instances of
23     * <code>Has15State</code> to one. I.e., <code>Has15State</code> is a
24     * singleton.
25     */
26     private Has15State() {
27     }
28
29     /**
30     * Factory method that returns the single instance of
31     * <code>Has15State</code>.
32     */
33     static State getState() {
34         return singleton;
35     }
36
37     /**
38     * Performs actions appropriate for the state represented by the class of
39     * this object for the add5 message. Returns a reference to the state
40     * object that represents the next state to transition to as a result of
41     * the arrival of this add5 message.
42     *
43     * @param stampDispenser the <code>StampDispenser</code> whose state this
44     *     object represents
45     * @param listeners <code>Set</code> containing zero to many (and only)
46     *     <code>StampDispenserListener</code>s
47     */
48     State add5(StampDispenser stampDispenser, Set listeners) {
49
50         StampDispenserEvent event = new StampDispenserEvent(stampDispenser,
51             0, 0);
52         fireStampDispensed(event, listeners);
53         return Has0State.getState();
54     }
55
56     /**
57     * Performs actions appropriate for the state represented by the class of
58     * this object for the add10 message. Returns a reference to the state
59     * object that represents the next state to transition to as a result of
60     * the arrival of this add10 message.
61     *
62     * @param stampDispenser the <code>StampDispenser</code> whose state this
63     *     object represents
64     * @param listeners <code>Set</code> containing zero to many (and only)
65     *     <code>StampDispenserListener</code>s
66     */
67     State add10(StampDispenser stampDispenser, Set listeners) {
68
69         StampDispenserEvent event = new StampDispenserEvent(stampDispenser,
70             5, 0);
71         fireStampDispensed(event, listeners);
72         return Has0State.getState();
73     }
74
75     /**
76     * Performs actions appropriate for the state represented by the class of
77     * this object for the returnCoins message. Returns a reference to the
78     * state object that represents the next state to transition to as a
79     * result of the arrival of this returnCoins message.
80     *
81     * @param stampDispenser the <code>StampDispenser</code> whose state this
82     *     object represents
83     * @param listeners <code>Set</code> containing zero to many (and only)
84     *     <code>StampDispenserListener</code>s
85     */
86     State returnCoins(StampDispenser stampDispenser, Set listeners) {
87
88         StampDispenserEvent event = new StampDispenserEvent(stampDispenser,
89             15, 0);
90         fireCoinsReturned(event, listeners);
91         return Has0State.getState();
92     }
93 }

Why Use the State Pattern?


Discussion


Stateless Objects

 1 package com.artima.examples.stampdispenser.ex3;
 2
 3 import java.util.Set;
 4
 5 /**
 6 * <code>State</code> subclass that represents the <em>Has0</em> stamp
 7 * dispenser state. See the documentation for class <code>State</code> for a
 8 * state transition table that specifies the required behavior of instances
 9 * of this class.
10 *
11 * @author Bill Venners
12 */
13 class Has0State extends State {
14
15     /**
16     * The single instance of <code>Has0State</code>
17     */
18     private static Has0State singleton = new Has0State();
19
20     /**
21     * Constructs a <code>Has0State</code> instance. This constructor is
22     * private to enable this class to restrict the number of instances of
23     * <code>Has0State</code> to one. I.e., <code>Has0State</code> is a
24     * singleton.
25     */
26     private Has0State() {
27     }
28
29     /**
30     * Factory method that returns the single instance of
31     * <code>Has0State</code>.
32     */
33     static State getState() {
34         return singleton;
35     }
36
37     /**
38     * Performs actions appropriate for the state represented by the class of
39     * this object for the add5 message. Returns a reference to the state
40     * object that represents the next state to transition to as a result of
41     * the arrival of this add5 message.
42     *
43     * @param stampDispenser the <code>StampDispenser</code> whose state this
44     *     object represents
45     * @param listeners <code>Set</code> containing zero to many (and only)
46     *     <code>StampDispenserListener</code>s
47     */
48     State add5(StampDispenser stampDispenser, Set listeners) {
49
50         StampDispenserEvent event = new StampDispenserEvent(stampDispenser,
51             0, 5);
52         fireCoinAccepted(event, listeners);
53         return Has5State.getState();
54     }
55
56     /**
57     * Performs actions appropriate for the state represented by the class of
58     * this object for the add10 message. Returns a reference to the state
59     * object that represents the next state to transition to as a result of
60     * the arrival of this add10 message.
61     *
62     * @param stampDispenser the <code>StampDispenser</code> whose state this
63     *     object represents
64     * @param listeners <code>Set</code> containing zero to many (and only)
65     *     <code>StampDispenserListener</code>s
66     */
67     State add10(StampDispenser stampDispenser, Set listeners) {
68
69         StampDispenserEvent event = new StampDispenserEvent(stampDispenser,
70             0, 10);
71         fireCoinAccepted(event, listeners);
72         return Has10State.getState();
73     }
74
75     /**
76     * Performs actions appropriate for the state represented by the class of
77     * this object for the returnCoins message. Returns a reference to the
78     * state object that represents the next state to transition to as a
79     * result of the arrival of this returnCoins message.
80     *
81     * @param stampDispenser the <code>StampDispenser</code> whose state this
82     *     object represents
83     * @param listeners <code>Set</code> containing zero to many (and only)
84     *     <code>StampDispenserListener</code>s
85     */
86     State returnCoins(StampDispenser stampDispenser, Set listeners) {
87         return this;
88     }
89 }

Discussion


The Immutable


Immutable ComplexNumber Object

  1 package com.artima.examples.complexnum.ex1;
  2
  3 /**
  4 * Represents a complex number whose real and imaginary components are
  5 * <CODE>double</CODE>s.
  6 *
  7 * @author Bill Venners
  8 */
  9 public class ComplexNumber {
 10
 11     private double real;
 12     private double imaginary;
 13
 14     /**
 15     * Construct a new <code>ComplexNumber</code> with the passed real and
 16     * imaginary components.
 17     *
 18     * @param real the real component of this <code>ComplexNumber</code>
 19     * @param imaginary the imaginary component of this
 20     *     <code>ComplexNumber</code>
 21     */
 22     public ComplexNumber(double real, double imaginary) {
 23
 24         this.real = real;
 25         this.imaginary = imaginary;
 26     }
 27
 28     /**
 29     * Returns the real component of this <code>ComplexNumber</code>.
 30     *
 31     * @return the real component of this <code>ComplexNumber</code>
 32     */
 33     public double getReal() {
 34         return real;
 35     }
 36
 37     /**
 38     * Returns the imaginary component of this <code>ComplexNumber</code>.
 39     *
 40     * @return the imaginary component of this <code>ComplexNumber</code>
 41     */
 42     public double getImaginary() {
 43         return real;
 44     }
 45
 46     /**
 47     * Returns the conjugate of this complex number. The conjugate of complex
 48     * number a + <em>i</em>b is a - <em>i</em>b.
 49     *
 50     * @return the conjugate of this <code>ComplexNumber</code>
 51     */
 52     public ComplexNumber getConjugate() {
 53
 54         return new ComplexNumber(real, -imaginary);
 55     }
 56
 57     /**
 58     * Compares the passed <CODE>ComplexNumber</CODE> to this
 59     * <code>ComplexNumber</code> for equality. Two
 60     * <code>ComplexNumber</code> objects are semantically equal if their
 61     * real components are equal and their imaginary components are equal.
 62     *
 63     * @param An object to compare to this <code>ComplexNumber</code>
 64     * @return <code>true</code> if this <code>ComplexNumber</code> is
 65     *     semantically equal to the passed <code>ComplexNumber</code>
 66     */
 67     public boolean equals(Object o) {
 68
 69         if ((o == null) || (getClass() != o.getClass())) {
 70             return false;
 71         }
 72
 73         // This can't fail because the class of o is exactly ComplexNumber,
 74         // as was proven when (getClass() != o.getClass()) returned false
 75         ComplexNumber cn = (ComplexNumber) o;
 76
 77         // Because this class extends Object, don't call super.equals()
 78
 79         // To be semantically equal, the complex numbers must have equal
 80         // real and imaginary components
 81         if ((real != cn.real) || (imaginary != cn.imaginary)) {
 82             return false;
 83         }
 84
 85         return true;
 86     }
 87
 88     /**
 89     * Computes the hash code for this <code>ComplexNumber</code>.
 90     *
 91     * @return a hashcode value for this <code>ComplexNumber</code>
 92     */
 93     public int hashcode() {
 94
 95         // Got this idea from Double's hashcode method. 
 96         long re = Double.doubleToLongBits(real);
 97         long im = Double.doubleToLongBits(imaginary);
 98
 99         int rex = (int) (re ^ (re >>> 32));
100         int imx = (int) (im ^ (im >>> 32));
101
102         return rex ^ imx;
103     }
104
105     /**
106     * Adds the passed <code>ComplexNumber</code> to this one. The sum of two
107     * complex numbers a + <em>i</em>b and c + <em>i</em>d is (a + c) +
108     * <em>i</em>(c + d).
109     *
110     * @param addend the <code>ComplexNumber</code> to add to this one
111     * @returns the sum of this and the passed <code>ComplexNumber</code>
112     */
113     public ComplexNumber add(ComplexNumber addend) {
114
115         return new ComplexNumber(real + addend.real,
116             imaginary + addend.imaginary);
117     }
118
119     /**
120     * Subtracts the passed <code>ComplexNumber</code> from this one. The
121     * difference between two complex numbers, a + <em>i</em>b - c +
122     * <em>i</em>d, is (a - c) + <em>i</em>(b - d).
123     *
124     * @param subtrahend the <code>ComplexNumber</code> to subtract from this
125     *     <code>ComplexNumber</code>
126     * @return the difference of this and the passed
127     *     <code>ComplexNumber</code>s
128     */
129     public ComplexNumber subtract(ComplexNumber subtrahend) {
130
131         return new ComplexNumber(real - subtrahend.real,
132             imaginary - subtrahend.imaginary);
133     }
134
135     /**
136     * Multiplies this <code>ComplexNumber</code> (the multiplicand) with the
137     * passed <code>ComplexNumber</code> (multiplier). The product of two
138     * complex numbers a + <em>i</em>b and c + <em>i</em>d is (ac - bd) +
139     * <em>i</em>(bc + ad).
140     *
141     * @param multiplier the <code>ComplexNumber</code> with which to
142     *     multiply this <code>ComplexNumber</code>
143     * @return the product of this and the passed
144     *     <code>ComplexNumber</code>
145     */
146     public ComplexNumber multiply(ComplexNumber multiplier) {
147
148         double prodReal = (real * multiplier.real)
149             - (imaginary * multiplier.imaginary);
150         double prodImaginary = (imaginary * multiplier.real)
151             + (real * multiplier.imaginary);
152
153         return new ComplexNumber(prodReal, prodImaginary);
154     }
155
156     /**
157     * Divides this <code>ComplexNumber</code> (the numerator) by the passed
158     * <code>ComplexNumber</code> (denominator). The quotient of two complex
159     * numbers a + <em>i</em>b / c + <em>i</em>d is ((ac + bd) +
160     * <em>i</em>(bc - ad)) / (c*c + d*d).
161     *
162     * @param denominator the <code>ComplexNumber</code> by which to divide
163     *     this <code>ComplexNumber</code>
164     * @return the quotient of this divided by the passed
165     *     <code>ComplexNumber</code>s
166     */
167     public ComplexNumber divide(ComplexNumber denominator) {
168
169         // Perform division by first multiplying both the numerator and
170         // denominator by the conjugate of the denominator.
171         ComplexNumber conj = denominator.getConjugate();
172         ComplexNumber tempNumerator = multiply(conj);
173         ComplexNumber tempDenominator = denominator.multiply(conj);
174
175         // By multiplying the denominator by its conjugate, its imaginary
176         // component drops to zero. Can then just divide the real and
177         // imaginary components of the numerator by the denominator's real
178         // value.
179         return new ComplexNumber(tempNumerator.real / tempDenominator.real,
180             tempNumerator.imaginary / tempDenominator.real);
181     }
182 }

When to Use Immutables


Discussion


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