something for nothing

4
7 4 Java Report | DECEMBER 1999 h t t p : / / w w w. j av a re po rt . co m I T HAS BEEN said that in the days of punched cards, if you wanted to find out how heavy a piece of software was you weighed all the holes. In spite of its apparent lack of substance, there are features of software that make it more substantial to its users and developers: You can develop classes, deploy them, reuse them, etc.; you can run applications and interact with them; you can describe software—espe- cially software built with components and running with objects—in very physical terms. If you want something really insubstantial you have to scratch the software development surface a lit- tle deeper: What are the successful design struc- tures, concepts, and prac- tices that go into making up a system? How do you communicate a piece of de- sign to a colleague or ex- press your ideas in code? What are the benefits of one solution over another, and how does applying a solu- tion affect the subsequent design? For software, pat- terns seem to offer a discipline based on applied thought; naming, structuring, and communicating re- curring solutions to problems. In software development, the most visible popular- ization of patterns is the Gang of Four’s (GoF) Design Patterns catalog, 1 containing 23 common design pat- terns found in object-oriented (OO) systems. Howev- er, these are not the only patterns, and there is a wealth of literature produced by the patterns community that focuses on patterns at all levels and across many dif- ferent domains. This column is going to look beyond the relatively familiar GoF territory, presenting other examples that demonstrate patterns and pattern con- cepts of use to the Java developer, as well as some re- visits of GoF patterns from a different perspective. So, without further ado, let’s look at a useful tacti- cal pattern: Null Object 2 is perhaps best described as something based on nothing. Null Object The intent of a Null Object is to encapsulate the absence of an object by providing a substitutable object that of- fers suitable default, do nothing behavior. In short, a design where, as William Shakespeare’s King Lear would say, “nothing will come of nothing.” Problem Given that an object reference may optionally be null, and that the result of a null check is to do nothing or use a default value, how can the presence of a null be treated transparently? Example Consider providing a logging facility for some kind of simple server service that can be used to record the outcome of operations and any significant events in the operation of the service. One can imagine many different kinds of log, such as a log that writes direct- ly to the console or one that uses RMI to log a message on a remote server. However, a server is not required to use a log, so the association between the server and log is optional. Figure 1 shows the relationships dia- grammed in UML, and Listing 1 shows the Log inter- face and two possible simple implementing classes. In Listing 2, we can see that in using a Log object we must always ensure that we first check it is not null, be- cause we know it is optional, i.e., logging need not be enabled for a service. Forces There is a great deal of procedural clunkiness in code, such as: if(log != null) log.write(message); This can be repetitive, as well as error prone: It is possible to forget to write such a null guard, and repeated checks for null references can clutter and obfuscate your code. The user is required to detect the condition and Something for nothing Kevlin Henney is a Principal Technologist with QA Training in the UK. Kevlin Henney / [email protected] Patterns in Java *Complete references are available at Java Report Online— www.javareport.com

Upload: kevlin-henney

Post on 17-Jul-2015

259 views

Category:

Software


1 download

TRANSCRIPT

Page 1: Something for Nothing

7 4 Java™Report | D E C E M B E R 19 9 9 ht t p : / / w w w. j ava re po rt . co m

IT HAS BEEN said that in the days of punchedc a rds, if you wanted to find out how heavy a pieceof software was you weighed all the holes. In spiteof its apparent lack of substance, there are feature s

of software that make it more substantial to its usersand developers: You can develop classes, deploythem, reuse them, etc.; you can run applications andinteract with them; you can describe software — e s p e-cially software built with components and ru n n i n gwith objects—in very physical term s .

If you want something really insubstantial youhave to scratch the software development surface a lit-

tle deeper: What are thesuccessful design stru c-t u res, concepts, and prac-tices that go into making upa system? How do youcommunicate a piece of de-sign to a colleague or ex-p ress your ideas in code?What are the benefits of onesolution over another, andhow does applying a solu-tion affect the subsequentdesign? For software, pat-

t e rns seem to offer a discipline based on appliedthought; naming, structuring, and communicating re-c u rring solutions to pro b l e m s .

In software development, the most visible popular-ization of patterns is the Gang of Four’s (GoF) D e s i g nP a t t e rn s c a t a l o g ,1 containing 23 common design pat-t e rns found in object-oriented (OO) systems. Howev-e r, these are not the only patterns, and there is a wealthof literature produced by the patterns community thatfocuses on patterns at all levels and across many dif-f e rent domains. This column is going to look beyondthe relatively familiar GoF terr i t o ry, presenting otherexamples that demonstrate patterns and pattern con-cepts of use to the Java developer, as well as some re-visits of GoF patterns from a diff e rent perspective.

So, without further ado, let’s look at a useful tacti-cal pattern: Null Obje c t2 is perhaps best described as

something based on nothing.

Null ObjectThe intent of a Null Object is to encapsulate the absenceof an object by providing a substitutable object that of-fers suitable default, do nothing behavior. In short, adesign where, as William Shakespeare ’s King Learwould say, “nothing will come of nothing.”

Pro b l e m

Given that an object re f e rence may optionally be null,and that the result of a null check is to do nothing oruse a default value, how can the presence of a null bet reated transpare n t l y ?

Ex a m p l e

Consider providing a logging facility for some kind ofsimple server service that can be used to re c o rd theoutcome of operations and any significant events inthe operation of the service. One can imagine manyd i ff e rent kinds of log, such as a log that writes dire c t-ly to the console or one that uses RMI to log a messageon a remote serv e r. However, a server is not re q u i re dto use a log, so the association between the server andlog is optional. Figure 1 shows the relationships dia-grammed in UML, and Listing 1 shows the L o g i n t e r-face and two possible simple implementing classes.

In Listing 2, we can see that in using a Log object wemust always ensure that we first check it is not null, b e-cause we know it is optional, i.e., logging need not beenabled for a serv i c e .

Fo rce s

T h e re is a great deal of procedural clunkiness in code,such as:

i f ( log != nu l l )lo g . w r i t e ( me s s age ) ;

This can be repetitive, as well as error prone: It is possibleto forget to write such a null guard, and repeated checksfor null re f e rences can clutter and obfuscate your code.

The user is re q u i red to detect the condition and

Something for nothing

Kevlin He n n ey is a Principal Te c h n o l ogist with QA Training inthe UK.

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

*Complete references are available at Java Report Online—w w w. j a v a re p o rt . c o m

Page 2: Something for Nothing

take appropriate inaction, even though the condition is notin any way exceptional, i.e., having a nu l l log is a norm a land expected state of the relationship. The condition makesthe use of the logging facility less uniform .

A feature of conditional code is that it makes the deci-sion flow of the code explicit. However, this is only a ben-e fit if the decisions taken are important to the logic of thes u rrounding code; if they are not, then the resulting code isless rather than more direct, and the core algorithm be-comes obscure d .

W h e re conditional code does not serve the main purposeof a method, it tends to get in the way of the method’s ownlogic, making the method longer and harder to understand.

This is especially true if the code is frequently re p e a t e d ,as one might expect from the logging facility in the exam-ple, or if in-house coding guidelines encourage use ofblocks in pre f e rence to single statements:

i f ( log != nu l l ){

lo g . w r i t e ( me s s age ) ;}

The use of an explicit conditional means that the user maychoose alternative actions to suit the context. However, ifthe action is always the same and if the optional re l a t i o n-ship is frequently used, as one might expect of logging, itleads to duplication of the condition and its action. Dupli-cate code is considered to have a “bad smell,”3 and worksagainst simple changes (fixes or other impro v e m e n t s ) .

W h e re the relationship is null, t h e re is no execution costexcept a condition test and branch. On the other hand, theuse of an explicit test means that there will always be a test.Because this test is repeated in separate pieces of code, it is

7 6 Java™Report | D E C E M B E R 19 9 9 ht t p : / / w w w. j ava re po rt . co m

Patterns in Java / Kevlin He n n ey

Fi g u re 1. UML class diagram of a serv i ce with optional suppo rt

for l og g i n g.

Figure 2. Key classes in a Null Object co l l a bo ra t i o n .

L I S T I N G 1. . .

The root L o g i n t e r face and sample c o n c rete implementations.public int e r face Log{

void write(String me s s age To L o g ) ;}

public class Cons o leLog imple me nts Log{

public void write(String me s s age To L o g ){

S ys t e m . o u t . p r i nt l n ( me s s age To L o g ) ;}

}

public class FileLog imple me nts Log{

public File L o g ( S t r i ng lo g F i le N a me )t h rows IO E xc e p t i o n

{out = new File Wr i t e r ( lo g F i le N a me, true);

}public void write(String me s s age To L o g ){

t r y{

out.write(“[“ + new Date() + “] “ +me s s age ToLog + “\n”);

o u t . flus h ( ) ;}c a tc h ( IO E xception igno re d ){}

}p r i vate final File Writer out;

}

L I S T I N G 2 . . .

Initializing and using a L o g object in the S e r v i c e.public class Service{

public Service(){

t h i s ( nu l l ) ;}public Service(Log lo g ){

t h i s. log = lo g ;... // any other initialization

}public Result hand le ( Request re q u e s t ){

i f ( log != nu l l )lo g . w r i t e ( “ Request “ + request + “ re c e i ve d ” ) ;

. . .i f ( log != nu l l )

lo g . w r i t e ( “ Request “ + request + “ hand le d ” ) ;. . .

}... // any other me t hods and fie ld sp r i vate Log lo g ;

}

Page 3: Something for Nothing

7 8 Java™Report | D E C E M B E R 19 9 9 h t t p : / / w w w. j ava re po r t . co m

Patterns in Java / Kevlin He n n ey

not possible to set debugging breakpoints or print state-ments that show all uses of a null c a s e .

So l u t i o n

P rovide a class that conforms to the interface re q u i red ofthe object re f e rence, implementing all of its methods to donothing or provide re t u rn default values. Use an instanceof this class when the object re f e rence would otherw i s ehave been nu l l. Figure 2 shows the essential config u r a t i o nas a class model.

Re s o l u t i o n

R e t u rning to the server example, we can introduce a N u l lO b je c t class, N u l l L o g, into the L o g h i e r a rc h y. Such a com-f o rtably nu l l class (see Listing 3) does nothing and does notdemand a great deal of coding skill!

The tactical benefit of a Null Obje c t, once initialized, is insimplifying the use of this simple logging framework. It isnow guaranteed that the relationship to a L o g object ism a n d a t o ry, i.e., the multiplicity from the S e r v i c e to the L o gis 1 rather than 0 . . 1. This means that the conditional codecan be eliminated, which makes the use of logging moreu n i f o rm and the presence or absence of enabled loggingt r a n s p a rent (see Listing 4).

Just as the essential stru c t u re for Null Obje c t may be dia-grammed in UML, its use in our code can be documented ina collaboration diagram. Figure 3 shows the classes and in-t e rfaces in our design and how they correspond, in terms ofroles, to the elements outlined in Figure 2.

Co n s e q u e n ce s

I n t roducing a Null Obje c t s i m p l i fies the client’s code by elim-inating superfluous and repeated conditionals that are notp a rt of a method’s core logic. The relationship moves fro mbeing optional to mandatory, and selection and variation

a re expressed through polymorphism and inheritancerather than procedural condition testing, making the use ofthe relationship more uniform. However, to pre s e rve thisinvariant, care must be taken to correctly initialize the re f-e rence and to ensure either that it is not changed, i.e., de-c l a re it fin a l, or that any change pre s e rves the re q u i re m e n tthat nu l l is replaced by Null Obje c t.

A Null Obje c t is encapsulated and cohesive: It does onething—nothing—and it does it well. This eliminates du-plicate coding, makes the (absence of) behavior easier toreuse, and provides a suitable venue for debug bre a k p o i n t sor print statements.

The absence of side effects in any method call to Null Ob-je c t means that instances are immutable and there f o re share-able and thread-safe, so that a Null Obje c t is a F l y we i g ht.1 ANull Obje c t is typically stateless, which means that all suchinstances will have the same behavior, and only one in-stance is re q u i red, suggesting that the object might be im-plemented as a S i ng le to n.1

Using a Null Obje c t in a relationship means that methodcalls will always be executed and arguments will always beevaluated. For the common case this is not a problem, butt h e re will always be an overhead for method calls whosea rgument evaluation is complex and expensive.

The use of Null Obje c t does not scale for distribution ifpassed by re f e rence, e.g., if it implements j a va . r m i . Re mo t e.E v e ry method call would incur the overhead of a re m o t ecall and introduce a potential point of failure, both of whichwould swamp and undermine the basic do nothing b e h a v-i o r. There f o re, if used in a distributed context, either nu l lshould be passed in pre f e rence to a Null Obje c t re f e rence orpass by value should be used if RMI is the distributionmechanism, i.e., transparent replication rather than trans-p a rent sharing becomes the priority. Because a Null Obje c tclass is small and simple, class loading is cheap and theonly change the programmer needs to make is to implementj a va . i o . S e r i a l i z a b le:

public class NullLog imple me nts Log, Serializable{

. . .}

Figure 3. The relationship be tween the serv i ce stru ct u re and the

roles in Null Object.

L I S T I N G 3 . . .

A N u l l L o g class providing do nothing b e h a v i o r.public class NullLog imple me nts Log{

public void write(String me s s age To L o g ){}

}

L I S T I N G 4 . . .

I n t roducing a N u l l L o g object into S e r v i c e.public class Service{

public Service(){

t h i s ( new NullLog());}. . .public Result hand le ( Request re q u e s t ){

lo g . w r i t e ( “ Request “ + request + “ re c e i ve d ” ) ;. . .lo g . w r i t e ( “ Request “ + request + “ hand le d ” ) ;. . .

}. . .p r i vate Log lo g ;

}

Page 4: Something for Nothing

Users of a hierarchy that includes a Null Obje c t will have moreclasses to take on board. The benefit is that with the exceptionof the point of creation, explicit knowledge of the new Null Ob-je c t class is not needed. Where there are many ways of d o i n gn o t h i n g, more Null Obje c t classes can be introduced. Va r i a t i o n son this theme include the use of an E xceptional Va l u e o b j e c t ,4

that rather than doing nothing, raises an exception for anymethod call or re t u rns E xceptional Va l u e objects as a re s u l t .

Taking an existing piece of code that does not use N u l lO b je c t and modifying it to do so may accidentally intro d u c ebugs. By carefully following the I nt ro d uce Null Obje c t re f a c-t o r i n g ,3 developers can have greater confidence makingsuch changes and avoiding pitfalls. When introducing a N u l lO b je c t class into an existing system, a suitable base interf a c emay not exist for the Null Obje c t class to implement. Eitherthat part of the system should be re f a c t o red to introduce oneor the Null Obje c t class must subclass the concrete class re-f e rred to by the re f e rence. The latter approach is a little un-desirable, as it implies that if there is any re p resentation inthe superclass it will be ignored. Such inheritance with can-cellation can lead to code that is harder to understand be-cause the subclass does not truly conform to the superc l a s s ,i.e., fails the is a or is a kind of litmus test for good use ofinheritance and includes redundant implementation thatcannot be uninherited. This approach also cannot be usedif the concrete class or any of its methods are declared fin a l.Note that a Null Obje c t class should not be used as a super-class except for other Null Obje c t c l a s s e s .

Pat te rn An ato myAny pattern is a narrative, essentially a story that unfoldswith dramatis personae, the to and fro movements of theplot elements, and a happy ending. A pattern is also a guideto construction, telling you when and how to build some-thing, passing on strategies, tactics, and observations fro mexperience. This is as true of software development pattern sas it is of building arc h i t e c t u re where patterns originated.5 , 6

Wh at You Should Find in a Pat te rnA pattern identifies a general approach to solving a pro b-lem, typically capturing a solution practice or collaboratives t ru c t u re. It identifies the general context of the pro b l e m ,the nature of the problem, the interplay of conflicting forc e sthat create the problem and binds its solution, the config-uration that resolves the problem, and the resulting contextof applying such a solution. In short: where, what, and howthe problem arises and is solved. Rather than living in theabstract, a pattern needs to be illustrated by something con-c rete, such as an example with code and diagrams.

P a t t e rns also tend not to live in isolation, and so a patterntypically contains re f e rences to other patterns that completeit. Such links increase the value and usability of a pattern be-cause they lay out some of the possible tracks your devel-opment can follow, for instance, the use of S i ng le to n with N u l lO b je c t. A pattern can also contain discussion of patterns thata re in some way similar to it or coincidentally related. How-e v e r, there is a danger that a pattern written in this style can

end up as a comparative pattern study or treatise, i.e., an aca-demic exercise rather than a piece of knowledge focused onpractice and problem solving.

P a t t e rns are considered to be about real things, so they areempirical rather than theoretical, and they are about re p e a t-ed rather than one-time solutions to problems. There is of-ten an onus on pattern authors to demonstrate this, such asby including a listing of some known uses of a pattern thatcite, for example, specific frameworks and libraries. We couldcite a number of framework examples for Null Obje c t, as it hasbeen discovered time and time again, but as a general pat-t e rn—as opposed to a specifically OO design pattern—it isp e rhaps more common than people realize: Consider the ro l eof the nu l l file device on many operating systems (/ dev / nu l lon Unix and N U L on Microsoft systems), no-op machine in-s t ructions, terminators on Ethernet cables, etc.

F i n a l l y, one of the most significant parts of a pattern isits name. The name allows us to discuss solutions easilyand at an appropriate level of abstraction. This suggests thata pattern ’s name should be drawn from the solution andshould be an evocative noun phrase7 that can be used in or-d i n a ry conversation, i.e., to support usage such as “if weuse a Null Obje c t...” as opposed to “if we apply the Null Ob-je c t p a t t e rn....” It is not uncommon for a pattern to have dif-f e rent names, either because diff e rent people havedocumented it at diff e rent times or because other nameshave diff e rent connotations. For instance, Null Obje c t h a salso been known as Ac t i ve Nothing and Smart NIL.

Pat te rn Fo rmT h e re are many forms for documenting pattern s ,8 r a n g i n gf rom the highly stru c t u red heading-based template form1

to more narrative, literary form s .5 The essential elements ofa pattern can be found in each form, and the choice of formtends to be a matter of personal pre f e rence as much as any-thing else.

A quick scan through pattern sources such as the Pat-t e rns Home Page9 or the P a t t e rn Languages of Program De -s i g n (PLoPD) books1 0 – 1 2 should be enough to convinceanyone of the diversity of pattern form. Null Obje c t has beendocumented in a variety of forms, which vary widely ins t ru c t u re and length: from the stru c t u red GoF-like form ,2 t othe shorter stru c t u red form used in this column, to the briefthumbnail-like production rule form .1 3

Co n c l u s i o nP a t t e rns can quite literally help create a vocabulary for de-velopment, accelerating communication, and the under-standing of ideas. They are pieces of literature that capturesuccessful stru c t u res and practices, offering developersreusable ideas rather than reusable code. A pattern ’s formis essentially a vehicle for conveying these.

As a specific example, Null Obje c t illustrates how substi-tuting a stateless object that does nothing offers a surpris-ingly powerful solution. It can be used tactically in manysystems to encapsulate and simplify control flo w — s o m e-thing for nothing. ■

8 0 Java™Report | D E C E M B E R 19 9 9 ht t p : / / w w w. j ava re po r t . co m

Patterns in Java / Kevlin He n n ey