Sponsored Link •
|
Advertisement
|
Guideline 2 encourages you to think of objects of bundles of services, not bundles of data. This guideline irreverently suggests that sometimes its OK to design objects that are bundles of data.
On occasion, you will see objects that are nothing more than containers for data. I like to call such objects Messengers. A messenger allows you to package and send bundles of data. Often the data is passed to the messenger's constructor, and the messenger is sent along its way. Recipients of the messenger access the data via get methods. Messengers are usually short-lived objects. Once a recipient retrieves the information contained in a messenger, it usually kills the messenger (even if the news is good).
In general, you should move code to data as described in Guideline 1. Most of your object designs should be service-oriented objects, as described in Guideline 2. But on occasion, you may find yourself with some data that you don't know what to do with. Sometimes you know some information, but you don't know what behavior that information implies. You can't move the code to the data, because even though you have the data, you don't know what the code should do. In such cases, you can encapsulate the data inside a messenger object, and send the messenger to some recipient that does know the behavior implied by the data. The recipient extracts the data from the messenger and takes appropriate action.
One common example of messengers is exceptions. As mentioned in Guideline ?, exception objects are usually composed of little more a small amount of data (if any), which is passed to the constructor, and some access methods to let catch clauses get at that data. The primary information in an exception is carried in the name of the exception itself.
1 package com.artima.examples.account.ex1; 2 3 /** 4 * Exception thrown by <code>Account</code>s to indicate that 5 * a requested withdrawal has failed because of insufficient funds. 6 */ 7 public class InsufficientFundsException 8 extends Exception { 9 10 /** 11 * Minimum additional balance required for the requested 12 * withdrawal to succeed. 13 */ 14 private long shortfall; 15 16 /** 17 * Constructs an <code>InsufficientFundsException</code> with 18 * the passed shortfall and no specified detail message. 19 * 20 * @param shortfall the amount in excess of available funds 21 * that caused a withdrawal request to fail. 22 */ 23 public InsufficientFundsException(long shortfall) { 24 this.shortfall = shortfall; 25 } 26 27 /** 28 * Constructs an <code>InsufficientFundsException</code> with 29 * the passed detail message and shortfall. 30 * 31 * @param message the detail message 32 * @param shortfall the amount in excess of available funds 33 * that caused a withdrawal request to fail. 34 */ 35 public InsufficientFundsException(String message, long shortfall) { 36 super(message); 37 this.shortfall = shortfall; 38 } 39 40 /** 41 * Returns the shortfall that caused a withrawal request 42 * to fail. The shortfall is the minimum additional balance 43 * required for the requested withdrawal to succeed. 44 * 45 * @returns shortfall the amount in excess of available funds 46 * that caused a withdrawal request to fail. 47 */ 48 public long getShortfall() { 49 return shortfall; 50 } 51 } 52 53
Another example of the Messenger are events. Like exceptions, event objects often just encapsulate some data, which is passed to the constructor, and offers access methods to let listeners get at the data. For examples of this see the Event Generator idiom.
Like exceptions and events, multivalued return objects are Messengers. It has some data, which must be passed to the constructor, and offers access methods so the calling method can get at the returned data.
Messengers are usually immutable (but not always: AWT event can be consumed)
In parting, I want to warn you to be suspicious of messengers when they appear in your designs. Challenge
their existence. A messenger makes sense when you don't know the behavior that should result
accompany some data. If you do know the behavior, then you know the code that should use that
data. In this case, you should refactor. You should move the code to the data, which will
transform the messenger into a service-oriented object. This is, in fact, the very process demonstrated
in Guideline 2: The Matrix
of Listing 2-1 was
a messenger, which was transformed into a service-oriented Matrix
in Listing 2-3.
To get from Listing 2-1 to Listing 2-3, I moved the code for matrix addition, subtraction,
multiplication, conversion to string, etc., to the
Matrix
class that contained the matrix data.
Probably mention the attribute classes in the Service UI API, and talk about EJB Blueprints "value" objects. Or perhaps value objects belong in Immutable.
Sponsored Links
|