4
1 2 6 Java Report | JUNE 2000 h t t p : / / w w w. j av a re po rt . co m I DENTITY, STATE, AND behavior can typify an object. For example, value objects 1,2 —used for representing simple pieces of information in a system, such as dates, strings, and quantities— have state, simple behavior, and transparent identity. Objects with significant identity typically represent the load-bearing structures in a system, such as per- sistent data and graphical objects. They are also typi- cally objects with significant life cycles that respond to method calls quite differently, depending on what general state or mode their content represents. Objects that do not have significant identity tend to have less interesting life cycles. A stateless service object has no state and therefore no life cycle; a value-based object tends to have either a trivial life cycle model or no life cycle model (e.g., Immutable Value 1, 2 ). The life cycle of an object can be modeled in UML using a Harel statechart. 3 Statechart di- agrams can tell developers a lot about the behavior of objects over time, and un- like many conventional class models, the move to code is far from trivial. Java offers features that sim- plify the implementation of object life cycle models. General Pattern Objects for States is a general-purpose (as opposed to domain-specific) pattern 4 that can be used to move from a documented state model to a practical im- plementation. It demonstrates a classic use of del- egation into a class hierarchy. Note that the name Objects for States is used here in preference to its common name of State, as this is both a more accu- rate description of its structure—State describes broad intent, whereas Objects for States describes structure—and does not give the reader the impres- sion that this is the only solution style possible for object life cycles; i.e., it is “a state pattern” not “the state pattern”.* State Anatomy. Although often presented as a sin- gle pattern, many have recognized 5 that there is a great deal more to implementing such a collabora- tion than can be elegantly captured in a single pat- tern. A pattern that covers a great deal of complexity or describes a number of implementation alterna- tives can often be opened up to reveal a more in- volved decision structure better represented through a pattern language. Pattern languages combine a number of patterns into a coherent whole, connecting them together through a set of decisions, with one pattern effective- ly completing another. 1, 2 A pattern that plays a role in a language does not belong exclusively to that lan- guage: It may be used to resolve similar problems in other pattern languages and contexts. The Patterns. A pattern language describes how to build a system or an aspect of a system. This article looks at the Objects for States pattern from a Java perspective. It shows a fragment of a protolanguage (i.e., a work in progress), focusing specifically on how the mode of the object is represented—as opposed to, say, how its transitions are managed. Figure 1 depicts the basic flow, showing that once Objects for States has been selected we have an either/or decision as to how to best represent the objects housing the actual delegated behavior. Stateful Object and Stateless Object have applica- bility outside this context, e.g., in considering the statefulness (or otherwise) of a transactional middle tier. They are here woven into the language through common links and a shared example. The patterns are described using a simple intent-problem- solution-example form, with a simplified example. Kevlin Henney is an independent consultant and trainer based in the UK. *Unfortunately, I have seen the havoc this misconception can wreak on a system: Every single statechart was converted un- questioningly into an implementation of “the State” pattern. Pat- terns solve particular problems well; if applied inappropriately they reduce rather than increase communication and under- standing. A clear name counts for a lot. Kevlin Henney / [email protected] Patterns in Java Matters of state

Matters of State

Embed Size (px)

Citation preview

Page 1: Matters of State

1 2 6 Java™Report | J U N E 2 0 0 0 ht t p : / / w w w. j ava re po rt . co m

ID E N T I T Y, STATE, AND behavior can typify anobject. For example, value objects1 , 2—used forre p resenting simple pieces of information in asystem, such as dates, strings, and quantities—

have state, simple behavior, and transparent identity.Objects with significant identity typically re p re s e n t

the load-bearing stru c t u res in a system, such as per-sistent data and graphical objects. They are also typi-cally objects with significant life cycles that re s p o n dto method calls quite diff e re n t l y, depending on whatgeneral state or mode their content re p resents. Objectsthat do not have significant identity tend to have less

i n t e resting life cycles. Astateless service object hasno state and there f o re nolife cycle; a value-basedobject tends to have eithera trivial life cycle model orno life cycle model (e.g.,I m mu t a b le Va l u e1, 2) .

The life cycle of an object can be modeled in UML using a Harel s t a t e c h a rt .3 S t a t e c h a rt di-agrams can tell developers

a lot about the behavior of objects over time, and un-like many conventional class models, the move tocode is far from trivial. Java offers features that sim-plify the implementation of object life cycle models.

Ge n e ral Pat te rnO b je c ts for States is a general-purpose (as opposed tod o m a i n - s p e c i fic) pattern4 that can be used to movef rom a documented state model to a practical im-plementation. It demonstrates a classic use of del-egation into a class hierarc h y. Note that the nameO b je c ts for States is used here in pre f e rence to itscommon name of S t a t e, as this is both a more accu-rate description of its stru c t u re —S t a t e d e s c r i b e sb road intent, whereas O b je c ts for States d e s c r i b e ss t ru c t u re—and does not give the reader the impre s-

sion that this is the only solution style possible forobject life cycles; i.e., it is “a state pattern” not “t h estate pattern ” . *

St ate An ato my. Although often presented as a sin-gle pattern, many have re c o g n i z e d5 that there is ag reat deal more to implementing such a collabora-tion than can be elegantly captured in a single pat-t e rn. A pattern that covers a great deal of complexityor describes a number of implementation altern a-tives can often be opened up to reveal a more in-volved decision stru c t u re better re p resented thro u g ha pattern language.

P a t t e rn languages combine a number of pattern sinto a coherent whole, connecting them togethert h rough a set of decisions, with one pattern eff e c t i v e-ly completing another.1, 2 A pattern that plays a ro l ein a language does not belong exclusively to that lan-guage: It may be used to resolve similar problems inother pattern languages and contexts.

The Pat te rn s. A pattern language describes how tobuild a system or an aspect of a system. This art i c l elooks at the O b je c ts for States p a t t e rn from a Java perspective. It shows a fragment of a pro t o l a n g u a g e(i.e., a work in pro g ress), focusing specifically on howthe mode of the object is re p resented—as opposed to,s a y, how its transitions are managed. Figure 1 depictsthe basic flo w, showing that once O b je c ts for Stateshas been selected we have an either/or decision as tohow to best re p resent the objects housing the actualdelegated behavior.

Stateful Obje c t and S t a t e less Obje c t have applica-bility outside this context, e.g., in considering thestatefulness (or otherwise) of a transactional middlet i e r. They are here woven into the language thro u g hcommon links and a shared example. The pattern sa re described using a simple i n t e n t - p ro b l e m -s o l u t i o n - e x a m p l e f o rm, with a simplified example.

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.

*Unfortunately, I have seen the havoc this misconception canwreak on a system: Every single statechart was converted un-questioningly into an implementation of “t h e State” pattern. Pat-t e rns solve particular problems well; if applied inappro p r i a t e l ythey reduce rather than increase communication and under-standing. A clear name counts for a lot.

Kevlin He n n ey / [email protected] in Java

Matters of state

Page 2: Matters of State

1 2 8 Java™Report | J U N E 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

Objects forS t a t e sAllow an object toalter its behaviors i g n i f i c a n t l y b ydelegating state-based behavior toa separate object.

Pro b l e m . H o wcan an object sig-

n i ficantly change its behavior, depending on its intern a lstate, without h a rd w i red multipart conditional code? Thetemptation is to use a flag internal to the object and in eachmethod to switch to the correct behavior accord i n g l y. Thisc reates long and complex m e t h o d s . I t b e c o m e s d i ffic u l tto locate the behavior for a particular mode, and harder stillto modify the behavioral model corre c t l y.

So l u t i o n . Separate the behavior from the main class,which holds the context, into a separate class hierarc h y,w h e re each class re p resents the behavior in a part i c u l a rstate. The context object—the instance of the main classwith which the client communicates—aggregates an ob-ject that is used to re p resent the behavior in one of itsmodes. Method calls on the context are forw a rded to themode object; responsibility for implementing behaviorin a particular state is there f o re delegated and localized.This behavioral mode object can be implemented as ei-

ther a Stateful Obje c t or a S t a t e less Obje c t.The transition to a diff e rent state means replacement of

the behavior object by another of a diff e rent class. Poly-morphism replaces the explicit conditional lookup. Tr a n-sitions between states can be managed either by thebehavioral mode objects themselves or by a centralizedmechanism such as a table lookup.

To move with confidence from an existing design thatuses a type switch of some kind to one that takes advan-tage of O b je c ts for States, the Replace Type Code Wi t hState/Strategy re f a c t o r i n g6 can be applied. The separa-tion and increased messaging through forw a rding meth-ods means that this design is not suitable for distribution;i.e., mode objects should live in the same process as theircontext objects.

Ex a m p l e. Consider the life cycle for a simple clock shownin Figure 2.

Leaving aside the issue of transitions and time updates, a tempting procedural implementation can beseen in Listing 1. Such a contro l - flow-based approach ise rror prone and scatters the state-based behavior acro s smany methods.

Using O b je c ts for States t u rns the basic stru c t u re insideout through delegation. All behavior relevant to a part i c u-lar mode is encapsulated in an object and the entire selec-tion is expressed through p o l y m o r p h i s m (see Figure 3).

Stateful ObjectAllow a behavioral object that is separate from the dataobject on which it operates access to that data by pro v i d i n ga field with a re f e rence back to it.

Pro b l e m . Given a separation of behavior from the statethat the behavior operates on, so that the state is one ob-ject and the behavior in another, what is the simplest andmost direct design for the behavioral class? The separa-tion means that whereas the behavior and data were pre-viously in the same object—and there f o re the behaviorcould act directly on it—the behavior can no longer di-rectly manipulate the data.

So l u t i o n . Implement the behavioral class so that an in-stance has access to the contents of the object on which itoperates. Because of the knowledge of its context, it hasstate of its own. At any one time, a behavior object is ded-icated to a single context object.

Fi g u re 1. The Objects for States p a t te rn andthe co n s t ru ction of its re p re s e n t a t i o n .

Figure 2. Statechart representing the life cycle of a simple clock object.

L I S T I N G 1.

Implementation of the clock’s life cycle using a fla gand explicit switching.public class Clo c k{public sy nc h ronized void change Mo de() ...public sy nc h ronized void inc re me nt ( ){s w i tc h ( mo de ){case displaying T i me :t h row new Exc e p t i o n ( “ C h a nge mo de to change time ” ) ;b re a k ;

case setting Ho u rs :+ + ho u rs ;b re a k ;

case setting M i nu t e s :+ + m i nu t e s ;b re a k ;

}}public sy nc h ronized void cancel() .... . .p r i vate static final int displaying T i me = 0;p r i vate static final int setting Ho u rs = 1;p r i vate static final int setting M i nutes = 2;p r i vate int ho u rs, minu t e s ;p r i vate int mo de = setting Ho u rs ;

}

Page 3: Matters of State

The simplest and most direct approach is to use innerclasses so that the context object’s state is implicitly visibleto the behavior classes. Altern a t i v e l y, an explicit back re f-e rence from the behavior object to the context object can bed e fined, with either raw access to the data—through pack-age or class-level visibility—or via additional query andm o d i fier methods.

This retains the separation between behavior and the data of interest, but means that a behavioral object isnow tied to a single data object at a time. This can leadto increased object creation and collection costs, espe-cially if the behavior object is changed for another andthe previous object is discarded rather than cached. If this is significant, implementing the behavioral modeobject as a S t a t e less Obje c t re p resents an altern a t i v e .

Ex a m p l e. Implementing the clock class with a S t a t e f u lO b je c t for the mode leads to Listing 2. An improvement inthe creation/collection perf o rmance of the program wouldbe to preinstantiate all the re q u i red mode objects for an object and cache them in an arr a y.

Stateless ObjectAllow a behavioral object that is separate from the data ob -ject on which it operates access to that data by passing are f e rence back to it as an argument with each method call.

Pro b l e m . Given a separation of behavior from the state itoperates on, so that the state is in one object and the be-

havior in another, what design for the behavioral class min-imizes object creation and collection costs? A Stateful Ob-je c t is simple to implement and manage, but can lead toi n c reased object numbers and reduced perf o rmance if theobjects are created and discarded fre q u e n t l y.

So l u t i o n . Implement the behavioral class so that it hasno data in it whatsoever. The context object is nowpassed in as an argument to each of the delegated meth-ods of the behavioral objects. The simplest approach that

L I S T I N G 2 .

Implementation of the clock’s life cycle using a Stateful Object for the mode.public class Clo c k{public sy nc h ronized void change Mo de() { mo de. c h a nge Mo de();

}public sy nc h ronized void inc re me nt() { mo de. i nc re me nt(); }public sy nc h ronized void cancel() { mo de. c a ncel(); }p r i vate int e r face Mo de{void change Mo de ( ) ;void inc re me nt ( ) ;void canc e l ( ) ;

}p r i vate class Displaying T i me imple me nts Mo de ...p r i vate abstract class Setting T i me imple me nts Mo de{public void cancel() { mo de = new Displaying T i me(); }

}p r i vate class Setting Ho u rs ex t e nds Setting T i me ...p r i vate class Setting M i nutes ex t e nds Setting T i me{public void change Mo de() { mo de = new Displaying T i me();

}public void inc re me nt() { ++minutes; }

}p r i vate int ho u rs, minu t e s ;p r i vate Mo de mo de = new Setting Ho u rs ( ) ;

}

Fi g u re 3. Re p resentation of the clock state model using O b j e c t sfor States.

1 2 9ht t p : / / w w w. j ava re po r t . co m J U N E 2 0 0 0 | Java™ Report

Kevlin He n n ey / Patterns in Java

L I S T I N G 3 .

Implementation of the clock’s life cycle using a Stateless Object for the mode.public class Clo c k{public sy nc h ronized void change Mo de ( ){ mo de. c h a nge Mo de(this); }

public sy nc h ronized void inc re me nt ( ){ mo de. i nc re me nt(this); }

public sy nc h ronized void canc e l ( ){ mo de. c a ncel(this); }

p r i vate int e r face Mo de{void change Mo de ( C lock cont ex t ) ;void inc re me nt ( C lock cont ex t ) ;void canc e l ( C lock cont ex t ) ;

}p r i vate static class Displaying T i me imple me nts Mo de ...p r i vate static abstract class Setting T i me imple me nts Mo de

. . .p r i vate static class Setting Ho u rs ex t e nds Setting T i me ...p r i vate static class Setting M i nutes ex t e nds Setting T i me{public void change Mo de ( C lock cont ex t ){ cont ex t . mo de = displaying T i me; }public void inc re me nt ( C lock cont ex t ){ ++cont ex t . m i nutes; }

}p r i vate int ho u rs, minu t e s ;p r i vate Mo de mo de = setting Ho u rs ;p r i vate static final Mo ded i s p l a y i ng T i me = new Displaying T i me ( ) ,s e t t i ng Ho u rs = new Setting Ho u rs ( ) ,s e t t i ng M i nutes = new Setting M i nu t e s ( ) ;

}

Page 4: Matters of State

allows behavioral objects to see the encapsulated contentof the context objects is to declare them as s t a t i c t o p - l e v-el classes in the context class. Altern a t i v e l y, package-lev-el visibility or additional query and modifier methodsmust be pro v i d e d .

Such behavioral objects are stateless, so they may bet reated as F l y we i g ht4 objects that can be shared transpar-ently among all instances of the context class. The ab-sence of state also means that they are implicitlyt h readsafe, and there f o re need no synchronization. Giv-en that only a single instance of a behavioral class is everre q u i red, S i ng le to n4 can be used to good effect when thedefinition of the behavioral class is outside the contextclass. Otherwise, S i ng le to n is overkill where p r i vate staticdata would suffic e .

The entire selection of behavior is now expressed pure-ly through polymorphism and callbacks on a separate ob-ject, and the state now resides only in the context object.If the behavioral model is quite stable, the V i s i to r p a t t e rnmay be used to put all the behavioral code back into thecontext object, with the behavioral mode object doing se-lection only and calling the relevant method on the con-text object to do all of the work. However, although this issometimes an option, it can make the implementation un-wieldy for large state models: The context class suff e r smethod explosion.

Ex a m p l e. Implementing the clock class with a S t a t e less Ob-je c t for the mode leads to Listing 3.

Co n c l u s i o nA common pattern for implementing object life cycle mod-els can be expressed in terms of other more granular pat-t e rns. They may be linked together in a language; i.e., themain pattern can be said to c o n t a i n or be completed by t h eother patterns. In this article, the focus was on the re p re-sentation of the O b je c ts for States p a t t e rn in Java, as opposedto a more detailed view of the whole pattern .5

The Stateful Obje c t and S t a t e less Obje c t p a t t e rns assist inthe realization of O b je c ts For States, but are not tied exclu-sively to it: The issues raised and resolutions given can befound in many object systems whether local or distributed,(e.g., EJB). ■

Re fe re n ce s

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

2 .H e n n e y, K., “Value added,” Java Report , Vol. 5, No. 4, Apr.2000, pp. 74–82.

3 . Rumbaugh, J., I. Jacobson, and G. Booch, The Unified Mod -eling Language Reference Manual, Addison–We s l e y, 1999.

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

5 . Dyson, P. and Anderson, B. “State Patterns,” P a t t e rn Lan -guages of Program Design 3, R. Martin, D. Riehle, and F.Buschmann, Eds., Addison–We s l e y, 1998.

6 .F o w l e r, M., Refactoring: Improving the Design of ExistingC o d e, Addison–We s l e y, 1999.

1 3 0 Java™Report | J U N E 2 0 0 0 h t t p : / / w w w. j ava re po r t . co m

Patterns in Java / Kevlin He n n ey