1 / 4

Something for Nothing

Something for Nothing (Java Report 4(12), December 1999).

Kevlin
Download Presentation

Something for Nothing

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/khenney@ qatraining.com Something for nothing I 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 — e s p e- cially software built with components and ru n n i n g with objects—in very physical term s . If you want something really insubstantial you have to scratch the software development surface a lit- tle deeper: What are the successful design stru c- t u res, 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- p ress 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- t e rns seem to offer a discipline based on applied thought; 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 n P a t t e rn s c a t a l o g ,1containing 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 wealth of literature produced by the patterns community that focuses on patterns at all levels and across many dif- f e rent domains. This column is going to look beyond the relatively familiar GoF terr i t o ry, 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 diff e rent perspective. So, without further ado, let’s look at a useful tacti- cal pattern: Null Obje c t2is perhaps best described as T HAS BEEN said that in the days of punched c a rds, 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 feature s 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.” 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 or use a default value, how can the presence of a null be t reated transpare n t l y ? Ex a m p l e Consider providing a logging facility for some kind of simple server service that can be used to re c o rd the outcome of operations and any significant events in the operation of the service. One can imagine many d 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 message on a remote serv e r. However, a server is not re q u i re d 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 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 we must always ensure that we first check it is not null, b e- cause we know it is optional, i.e., logging need not be enabled 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 possible to forget to write such a null guard, and repeated checks for null re f e rences can clutter and obfuscate your code. The user is re q u i red to detect the condition and Kevlin He n n ey is a Principal Te c h n o l ogist with QA Training in the UK. *Complete references are available at Java Report Online— w w w. j a v a re p o rt . c o m 7 4 Java ™Report | DECEMBER 1999 ht t p : / / w w w. j ava re po rt. co m

  2. 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 . take appropriate inaction, even though the condition is not in any way exceptional, i.e., having a nu l l log is a norm a l and expected state of the relationship. The condition makes the 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 the s u rrounding code; if they are not, then the resulting code is less rather than more direct, and the core algorithm be- comes obscure d . W h e re conditional code does not serve the main purpose of a method, it tends to get in the way of the method’s own logic, 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 of blocks 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 may choose alternative actions to suit the context. However, if the 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, it leads to duplication of the condition and its action. Dupli- cate code is considered to have a “bad smell,”3and works against 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 cost except a condition test and branch. On the other hand, the use of an explicit test means that there will always be a test. Because this test is repeated in separate pieces of code, it is LISTING 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 ) ; } } LISTING 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 s p r i vate Log lo 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; } 7 6 Java ™Report | DECEMBER 1999 ht t p : / / w w w. j ava re po rt. co m

  3. Patterns in Java/Kevlin He n n ey a re expressed through polymorphism and inheritance rather than procedural condition testing, making the use of the relationship more uniform. However, to pre s e rve this invariant, 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 t that nu l l is replaced by Null Obje c t. A Null Obje c t is encapsulated and cohesive: It does one thing—nothing—and it does it well. This eliminates du- plicate coding, makes the (absence of) behavior easier to reuse, and provides a suitable venue for debug bre a k p o i n t s or print statements. The absence of side effects in any method call to Null Ob- je c tmeans 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.1A Null Obje c t is typically stateless, which means that all such instances 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 method calls will always be executed and arguments will always be evaluated. For the common case this is not a problem, but t h e re will always be an overhead for method calls whose a rgument evaluation is complex and expensive. The use of Null Obje c t does not scale for distribution if passed 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 e call and introduce a potential point of failure, both of which would 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 l should be passed in pre f e rence to a Null Obje c t re f e rence or pass by value should be used if RMI is the distribution mechanism, i.e., transparent replication rather than trans- p a rent sharing becomes the priority. Because a Null Obje c t class is small and simple, class loading is cheap and the only change the programmer needs to make is to implement j a va . i o . S e r i a l i z a b le: public class NullLog imple me nts Log, Serializable { . . . } LISTING 4... Figure 3. The relationship be tween the serv i ce stru ct u re and the roles in Null Object. 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 of the object re f e rence, implementing all of its methods to do nothing or provide re t u rn default values. Use an instance of this class when the object re f e rence would otherw i s e have been nu l l. Figure 2 shows the essential config u r a t i o n as 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 l O 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 not demand a great deal of coding skill! The tactical benefit of a Null Obje c t, once initialized, is in simplifying the use of this simple logging framework. It is now guaranteed that the relationship to a L o g object is m a n d a t o ry, i.e., the multiplicity from the S e r v i c e to the L o g is 1 rather than 0 . . 1. This means that the conditional code can be eliminated, which makes the use of logging more u n i f o rm and the presence or absence of enabled logging t 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 in a collaboration diagram. Figure 3 shows the classes and in- t e rfaces in our design and how they correspond, in terms of roles, to the elements outlined in Figure 2. 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 ; } 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 not p a rt of a method’s core logic. The relationship moves fro m being optional to mandatory, and selection and variation LISTING 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 ) { } } 7 8 Java ™Report | DECEMBER 1999 ht t p : / / w w w. j ava re po rt. co m

  4. Patterns in Java/Kevlin He n n ey Users of a hierarchy that includes a Null Obje c twill have more classes to take on board. The benefit is that with the exception of 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 g n o t h i n g, more Null Obje c tclasses can be introduced. Va r i a t i o n s on 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 any method 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 l O b je c tand modifying it to do so may accidentally intro d u c e bugs. By carefully following the I nt ro d uce Null Obje c t re f a c- t o r i n g ,3developers can have greater confidence making such changes and avoiding pitfalls. When introducing a N u l l O b je c t class into an existing system, a suitable base interf a c e may not exist for the Null Obje c t class to implement. Either that part of the system should be re f a c t o red to introduce one or 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 in the 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 of inheritance and includes redundant implementation that cannot be uninherited. This approach also cannot be used if 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 . end up as a comparative pattern study or treatise, i.e., an aca- demic exercise rather than a piece of knowledge focused on practice and problem solving. P a t t e rns are considered to be about real things, so they are empirical 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 as by including a listing of some known uses of a pattern that cite, for example, specific frameworks and libraries. We could cite a number of framework examples for Null Obje c t, as it has been discovered time and time again, but as a general pat- t e rn—as opposed to a specifically OO design pattern—it is p e rhaps more common than people realize: Consider the ro l e of the nu l l file device on many operating systems (/ dev / nu l l on 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 is its name. The name allows us to discuss solutions easily and at an appropriate level of abstraction. This suggests that a pattern ’s name should be drawn from the solution and should be an evocative noun phrase7that can be used in or- d i n a ry conversation, i.e., to support usage such as “if we use 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 have documented it at diff e rent times or because other names have diff e rent connotations. For instance, Null Obje c t h a s also been known as Ac t i ve Nothing and Smart NIL. Pat te rn An ato my Any pattern is a narrative, essentially a story that unfolds with dramatis personae, the to and fro movements of the plot elements, and a happy ending. A pattern is also a guide to construction, telling you when and how to build some- thing, passing on strategies, tactics, and observations fro m experience. This is as true of software development pattern s as it is of building arc h i t e c t u re where patterns originated.5 , 6 Pat te rn Fo rm T h e re are many forms for documenting pattern s ,8r a n g i n g f rom the highly stru c t u red heading-based template form1 to more narrative, literary form s .5The essential elements of a pattern can be found in each form, and the choice of form tends 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 Page9or the P a t t e rn Languages of Program De - s i g n (PLoPD) books1 0 – 1 2should be enough to convince anyone of the diversity of pattern form. Null Obje c thas been documented in a variety of forms, which vary widely in s t ru c t u re and length: from the stru c t u red GoF-like form ,2t o the shorter stru c t u red form used in this column, to the brief thumbnail-like production rule form .1 3 Wh at You Should Find in a Pat te rn A pattern identifies a general approach to solving a pro b- lem, typically capturing a solution practice or collaborative s 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 s that create the problem and binds its solution, the config- uration that resolves the problem, and the resulting context of applying such a solution. In short: where, what, and how the problem arises and is solved. Rather than living in the abstract, 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 pattern typically contains re f e rences to other patterns that complete it. 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 nwith N u l l O b je c t. A pattern can also contain discussion of patterns that a 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 Co n c l u s i o n P 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 capture successful stru c t u res and practices, offering developers reusable ideas rather than reusable code. A pattern ’s form is essentially a vehicle for conveying these. As a specific example, Null Obje c tillustrates how substi- tuting a stateless object that does nothing offers a surpris- ingly powerful solution. It can be used tactically in many systems to encapsulate and simplify control flo w — s o m e- thing for nothing. I 8 0 Java ™Report | DECEMBER 1999 ht t p : / / w w w. j ava re po rt. co m

More Related