Collections for States

5
9 2 Java Report | AUGUST 2000 h t t p : / / w w w. j a v a re po r t . co m I N DESIGN, AS with anything in life, we often fall into a groove, repeating the same solutions time and again. Sometimes this is appropriate— hence the emphasis in the patterns community on capturing recurring practice—but at other times force of habit is the real decision maker. Patterns are all about capturing breadth as well as depth of knowledge, and we can find that problems that seemed similar have enough minor differences that the appropriate solutions are in fact poles apart. Consider implementing a life cycle model for an object. It is tempting to use a flag-and-switch approach, but this is often clumsy for models with significant behavior. Objects for States (commonly known as the State p a t t e rn ) 1 , 2 a l l o w s state-specific behavior to be represented elegantly t h ro u g h a delegation and inheritance s t ru c t u re . However, it is not the case that Objects for States is the s t a t e p a t t e rn , a n d t h a t other techniques—includ- ing flags—are inappropriate in realizing state models. The Collections for State pattern addresses the imp- lementation of simple state models over collections of objects. Problem Consider a collection of similar objects managed by another object. The managed objects have life cycles with only a few distinct states, and the Manager object 3 often operates on them collectively with respect to the state they are in. These objects can be modeled as individual state machines. What is a suitable model for the collected objects and their managing objects that empha- sizes and supports this independence? How is the responsibility for state representation and man- agement divided between the collected objects and their managers? Example Consider an application that holds a number of graphical objects that may be manipulated by a user, such as a CAD tool or graphical editor. The general shape of this application follows from the Workpieces frame. 4 The objects manipulated by the user are the workpieces, and these may be saved to some kind of persistent storage, for example, a data- base. These objects all share a simple life cycle model (see Figure 1) in addition to their type-spe- cific state. For such a simple state model, the use of the Objects for States pattern 1,2 —where a class hierarchy is introduced to reflect the separate states and the behavior associated with them—would be overkill. Instead, we can use a simpler flag-based approach, representing state as an attribute. In Java this would lead to the code in Listing 1. The saveState method acts as a Template Method. 1 In an application, which acts as the manager for workpiece objects, saving all the changes since the last save could be implemented as shown in Listing 2. * However, this suffers from a verbose control structure and an excessive amount of checking. We might try to simplify the application class logic with the rearrangement of responsibilities shown in Listing 3. Here we have hidden the condition check to simplify usage. However, both of these approaches fail to address the basic flaw: the brute force of the “linear search, query, then act” model. This is both cum- bersome and inefficient, especially where many Kevlin Henney is an independent consultant and trainer based in the UK. Kevlin Henney / [email protected] Patterns in Java Collections for States Figure 1. Statechart representing the simple life cycle of a workpiece object. * Note that for brevity, and to keep a focus on the core prob- lem at hand, handling of threading and exception issues are omitted from the code.

Transcript of Collections for States

Page 1: Collections for States

9 2 Java™Report | A U G U S T 2 0 0 0 ht t p : / / w w w. j ava re po r t . co m

IN DESIGN, AS with anything in life, we oftenfall into a groove, repeating the same solutionstime and again. Sometimes this is appropriate—hence the emphasis in the patterns community

on capturing recurring practice—but at other timesforce of habit is the real decision maker. Patterns areall about capturing breadth as well as depth ofknowledge, and we can find that problems thatseemed similar have enough minor differences thatthe appropriate solutions are in fact poles apart.

Consider implementing a life cycle model for anobject. It is tempting to use a fla g - a n d -s w i tc h a p p ro a c h ,

but this is often clumsy formodels with significantb e h a v i o r. O b je c ts for States(commonly known as theS t a t e p a t t e rn )1 , 2 a l l o w ss t a t e - s p e c i fic b e h a v i o r t ob e re p re s e n t e d e l e g a n t l yt h ro u g h a delegation and i n h e r i t a n c e s t ru c t u re .H o we v e r, it is not the casethat O b je c ts for States is t h es t a t e p a t t e rn , a n d t h a tother techniques—includ-

ing fla g s — a re inappropriate in realizing state models.The C o l le c t i o ns for State p a t t e rn addresses the imp-lementation of simple state models over collections of objects.

Pro b l e mConsider a collection of similar objects managed byanother object. The managed objects have lifecycles with only a few distinct states, and theManager object3 often operates on them collectivelywith respect to the state they are in. These objectscan be modeled as individual state machines.

What is a suitable model for the collectedobjects and their managing objects that empha-sizes and supports this independence? How is theresponsibility for state re p resentation and man-

agement divided between the collected objectsand their managers?

Ex a m p l eConsider an application that holds a number ofgraphical objects that may be manipulated by auser, such as a CAD tool or graphical editor. Thegeneral shape of this application follows from theWorkpieces frame.4 The objects manipulated by theuser are the workpieces, and these may be saved tosome kind of persistent storage, for example, a data-base. These objects all share a simple life cyclemodel (see Figure 1) in addition to their type-spe-cific state.

For such a simple state model, the use of theObjects for States pattern1,2—where a class hierarchyis introduced to reflect the separate states and thebehavior associated with them—would be overkill.Instead, we can use a simpler flag-based approach,representing state as an attribute. In Java this wouldlead to the code in Listing 1. The saveState methodacts as a Template Method.1

In an application, which acts as the managerfor workpiece objects, saving all the changessince the last save could be implemented asshown in Listing 2.* H o w e v e r, this suffers from averbose control stru c t u re and an excessiveamount of checking. We might try to simplify theapplication class logic with the re a rrangement ofresponsibilities shown in Listing 3. Here we havehidden the condition check to simplify usage.H o w e v e r, both of these approaches fail to addre s sthe basic flaw: the brute force of the “linears e a rch, query, then act” model. This is both cum-bersome and inefficient, especially where many

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.

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

Collections for States

Figure 1. Statechart representing the simple life cycle ofa workpiece object.

* Note that for bre v i t y, and to keep a focus on the core pro b-lem at hand, handling of threading and exception issues areomitted from the code.

Page 2: Collections for States

9 4 Java™Report | A U G U S T 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

workpiece objects exist but only a small pro p o rtion aremodified between saves.

Fo rce sFor an object whose own behavior changes substantiallydepending on its state in a life cycle, the most dire c timplementation is for the object to be fully aware of itsown state, meaning the object is self-contained andincludes all of the mechanisms for its behavior. Thisa w a reness strengthens the coupling between the objectand its life cycle, and increases the footprint of the indi-vidual object.

For simple state models, flag-based solutions areattractive because they do not re q u i re much code stru c-turing eff o rt, although they may lead to simplistic codewith a lot of repetition and explicit, hard - w i red contro lflo w. With O b je c ts fo r S t a t e s, selection is expre s s e dt h rough polymorphism and behavior within a state ismodeled and implemented cohesively. This allowsg reater independence between the object and its statemodel, but can be complex in implementation, and, for

l a rge state models, it is very easy to lose sight of the lifecycle model. State transition tables can be used to driveand increase the flexibility of either the flag-based or theO b je c ts for States a p p roach, or they may be used indepen-d e n t l y. In all of these cases the object is tied to a singlegiven life cycle model. If the object’s own behavior isl a rgely independent of its state, and such state behavior isl a rgely governed by its application context, an intern a lre p resentation of the life cycle can increase the couplingof the object to its enviro n m e n t .

If an object’s life cycle is managed extern a l l y, theobject is independent of its context and its re p re s e n t a-tion is simplified. However, this means that signific a n tdecisions about the object’s behavior are now taken out-side the object. One of the benefits of flag variables,O b je c ts for States, or state transition tables is that thestate model is made explicit intern a l l y. Objects are fullya w a re of what state they are in and can act appro p r i a t e-l y w h e n u s e d o u t s i d e t h e c o l l e c t i v e . A l s o , a n y o n ee x a m i n i n g t h e c l a s s c a n e a s i l y d e t e rm i n e its objects’possible life cycles.

Modeling states explicitly and internally means thatchanges to the life cycle model re q u i re changes to theclass code. So life cycles cannot easily be modified inde-pendently of the class’s re p re s e n t a t i o n .

With O b je c ts for States, having additional state-depen-dent data is relatively simple: The class re p resenting ap a rticular state also defines any data that is relevant toobjects in that state. For flag-based or state transitiontables the data must be held elsewhere, typically aspotentially redundant data within the stateful object. If

L I S T I N G 1 .

R e p resentation of workpiece saved as a fla g .public abstract class Wo r k p i e c e{

public void save ( ){

s a ve S t a t e ( ) ;c h a nged = fa ls e ;

}public boolean save d ( ){

return !change d ;}. . .p rotected abstract void save S t a t e ( ) ;p r i vate boolean change d ;

}

L I S T I N G 2 .

Saving all changed workpiece objects by queryingthen saving them.public class Application{

public void save C h a nge s ( ){

It e ra tor workpiece = change d . i t e ra to r ( ) ;w h i le ( wo r k p i e c e. h a s N ex t ( ) ){

Workpiece curre nt = (Workpiece) wo r k p i e c e. nex t ( ) ;i f ( ! c u r re nt . s a ve d ( ) )

c u r re nt . s a ve ( ) ;}

}. . .p r i vate Collection wo r k p i e c e s ;

}

L I S T I N G 3 .

The workpiece assumes responsibility for saving ornot, depending on its state.public abstract class Wo r k p i e c e{

public void save ( ){

i f ( c h a nge d ){

s a ve S t a t e ( ) ;c h a nged = fa ls e ;

}}. . .

}

public class Application{

public void save C h a nge s ( ){

It e ra tor workpiece = change d . i t e ra to r ( ) ;w h i le ( wo r k p i e c e. h a s N ex t ( ) )

( ( Workpiece) wo r k p i e c e. nex t ( ) ) . s a ve ( ) ;}. . .

}

Page 3: Collections for States

9 6 Java™Report | A U G U S T 2 00 0 ht t p : / / w w w. j ava re po r t . co m

Patterns in Java / Kevlin He n n ey

the life cycle is managed extern a l l y, extra state-depen-dent data can introduce the same management burd e n ;i.e., the manager must add additional context data to itscollection entry, or potentially redundant data must beheld within the object.

With collections of objects in which objects in thesame state receive uniform treatment, an internal statere p resentation can lead to poor perf o rmance. All col-lected objects are traversed re g a rdless of the state theya re in, and a state-based decision is taken for each one:e x p l i c i t l y, in the case of flag variables; implicitlyt h rough polymorphic lookup, in the case of O b je c ts fo rS t a t e s; implicitly through table lookup, in the case ofstate transition tables. In each case, eff o rt is wasted tra-versing all objects only to perf o rm actions on a subset.E x t e rnal life cycle management can support a concep-tually more direct approach, which also has perf o r-mance benefit s .

So l u t i o nRepresent each state of interest by a separate collectionthat refers to all objects in that state. The manager objectholds these state collections and is responsible for man-aging the life cycle of the objects. When an objectchanges state, the manager ensures that it is moved fromthe collection representing the source state to the collec-tion representing the target state.

Re s o l u t i o nApplying this solution to our example leads to the codes t ru c t u re shown in Listing 4. This is both signific a n t l ysimpler and more efficient, in terms of traversal, thanthe previous attempts. The s a ve d collection refers to theobjects that are unchanged, and when modified via theapplication these are transferred to the c h a nge d c o l l e c-tion. As this is a re f e rence move, it is cheap. To save allof the changed workpiece objects, the application

object simply needs to run through the c h a nge d c o l l e c-tion saving each workpiece there, and then merge themback into the s a ve d c o l l e c t i o n .

If we need to treat all of the objects collectively, re g a rd-less of state, it would be tedious to set up two loops to ru nt h rough all of the objects—one for s a ve d and one forc h a nge d—and so we can re p resent the superstate of savedand changed workpiece objects—i.e., all workpieceobjects, with an additional collection (see Listing 5).

The extra collection is the “boss” collection, holdingall of the objects in a definitive order. It is used for state-independent operations that apply to all the objects. Theconstraint is clearly that objects can only be present inone of the two state collections but must be present inthe boss collection, and any object present in the bosscollection must be present in one of the state collections.

In the context of this application, if unsaved objects areof interest because we can collectively save them, but savedobjects are not operated on as a group, we can refactor toeliminate the use of the s a ve d collection (see Listing 6).

Co n s e q u e n ce sBy assigning collections to represent states we can nowmove all objects in a particular state into the relevant col-lection and perform actions on them together. Other thanselecting the correct collection for the state, there is noselection or traversal required. Thus, this is both a clean-er expression of the model and a more time efficient one.

An object’s collection implicitly determines its state,so there is no need to also represent the state internally.Because the objects are already being held collectively,this can lead to a smaller footprint per object, which canbe considered a benefit for resource constrained environ-

L I S T I N G 4 .

Applying the proposed solution to the exa m p l e.public abstract class Wo r k p i e c e{

public abstract void save ( ) ;. . .

}

public class Application{

public void save C h a nge s ( ){

It e ra tor workpiece = change d . i t e ra to r ( ) ;w h i le ( wo r k p i e c e. h a s N ex t ( ) )

( ( Workpiece) wo r k p i e c e. nex t ( ) ) . s a ve ( ) ;s a ve d . add A l l ( c h a nge d ) ;c h a nge d . c le a r ( ) ;

}. . .p r i vate Collection saved, change d ;

}

L I S T I N G 5 .

Adding a single collection to hold all of the objectsm a n a g e d .public class Application{

. . .C o l lection wo r k p i e c e s, saved, change d ;. . .

}

L I S T I N G 6 .

Ignoring a collection for saved objects.public class Application{

public void save C h a nge s ( ){

It e ra tor workpiece = change d . i t e ra to r ( ) ;w h i le ( wo r k p i e c e. h a s N ex t ( ) )

( ( Workpiece) wo r k p i e c e. nex t ( ) ) . s a ve ( ) ;c h a nge d . c le a r ( ) ;

}. . .p r i vate Collection wo r k p i e c e s, change d ;

}

Page 4: Collections for States

9 7h t t p : / / w w w. j ava re po r t . co m A U G U S T 2 0 0 0 | Java™ Report

Kevlin He n n ey / Patterns in Java

ments but need not be a necessary consequence of apply-ing this pattern. Adding back-references from the objectto its collection or manager would lose this space saving.

The manager object now has more responsibility andbehavior, and the managed object less. On the one hand,this can lead to a more complex manager; on the other, itmeans that the object life cycle model can be changedindependently of the class of the objects. The state modelcan be represented within the manager using Objects forStates, state transition tables, or explicit hardwired con-ditional code, as in the motivating Example section. Thestate management can become more complex if, inchanging, the state model acquires significantly morestates and therefore more collections to manage.

Multiple state models can be used without affectingthe class of the objects. For instance, we can introduceorthogonal state models for the objects in the motivatingexample based on their Z-ordering, versioning, or someother property, e.g., if they are active objects whether ornot they are currently active. When orthogonal life cyclesare involved, allocating separate managers for differentstate models simplifies the implementation: A singlemanager would become significantly more complexthrough acquisition of this extra responsibility.

Where the manager acts as some kind of Broker,5 allcommunication with the objects will be routed throughit and therefore the manager will be fully aware of whatchanges objects might undergo. If objects can be acted onindependently, state-changing events must not cause themanager to keep the wrong view, and so the managerwould have to be notified. The notification collaborationcan be implemented as a variation of the Observer pat-tern,1 with the object either notifying the manager of achange or requesting a specific state transition of themanager. This would result in the object maintaining aback pointer to the manager, which would increase thecomplexity of the code, create a dependency on the man-ager, and increase the object’s footprint.

A bidirectional relationship can also arise if the object’sown behavior is not completely independent of the man-a g e r’s view of it, and must there f o re have some aware n e s sof its own state. In this case, an alternative is to adoptsome redundant internal state that acts as a cache.

There are as many collections as there are states ofinterest in the life cycle. Figure 2 includes superstates ora boss collection that holds all of the objects (notionallya superstate of all of the states for the objects, anyway).Thus, this pattern can be applied recursively, and eachsuperstate collection corresponds to the union of the cor-responding substate collections. A boss collection is nec-essary where all of the objects need to be treated collec-tively, as in a display or update. Iterating through manyseparate collections is not appropriate either because ofconvenience or because of ordering. The presence of aboss collection may obviate the need for one of the statecollections; i.e., there are no collective operations per-formed in that state and the object is already accounted

for in the boss collection.It is not as easy to accommodate additional state-

dependent data for each object as to manage the stateexternally. Either the object must hold redundant state orit must be held along with the object’s entry in the collection state. This leads to an increase in code com-plexity and runtime footprint. For instance, in the motivating example we could hold additional data forsaved objects, indicating when they were last saved, orfor changed objects to record when they were changed.

W h e re the frequency of state change is high this patterncan become impractical: The mediation of state changef rom one collection to another via a manager can come todominate the execution time of the system. Internal re p re-sentations have the benefit of immediacy when objects areacted on individually and with frequent state changes.

The Collections for States pattern can be used in con-junction with other life cycle implementations as anoptimization. For instance, in a library where each loanknows its due date, an explicit collection of overdueloans can be cached for quick access, rather than requir-ing a search over all loans for each inquiry.

Di s c u s s i o nIn C o l le c t i o ns for States, variation in state is expre s s e dt h rough collection objects rather than through polymor-phic classes. In this, and many other respects, it is insideout when compared to O b je c ts for States, with state modeledextrinsically rather than intrinsically, i.e., the life cycle isw o rn on the outside. It is focused on collections of objectsrather than individual objects, and the entity/b e h a v i o rseparation is in the opposite direction, with behavior man-aged by the collective rather than something within theindividual. Such structural inversion illustrates that solu-tions to apparently similar problems, such as state man-agement need not be similar: Radically diff e rent stru c t u re s ,w h e re roles and responsibilities are almost re v e r s e dbetween the solution elements, can arise from part i c u l a rd i ff e rences in the context and problem statement.

A set of decisions can guide developers to weigh upthe use of this pattern, as opposed to or in conjunctionwith Objects for States, state transition tables, and flag-based approaches, suggesting that all these approachescan be related within a generative framework, i.e., a pat-tern language. Such a language could be integrated withand extend existing state implementation languages.2,6

Where independence and collective requirements domi-

Fi g u re 2. Schematic re p resenting the fe a t u res of thep ro posed solution.

Page 5: Collections for States

9 8 Java™Report | A U G U S T 2 00 0 ht t p : / / w w w. j ava re po rt . co m

Patterns in Java / Kevlin He n n ey

nate, Collections for States proves to be a good match, lead-ing to a better representation of the conceptual modeland a simplification of object implementation.

We can see this pattern in systems programming andoperating systems. It is the strategy used in file systems forhandling free as opposed to used file blocks. It is used bymany heap managers for holding free lists, especiallydebugging heap managers that track memory usage, i.e.,f ree, used, and freed. Schedulers hold separate queues toschedule processes, which may be in various diff e re n tstates of readiness—e.g., running, ready to run, or blocked.The theme of processing states can also be seen at a higherlevel in GUI systems supporting a comprehensive edit com-mand history facility. Once initially executed, a commandcan be in one of two states, either done so that it can beundone or undone so it can be redone. In this case, the col-lections used are quite specific and must support stack-o rd e red (last-in/first-out) access. An example of this can beseen in the C o m m a nd Pro c e s s o r p a t t e rn .5

Collections for States is also a common strategy fordenormalizing a relational database. The library loanexample would include a table representing overdueloans. The low frequency of state change and the relativesizes of the collections make this external state model anefficient and easy to manage caching strategy, optimizinglookup for queries concerning overdue books.

We also see a similar structure in action in the real

w o r l d w h e n p e o p l e a re g ro u p e d t o g e t h e r b e c a u s e o fsome aspect of state, for example, queuing at airportswith respect to EU and non-EU passports. ■

Ac kn ow l e d g m e nt sT h i s a rt i c l e i s b a s e d o n a p a p e r w o r k s h o p p e d a t t h eE u ro P L o P ’ 9 9 c o n f e re n c e . I w o u l d l i k e t o t h a n k P a u lDyson, Charles Weir, Brad Appleton, Frank Buschmann,a n d t h e re s t o f t h e P O S A t e a m a t S i e m e n s , a n d the members of the EuroPLoP ’99 Writers’ Workshop whoreviewed the original paper.

Re fe re n ce s

1.Gamma, E. et al., Design Patterns: Elements of ReusableObject-Oriented Software, Addison–Wesley, 1995.

2.Henney, K., “Matters of State,” Java Report, Vol. 5, No. 6,June 2000, pp. 126–130.

3.Sommerlad, P., “Manager,” Pattern Languages ofProgram Design 3, R. Martin, D. Riehle, and F.Buschmann, Eds., Addison–Wesley, 1998.

4.Jackson, M., Software Requirements & Specifications: ALexicon of Practice, Principles and Prejudices,Addison–Wesley, 1995.

5.Buschmann, F. et al., Pattern-Oriented SoftwareArchitecture: A System of Patterns , John Wiley & Sons,1996.

6.Dyson, P. and B. Anderson, “State Patterns,” PatternLanguages of Program Design 3 , R. Martin, D. Riehle,and F. Buschmann, Eds., Addison–Wesley, 1998.

“None of the problems we’re having with Java areshowstoppers,” says Brilon. “They are all problems wecan work around. And, at the end of the day, when wecan tally up the math of what we’re doing, we see theadvantages far outweigh the disadvantages. So we’rehappy with our decision.” ■

Philip J. Gill is As s oc i ate Ed i tor of Java Re po rt. He can be co nt a cte d

at java b u z z @ s i g s. co m .

Real Wo rld Java continued from page 30

U R Ls

Apache So ftwa re

Fo u n d at i o n

w w w. a p a c h e. o rg

En hyd ra . o rg

w w w. e n hyd ra . o rg

MySQL dow n l o a d

we b. mys q l . co m

Sun Mi c ro s ys tems In c.

w w w. s u n . co m

Palm In c.

w w w. p a l m . co m

Mi c ro s o ft Co rp.

w w w. m i c ro s o ft. co m

Li n u x

w w w. l i n u x . o rg

Bl a c k d ow n

w w w. b l a c k d ow n . o rg

Inte l

w w w. i nte l . co m

Lu t ris In c.

w w w. l u t ri s. co m

Penguin Sys tems In c.

w w w. pe n g u i n co m p u t-

i n g. co m

Red Hat Sys tems In c.

w w w. re d h at. co m

HTML has two types of tags: those which come in pairs(< b o d y > and < / b o d y >) and those that do not (< p >). This sec-ond type of tag must be handled in the h a nd le S i m p le Tag ( )method. In our case, we don’t care about those tags; wesimply print them out if the d u m p O K flag is set to tru e .

T h a t ’s basically all there is to it, although to completethe example, we’d really need to do a lot more work.We’d have to keep track of anchors in each document tomake sure they are not repeated, and we’d want tochange hyperlinks between the original documents tohyperlinks within our new single document. We mightwant to insert <hr> tags every time a document ends toindicate a visual separation between chapters; we coulddo that by printing < h r > in the h a nd le E nd Tag ( ) m e t h o dwhen the tag in question is H T M L . Tag . H T M L. However,you might want to manipulate the HTML, the documentparser gives you an easy way to do it. ■

S co t t’s So l u t i o n s continued from page 104

Write to UsSend co m m e nt s, suggestions

for to p i c s, e tc. to Managing Ed i tor Anna M. Kanson at

a ka n s o n @ s i g s. co m