1 / 5

Value Added

Value Added (Java Report 5(4), April 2000).

Kevlin
Télécharger la présentation

Value Added

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Patterns in Java Kevlin He n n ey/kevlin@ acm.org Value added T calculating and storing amounts is a typical re q u i re- ment of software . Value objects1re p resent the simple, granular pieces of information you find in a system: strings, dates, m o n e y, dimensions, colors, etc. This is in contrast to entity objects—which capture the significant, persis- tent pieces of information real estate such as orders and customers—and to process and service objects such as HE TERM VALUE can refer to the desirabil- ity of a thing or to an amount. While I’m not s u re that Java’s type system is up to model- ing desirability, we can readily see that tion of and method traffic between other objects. As objects, this may lead to an increase in the number of classes at development time and the creation of m o re small objects at runtime. However, these con- sequences and their relative impact depend on the system being built. They must be weighed against an 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 for independent 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 g the developer: To satisfy the principle of least aston- ishment, the class provider should ensure either that the effects of any modifier methods on classes are t h read safe, or that class users are made aware of the lack of thread safety. 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 s A pattern language re l a t e s a set of practices. Pattern s are arranged so they flow from one to another, with d e c i s i o n s t a k e n t o go fro m o n e t o t h e next, forming a design around a part i c u l a r m o d e l — i n t h i s case, value-based pro g r a m m i n g in Java. Figure 1 shows the Whole Va l u e P rovide distinct classes to express distinct domain s p e c i fic value quantities clearly and safely. Pro b l e m . How can you re p resent a primitive domain quantity in your system without loss of meaning? It is tempting, and indeed common, to re p resent value quantities in a program in the most fundamental units possible, 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, 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 in a kind of best-fit, partial ordering. Each pattern is pre- sented in a brief problem/solution form, preceded by a sentence summarizing the intent. A simple date re p- resentation example runs throughout, but you can fin d these patterns applied in many Java systems, includ- ing the JDK. WHOLE VALUE VALUE CLASS Common Fo rces and Co n s e q u e n ce s Common constraining forces and common re s u l t i n g consequences 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- ENUMARATION VALUES IMMUTABLE VALUE CLASS FACTORY METHOD COPIED VALUE MUTABLE COMPANION Kevlin He n n ey is an indepe n d e nt co n s u l t a nt and trainer based in the UK.He can be co nt a cted at kev l i n @ a c m . o rg. Figure 1.Pa t te rns and their successors for suppo rting va l u e - based prog ramming in Java . 7 4 Java ™Report | APRIL 2000 ht t p : / / w w w. j ava re po rt. co m

  2. Patterns in Java/Kevlin He n n ey Va l u e can wrap up a more primitive type, e.g., P ho ne N u m b e r wrapping 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. LISTING 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 ; } Value Cl a s s D e fine features for a class that re p resents a value type so that 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 steps must 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 to their use as keys in associative collections as well as the m o re obvious comparisons for equality. 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) ... . . . } So l u t i o n . O v e rride the methods in O b je c t whose action should 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. Listing 2 shows e q u a ls d e fined with minimum casting and clutter. If you override e q u a lsyou 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 you can publicize this feature more explicitly by implement- ing the Comparable interface. Value objects are normally the kind of object that should p rovide a printed form. Overriding O b je c t . to S t r i ng a l l o w s class users to take advantage of simple string concatenation s y n t a x : H o w e v e r, an i nt is a pretty meaningless unit of currency: It does not define its units and it cannot be checked for sen- sible use. Compile-time errors should always be pre f e rre d to 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 Value3recovers 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 to both the human reader and the compiler—to one with m o re complete behavior. A W ho le Va l u e makes a method interface clearer and safer; whether it is used in the encapsulated implementation—as opposed to re v e rting to raw values—depends on other design choices and preferences. Listing 1 demonstrates how a simple Ye a r class elevates years to a checkable and named quantity. Ye a r could be made a little more sophisticated, acquiring the fuller trappings of a Value Class and some range check- ing. To make D a t e initialization safe, either one or both of months and days should also be re p resented by a W ho le Va l u e. Now, only the correct combination will compile: S ys t e m . o u t . p r i nt l n ( “ T he date today is “ + Date. to d a y ( ) ) ; LISTING 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 ; } 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 that a re not possible with simpler types such as i nt, do u b le, or S t r i ng. This can be likened to dimensional analysis in the physical sciences: It is plain nonsense to divide a mea- s u rement of distance by a measurement of temperature and assign the result as a measure of acceleration. A W ho le 7 6 Java ™Report | APRIL 2000 ht t p : / / w w w. j ava re po rt. co m

  3. Patterns in Java/Kevlin He n n ey C reating instances of a Value Class can be simplified by p roviding Class Fac to r y Me t ho d sas well as, or instead of, pub- lic constru c t o r s . LISTING 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 }; . . . } En u m e ration Va l u e s R 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 that a 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 made for 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 start months from 0 and days from 1. Given that Java defaults i nt fields to 0—potentially hiding initialization pro b l e m s — i t seems best to opt for the 1 convention. Now that the decision has been made, how can it be e n f o rced? The simplest way to communicate this intent with the user is to use named i nt constants. Although this is preferable to inviting users to plug in magic numbers, it is still open to intentional or accidental abuse: i nt i s checked only as an i nt and not as a month. Modeling month 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 the c o rrect ones. E nu me ration Va l u e s has special significance—which is the case for months—the initialization of each constant should be explicit and fully under the control of the class developer (see Listing 3). Otherwise a no-arg c o n s t ructor and a static counter can be used to assign each constant value a distinct number based on order of initialization. Class Fa cto ry Me t h od P 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 without re s o rting to intrusive new expressions? How do you pro- vide for the opportunity to reuse existing objects that have the 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 le Va l u e objects, the following code to initialize a D a t e l o o k s syntactically clumsy and does not support the use of E nu- me ration Va l u e s for months: 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. To e sure that the set of constant values remains fixed, p revent public construction by defining the constructor as private. Implementing the class as an I m mu t a b le Va l u e p revents the possibility of the constant values being unexpectedly nonconstant. Users of the Mo nt h class can now both name the month of their choice and receive the full support of the c 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 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 (or as well as) ord i n a ry constructors. The methods re t u rn ei- ther newly created Value Classobjects or existing objects fro m a lookup table (see Listing 4). For value types with a clear and commonly used string re p resentation, it is common to provide a Class Fac to r y Me t ho d that takes a S t r i ng and re t u rns a value object based on 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, a Class Fac tory Me t ho d has benefits in promoting uniform us- age and syntactic simplification. Class Fac tory Me t ho d is a common variation of the vanilla Fac tory Me t ho d p a t t e rn .2 LISTING 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 ; } Copied Va l u e* Describe how modifiable value objects should be passed a 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 *The previous art i c l e3re f e rred to this as C lo ne a b le Va l u e, but the name Copied Va l u e better captures the general intent of the pattern . 7 8 Java ™Report | APRIL 2000 ht t p : / / w w w. j ava re po rt. co m

  4. Patterns in Java/Kevlin He n n ey m o d i fied via an aliasing re f e re n c e . LISTING 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 ; } So l u t i o n . Set the internal state of the Value Class object at c o n s t ruction, and allow no subsequent modifications; i.e., p rovide only query methods and constructors, as shown in Listing 6. Declaring the fields fin a l e n s u res this no-change p romise is honored. This guarantee implies also that either the class itself must be fin a l or its subclasses must also be I m mu t a b le Va l u e s. The absence of any possible state changes means there is 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 of locking means that their use in a threaded environment is also effic i e n t . Sharing of I m mu t a b le Va l u eobjects is also safe and trans- p a rent in other circumstances; so, there is no need to copy an Immutable Value, and therefore no reason to implement Cloneable. 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 set of constructors; provide a number of Class Fac tory Me t ho d s; or provide a Mu t a b le Companion. object into and out of methods without permitting callers or called methods to affect the original object? Value objects a re often used as attributes inside other objects. Subtle alias- ing problems can arise if the attribute is assigned dire c t l y f rom a method argument when it is set or if it is re t u rn e d d 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 ) ; Mutable Co m p a n i o n D 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 . In essence, this is a violation of encapsulation: Although d e c l a red private, users can still interf e re with the object’s re 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 a copy constructor for the Value Class, and use a copy of the original whenever it needs to be passed (see Listing 5). This p revents sharing and pre s e rves encapsulation, while still allowing value objects to be modifie d . For values that are queried far more often than they are m o d i fied, an I m mu t a b le Va l u e with a Mu t a b le Companion o ffers an alternative leading to less object cre a t i o n . Pro b l e m . How can you simplify complex construction of an 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 cannot accumulate changes or handle complex expressions with- out becoming too complex, e.g., calculate the date 15 working days from today. Such re q u i rements typically lead to expressions that create many temporary objects. LISTING 6. Immutable Va l u e D e fine a class for which instances have fixed state to avoid aliasing pro b l e m s . 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 ; } 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 to minimize aliasing issues. However, this can be erro r p rone and may lead to excessive creation of small objects, especially where values are frequently queried or passed aro u n d . If objects are shared between threads they must be t 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 be c o m p romised if the state of an object used as a key is 8 0 Java ™Report | APRIL 2000 ht t p : / / w w w. j ava re po rt. co m

  5. Patterns in Java/Kevlin He n n ey objects. For convenience, the factory can stand not only as a separate class, but can also take on some of the roles and capabilities 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 d S t r i ng. The modifiers, which should be synchronized, allow for cumulative or complex state changes, and a query method allows users to get access to the resulting I m mu t a b le Va l u e (see Listing 7). Although it may support many of the same methods as its 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. I LISTING 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; } 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. Class Fac tory Me t hods may be able to optimize some of the c reation and expressiveness issues, but they are still one off 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 Effective Java/Steve Ba l l to re t u rn a subclass of the type re t u rned by the method it o v e rrides. This re q u i res callers of the c lo ne method to cast the result to retrieve the type information that was lost when the c lo ne method re t u rn e d . The c lo nemethod 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 classes t 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 designing classes 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 tre f e rences, one that can be added to any of the Java API container classes that will cause them to perf o rm a deep copy when cloned. You will then have all the weaponry you need to beat Java at its own game. I 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 of c 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 column I’ll address the first, by showing how to explicitly disable cloning for classes that have inherited a c lo ne method 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 clone objects through O b je c t re f e rences because the c lo ne m e t h o d is declared p ro t e c t e d in O b je c t and, furt h e rm o re, is not accessible through the C lo ne a b le i n t e rface. Java’s designers openly acknowledge the difficulties this imposes, stating, “The sad fact of the matter is that C lo ne a b le is broken, and always 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, and one 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 d f rom cloning their elements because the c lo ne method is in- accessible. In the words of the Java language developers again, “In fact, it’s nearly impossible to do deep copies in Java, as the C lo ne a b lei n t e rface lacks a public c lo neo p e r a t i o n . ”4 J a v a ’s cloning mechanism has numerous defects and is unnecessarily awkward and error pro n e .1Not only that, but cloning has been even more “broken” by the addition of new language features such as fin a l members and inner classes, 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 more bothersome 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 8 2 Java ™Report | APRIL 2000 ht t p : / / w w w. j ava re po rt. co m

More Related