value added

Post on 17-Jul-2015

190 Views

Category:

Software

1 Downloads

Preview:

Click to see full reader

TRANSCRIPT

7 4 Java™Report | A P R I L 2 0 0 0 ht t p : / / w w w. j ava re po rt . co m

THE TERM VALUE can refer to the desirabil-ity of a thing or to an amount. While I’m nots u re that Java’s type system is up to model-ing desirability, we can readily see that

calculating and storing amounts is a typical re q u i re-ment of software .

Value objects1 re p resent the simple, granular piecesof information you find in a system: strings, dates,m o n e y, dimensions, colors, etc. This is in contrast toentity objects—which capture the significant, persis-tent pieces of information real estate such as orders andcustomers—and to process and service objects such as

C o m m a nd o b j e c t s ,2 s e rv l e t s ,and layout managers.

Pat te rn sA pattern language re l a t e sa set of practices. Pattern sare arranged so they flowfrom one to another, withd e c i s i o n s t a k e n t o go fro mo n e t o t h e next, forming adesign around a part i c u l a rm o d e l — i n t h i s case, value-based pro g r a m m i n gin Java. Figure 1 shows the

p a t t e rns and their connections in the language.Although the flow in the language is not linear, this

column is. The following sections list the patterns ina kind of best-fit, partial ordering. Each pattern is pre-sented in a brief problem/solution form, preceded bya sentence summarizing the intent. A simple date re p-resentation example runs throughout, but you can fin dthese patterns applied in many Java systems, includ-ing the JDK.

Common Fo rces and Co n s e q u e n ce sCommon constraining forces and common re s u l t i n gconsequences are a feature of many of the pattern s ;other patterns work to offset these.

Values tend to make up a lot of the re p re s e n t a-

tion of and method traffic between other objects. Asobjects, this may lead to an increase in the numberof classes at development time and the creation ofm o re small objects at runtime. However, these con-sequences and their relative impact depend on thesystem being built. They must be weighed againstan overall reduction in duplicated logic—and there-f o re system size and effect of change—and an in-c rease in code clarity.

As part of other objects, values often re q u i re per-sistence; as part of remote method communication,they should be copied rather than stand as targets forindependent communication. Implementing S e r i a l i z-a b le is a solution to both of these issues.

T h reading is another common issue confro n t i n gthe developer: To satisfy the principle of least aston-ishment, the class provider should ensure either thatthe effects of any modifier methods on classes aret h read safe, or that class users are made aware of thelack of thread safety.

Whole Va l u eP rovide distinct classes to express distinct domains p e c i fic value quantities clearly and safely.

Pro b l e m . How can you re p resent a primitive domainquantity in your system without loss of meaning? It istempting, and indeed common, to re p resent valuequantities in a program in the most fundamental unitspossible, e.g., integers and floating point numbers.H o w e v e r, this fails to communicate the understand-ing of the problem and its quantities into the solution,

Kevlin He n n ey is an indepe n d e nt co n s u l t a nt and trainer basedin the UK.He can be co nt a cted at kev l i n @ a c m . o rg.

Kevlin He n n ey / kevlin@acm.orgPatterns in Java

Value added

Figure 1. Pa t te rns and their successors for suppo rting va l u e -

based prog ramming in Java .

WHOLEVALUE

IMMUTABLEVALUE

MUTABLECOMPANION

VALUECLASS

CLASS FACTORYMETHOD

COPIEDVALUE

ENUMARATIONVALUES

7 6 Java™Report | A P R I L 2 0 0 0 ht t p : / / w w w. j ava re po rt . co m

Patterns in Java / Kevlin He n n ey

and shows poor use of the checked type system.Consider initializing a date object from the year, month,

and day. The following code fragment arbitrates a more in-t e rnational (and logical) solution to the DD/MM/YYYY ver-sus MM/DD/YYYY convention:

public final class Date ...{

public Date(int ye a r, int mo nth, int day) .... . .

}

H o w e v e r, an i nt is a pretty meaningless unit of currency: Itdoes not define its units and it cannot be checked for sen-sible use. Compile-time errors should always be pre f e rre dto runtime errors—it can mean the diff e rence between de-bugging before a release or after it:

Date right = new Date(ye a r, mo nth, day);Date wro ng = new Date(day, mo nth, ye a r ) ;Date als o Wro ng = new Date(mo nth, day, ye a r ) ;

Solution. Express the type of the quantity as a class.A Whole Value3 recovers the loss of meaning and check-ing. A Whole Value class can range from a simple wrap-per class—making the intent of the code clearer toboth the human reader and the compiler—to one withm o re complete behavior. A W ho le Va l u e makes amethod interface clearer and safer; whether it is usedin the encapsulated implementation—as opposed tore v e rting to raw values—depends on other designchoices and preferences.

Listing 1 demonstrates how a simple Ye a r class elevatesyears to a checkable and named quantity.

Ye a r could be made a little more sophisticated, acquiringthe fuller trappings of a Value Class and some range check-ing. To make D a t e initialization safe, either one or both ofmonths and days should also be re p resented by a W ho le Va l u e. Now, only the correct combination will compile:

new Date(Ye a r. va l u e O f ( year), Mo nt h . va l u e O f ( mo nth), day)

W ho le Va l u e allows us to enforce rules of combination thata re not possible with simpler types such as i nt, do u b le, orS t r i ng. This can be likened to dimensional analysis in thephysical sciences: It is plain nonsense to divide a mea-s u rement of distance by a measurement of temperature andassign the result as a measure of acceleration. A W ho le

Va l u e can wrap up a more primitive type, e.g., P ho ne N u m b e rwrapping S t r i ng; or bundle together simple attributes, e.g.,D i me ns i o n wrapping i nts for width and height.

Value Cl a s sD e fine features for a class that re p resents a value type sothat it conforms to expectations.

Pro b l e m . Having established that a value type needs implementing as a class, often because it is a W ho le Va l u e re p resenting a domain specific quantity, what stepsmust be taken to define the class so that it meets user e x p e c t a t i o n s ?

The use of value objects revolves around their re p re-sented content rather than their state: Comparisons and o rdering between value objects should depend on their content and not on their re f e rence identity. This applies totheir use as keys in associative collections as well as them o re obvious comparisons for equality.

So l u t i o n . O v e rride the methods in O b je c t whose actionshould be related to content and not identity (e.g., e q u a ls)and, if appropriate, implement S e r i a l i z a b le.

Field by field comparison is more appropriate for values than the default, identity-based O b je c t . e q u a ls. Listing2 shows e q u a ls d e fined with minimum casting and clutter.

If you override e q u a ls you should also override h a s h C o de,ensuring that if two objects compare equal so do their hash values. Related to equality comparison is general relational comparison. Not all values can be meaningful-ly compared, e.g. C o lo r, but where it makes sense, e.g., D a t e,provide a compareTo method. If you are using JDK 1.2 youcan publicize this feature more explicitly by implement-ing the Comparable interface.

Value objects are normally the kind of object that shouldp rovide a printed form. Overriding O b je c t . to S t r i ng a l l o w sclass users to take advantage of simple string concatenations y n t a x :

S ys t e m . o u t . p r i nt l n ( “ T he date today is “ + Date. to d a y ( ) ) ;

L I S T I N G 2 .

The implementation of e q u a ls for the Date Va l u e c l a s s.public final class Date ...{

. . .public boolean equals ( O b ject rhs ){

return rhs ins t a nc e of Date && equals((Date) rhs ) ;}public boolean equals(Date rhs ){

return rhs != null && rhs. year == year &&r hs. mo nt h . e q u a ls ( mo nth) && rhs.day == day;

}p r i vate int ye a r, day;p r i vate Mo nth mo nt h ;

}

L I S T I N G 1.

Ye a r re p resented as a simple W ho le Va l u e.public final class Ye a r{

public static Year va l u e O f ( i nt ye a r ){ return new Ye a r ( year); }

public Ye a r ( i nt year) { value = year; }public int ge t Value() { return value; }p r i vate final int va l u e ;

}

7 8 Java™Report | A P R I L 2 0 0 0 ht t p : / / w w w. j ava re po rt . co m

C reating instances of a Value Class can be simplified by p roviding Class Fac to r y Me t ho d s as well as, or instead of, pub-lic constru c t o r s .

En u m e ration Va l u e sR e p resent a distinct and bounded set of values as objects.

Pro b l e m . How can you re p resent a fixed set of constant val-ues and pre s e rve type safety? It is commonly the case thata fixed set of options or indicators need re p re s e n t a t i o n .Even if they have no specific values they must still be dis-tinct from one another.

For example, assuming you count months from January,do you number from 0 or from 1? A good case can be madefor either one, which is exactly the problem with raw,meaningless i nts. That said, there is a question of consis-tency over any API that thinks it is reasonable to startmonths from 0 and days from 1. Given that Java defaults i ntfields to 0—potentially hiding initialization pro b l e m s — i tseems best to opt for the 1 convention.

Now that the decision has been made, how can it bee n f o rced? The simplest way to communicate this intentwith the user is to use named i nt constants. Although thisis preferable to inviting users to plug in magic numbers,it is still open to intentional or accidental abuse: i nt i schecked only as an i nt and not as a month. Modelingmonth as a W ho le Va l u e o ffers a more expressive appro a c h ,but does not necessarily constrain the set of values to thec o rrect ones.

So l u t i o n . Treat each constant as a W ho le Va l u e i n s t a n c e ,declaring it as public static fin a l in the W ho le Va l u e class. Toe sure that the set of constant values remains fixed, p revent public construction by defining the constructor asprivate. Implementing the class as an I m mu t a b le Va l u ep revents the possibility of the constant values being unexpectedly nonconstant.

Users of the Mo nt h class can now both name themonth of their choice and receive the full support of thec o m p i l e r’s type checking. Also, because the type implicitly restricts the E nu me ration Va l u e s, range check-ing is fre e .

W h e re the primitive value associated with each of the

E nu me ration Va l u e s has special significance—which is thecase for months—the initialization of each constantshould be explicit and fully under the control of theclass developer (see Listing 3). Otherwise a no-arg c o n s t ructor and a static counter can be used to assigneach constant value a distinct number based on order of initialization.

Class Fa cto ry Me t h odP rovide a convenient way to create an object that encap-sulates the details of cre a t i o n .

Pro b l e m . How can you simplify, and potentially optimize,c o n s t ruction of Value Class objects in expressions withoutre s o rting to intrusive new expressions? How do you pro-vide for the opportunity to reuse existing objects that havethe same value, i.e., F l y we i g ht o b j e c t s ?2

Assuming that year and month are re p resented by W ho leVa l u e objects, the following code to initialize a D a t e l o o k ssyntactically clumsy and does not support the use of E nu-me ration Va l u e s for months:

Date clumsy = new Date(new Ye a r ( year), new Mo nt h ( mo nth), day);

So l u t i o n . P rovide s t a t i c methods to be used instead of (oras well as) ord i n a ry constructors. The methods re t u rn ei-ther newly created Value Class objects or existing objects fro ma lookup table (see Listing 4).

For value types with a clear and commonly used stringre p resentation, it is common to provide a Class Fac to r yMe t ho d that takes a S t r i ng and re t u rns a value object basedon the parsing of that string. For example, Mo nt h may pro-vide a conversion from the month name to the relevant E nu-me ration Va l u e.

Even when it does not offer a lookup optimization, aClass Fac tory Me t ho d has benefits in promoting uniform us-age and syntactic simplification. Class Fac tory Me t ho d is acommon variation of the vanilla Fac tory Me t ho d p a t t e rn .2

Copied Va l u e*

Describe how modifiable value objects should be passeda round so as to avoid aliasing pro b l e m s .

Pro b l e m . How can you pass a modifiable Value Class

Patterns in Java / Kevlin He n n ey

L I S T I N G 3 .

Mo nt hs ex p ressed as E nu me ra t ion Va l u e s.public final class Mo nth ...{

public static final Mo nth january = new Mo nt h ( 1 ) ;. . .public static final Mo nth december = new Mo nt h ( 1 2 ) ;public int ge t Value() { return value; }. . .p r i vate Mo nt h ( i nt mo nth) { value = mo nth; }p r i vate final int va l u e ;

}

L I S T I N G 4 .

A Class Factory Me t ho d that converts from i nt to Mo nt h.public final class Mo nth ...{

. . .public static Mo nth va l u e O f ( i nt mo nt h )

{ return mo nt hs [ mo nth - 1]; }p r i vate static final Mo nth[] mo nt hs =

{ janu a r y, ..., december };. . .

}

*The previous art i c l e3 re f e rred to this as C lo ne a b le Va l u e, but the nameCopied Va l u e better captures the general intent of the pattern .

8 0 Java™Report | A P R I L 2 0 0 0 ht t p : / / w w w. j ava re po rt . co m

Patterns in Java / Kevlin He n n ey

object into and out of methods without permitting callersor called methods to affect the original object? Value objectsa re often used as attributes inside other objects. Subtle alias-ing problems can arise if the attribute is assigned dire c t l yf rom a method argument when it is set or if it is re t u rn e dd i rectly when queried:

O rder firs t O rde r, second O rde r ;. . .Date date = new Date(ye a r, mo nth, day);firs t O rde r. s e t D e l i ve r y D a t e ( d a t e ) ;d a t e. nex t D a y ( ) ;s e c o nd O rde r. s e t D e l i ve r y D a t e ( d a t e ) ;

In essence, this is a violation of encapsulation: Althoughd e c l a red private, users can still interf e re with the object’sre p re s e n t a t i o n .

So l u t i o n . Implement the C lo ne a b le i n t e rface or provide acopy constructor for the Value Class, and use a copy of theoriginal whenever it needs to be passed (see Listing 5). Thisp revents sharing and pre s e rves encapsulation, while stillallowing value objects to be modifie d .

For values that are queried far more often than they arem o d i fied, an I m mu t a b le Va l u e with a Mu t a b le Companiono ffers an alternative leading to less object cre a t i o n .

Immutable Va l u eD e fine a class for which instances have fixed state to avoidaliasing pro b l e m s .

Pro b l e m . How can you share Value Class objects and guar-antee no side effect problems? Copied Va l u e describes the conventions for using a modifiable value object tominimize aliasing issues. However, this can be erro rp rone and may lead to excessive creation of small objects, especially where values are frequently queriedor passed aro u n d .

If objects are shared between threads they must bet h read safe. Synchronization of state change incurs an over-head, but is essential in guarding against race conditions.Even where copying or synchronization are carefully attended to, there are still opportunities for undefined behavior: The integrity of an associative collection may bec o m p romised if the state of an object used as a key is

m o d i fied via an aliasing re f e re n c e .

So l u t i o n . Set the internal state of the Value Class object atc o n s t ruction, and allow no subsequent modifications; i.e.,p rovide only query methods and constructors, as shown inListing 6. Declaring the fields fin a l e n s u res this no-changep romise is honored. This guarantee implies also that eitherthe class itself must be fin a l or its subclasses must also beI m mu t a b le Va l u e s.

The absence of any possible state changes means thereis no reason to synchronize. Not only does this make I m-mu t a b le Va l u e objects implicitly thread safe, the absence oflocking means that their use in a threaded environment isalso effic i e n t .

Sharing of I m mu t a b le Va l u e objects is also safe and trans-p a rent in other circumstances; so, there is no need to copyan Immutable Value, and therefore no reason to implementCloneable. Attributes are changed by replacing their refer-enced I m mu t a b le Va l u e object with another holding a d i ff e rent value, rather than modifying the object that is already there.

T h e re are complementary techniques for creating I m-mu t a b le Va l u e objects: Provide a complete and intuitive setof constructors; provide a number of Class Fac tory Me t ho d s;or provide a Mu t a b le Companion.

Mutable Co m p a n i o nD e fine a companion class to simplify complex manipula-tion of I m mu t a b le Va l u e o b j e c t s .

Pro b l e m . How can you simplify complex construction ofan I m mu t a b le Va l u e? Constructors provide a way of cre a t-ing objects from a fixed set of arguments, but they cannotaccumulate changes or handle complex expressions with-out becoming too complex, e.g., calculate the date 15working days from today. Such re q u i rements typicallylead to expressions that create many temporary objects.

L I S T I N G 5 .

Passing a C o p ied Va l u e in and out of methods.class Orde r{

. . .public void setDeliveryDate(Date new D e l i ve r y D a t e )

{ de l i veryDate = (Date) new D e l i ve r y D a t e. c lo ne(); }public Date ge t D e l i ve r y D a t e ( )

{ return (Date) de l i ve r y D a t e. c lo ne(); }p r i vate Date de l i ve r y D a t e ;

}

L I S T I N G 6 .

D a t e ex p ressed as an I m mutable Va l u e.

public final class Date ...{

public Date(Year ye a r, Mo nth mo nth, int day){

t h i s. year = ye a r. ge t Va l u e ( ) ;t h i s. mo nth = mo nt h ;t h i s.day = day;

}public int ge t Year() { return year; }public Mo nth ge t Mo nth() { return mo nth; }public int ge t D a y I n Mo nth() { return day; }. . .p r i vate final int ye a r, day;p r i vate final Mo nth mo nt h ;

}

8 2 Java™Report | A P R I L 2 0 0 0 h t t p : / / w w w. j ava re po r t . co m

Class Fac tory Me t hods may be able to optimize some of thec reation and expressiveness issues, but they are still oneoff creation requests.

So l u t i o n . I m p l e m e n t a c o m p a n i o n c l a s s t h a t s u p p o rts m o d i fier methods and acts as a factory for I m mu t a b le Va l u e

objects. For convenience, the factory can stand not only asa separate class, but can also take on some of the roles andcapabilities of the I m mu t a b le Va l u e, e.g., S t r i ng B u f f e r a n dS t r i ng. The modifiers, which should be synchronized, allowfor cumulative or complex state changes, and a querymethod allows users to get access to the resulting I m mu t a b leVa l u e (see Listing 7).

Although it may support many of the same methods asits associated I m mu t a b le Va l u e, it is not related to it by in-heritance: It cannot fulfill the same guarantees on im-mutability and there f o re cannot be considered a subtype,hence its nonfamilial status as a companion. ■

Re fe re n ce s

1 . h t t p : / / c 2 . c o m / c g i / w i k i ? Va l u e O b j e c t.

2 . Gamma, E. et al., Design Patterns: Elements of Reusable Object-Oriented Software, Addison–We s l e y, 1995.

3 .H e n n e y, K., “Patterns of value,” Java Report, Vol. 5, No. 2,Feb. 2000, pp. 64–68.

4 . Cunningham, W., “The CHECKS Pattern Language of Infor-mation Integrity,” P a t t e rn Languages of Program Design, J.Coplien and D. Schmidt, Eds., Addison–We s l e y, 1995.

L I S T I N G 7 .

The D a t e Manipulator Mutable Companio n to D a t e.public class DateManipulator ...{

public sy nc h ronized void set(Year ye a r, Mo nth mo nth, int day)

{t h i s. year = ye a r ;t h i s. mo nth = mo nt h ;t h i s.day = day;

}public sy nc h ronized Date to D a t e ( )

{ return new Date(ye a r, mo nth, day); }public int ge t Year() { return ye a r. ge t Value(); }. . .p r i vate Year ye a r ;p r i vate Mo nth mo nt h ;p r i vate int day;

}

Patterns in Java / Kevlin He n n ey

column I’ll address the first, by showing how to explicitlydisable cloning for classes that have inherited a c lo nemethod with no checked exceptions.

The second trouble spot derives from a defect in the orig-inal design of the c lo ne method: It is not possible to cloneobjects through O b je c t re f e rences because the c lo ne m e t h o dis declared p ro t e c t e d in O b je c t and, furt h e rm o re, is notaccessible through the C lo ne a b le i n t e rface. Java’s designersopenly acknowledge the difficulties this imposes, stating,

“The sad fact of the matter is that C lo ne a b le is broken, andalways has been. The C lo ne a b le i n t e rface should have a pub-lic c lo ne method in it. This is a very annoying problem, andone for which there is no obvious fix . ”5

The inaccessibility of the c lo ne method via O b je c t re f e r-ences has implications for perf o rming deep copying on con-tainer objects. Generic O b je c t-based containers are pre v e n t e df rom cloning their elements because the c lo ne method is in-accessible. In the words of the Java language developersagain, “In fact, it’s nearly impossible to do deep copies inJava, as the C lo ne a b le i n t e rface lacks a public c lo ne o p e r a t i o n . ”4

J a v a ’s cloning mechanism has numerous defects and isunnecessarily awkward and error pro n e .1 Not only that, butcloning has been even more “broken” by the addition ofnew language features such as fin a l members and innerclasses, with apparent disre g a rd for their impact on a facil-ity that already presents a number of pro b l e m s .

Missing features in the language also make cloning morebothersome than it could be: Java does not support c o v a r i -ant re t u rn types, where an overriding method is perm i t t e d

to re t u rn a subclass of the type re t u rned by the method ito v e rrides. This re q u i res callers of the c lo ne method to castthe result to retrieve the type information that was lostwhen the c lo ne method re t u rn e d .

The c lo ne method is not supported for many common class-es for which it would be useful: notably S t r i ng and the wrap-per classes I nt e ge r, F lo a t, etc. Cloning instances of these classest h rough generic O b je c t re f e rences is unnecessarily irre g u l a r.

F o rt u n a t e l y, all these problems may be overcome. P roviding cloning support is an important part of designingclasses of maximum usefulness and re l i a b i l i t y. In a future col-u m n on cloning, I will present a mechanism that allows ob-jects to be cloned via O b je c t re f e rences, one that can be addedto any of the Java API container classes that will cause themto perf o rm a deep copy when cloned. You will then have allthe weaponry you need to beat Java at its own game. ■

Re fe re n ce s

1 . Ball, S., “Effective Cloning,” Java Report, Vol. 5, No. 1, Jan.2000, pp. 60–67.

2 . Ball, S. “Superc h a rged S t r i ngs,” Java Report, Vol. 4, No. 2,Feb. 1999, pp. 62–69.

3 . Gosling, J., B. Joy, and G. Steele, The Java Language Speci-fic a t i o n, Addison–We s l e y, 1996.

4. Sun Microsystems Bug Parade, h t t p : / / d e v e l o p e r. j a v a . s u n . -c o m / d e v e l o p e r / b u g P a r a d e / b u g s / 4 1 0 3 4 7 7 . h t m l.

5. h t t p : / / d e v e l o p e r. j a v a . s u n . c o m / d e v e l o p e r / b u g P a r a d e / -b u g s / 4 2 2 8 6 0 4 . h t m l.

6. Source code for the example classes, as well as the bench-marking application for timing the relative efficiency ofc o n s t ruction vs. cloning, is available at h t t p : / / e ff e c t i v e j a-v a . c o m / c o l u m n / d - c l o n i n g.

Continued from page 72

Effective Java / Steve Ba l l

top related