online shoes store xkshoes,here check the latest yeezy shoes click here.

know more about 2020 nike and adidas soccer cleats news,check shopcleat and wpsoccer.

Two More Implementation Patterns

Kent Beck, Three Rivers Institute
October 2007

 

Nested Method Objects and Conditional Factory helped simplify code in JUnit and make it more flexible.

Introduction

It never fails. No sooner do I finish a book than I think of ideas that would have been perfect to include. This time the book is Implementation Patterns and the ideas are two patterns: one I knew but forgot to include and the other I just encountered for the first time but that neatly extends a pattern in the book.

First I’ll set the stage a little. David Saff and I were refining JUnit. The code we were working on runs a single test method on a single test class instance. Before JUnit 4, the logic was simple:

            protected void run(TestCase test) {

                        startTest(test);

                        try {

                                    test.runBare();

                        } catch (Throwable e) {

                                    addError(test, e);

                        }

                        endTest(test);

            }

In JUnit 4 running a test is no longer so simple. We included several commonly implemented extensions—ignored tests, expected exceptions, and timeouts. To support all this functionality, we structured the code as a series of nested procedures, pairs of which took care of one aspect of running a test (thanks to Structure 101 for the diagram):

     

This code worked in the sense that it passed all the tests, although some of the intervening layers were quite complicated. The camel’s back snapped when people (us included) began writing custom runners. Most people running a single test method want most or all of the default functionality with some additions or alterations. Figuring out which methods to override and when to invoke superclass methods was complicated, time consuming, and error prone. We were failing to provide ease of extension, one of our primary goals for JUnit 4.

Nested Method Object

The Method Object implementation pattern suggests turning a complex method into an object to enable further simplification. In its original form it doesn’t apply to the code above, since the complexity mostly comes from the relationship of the methods, not from the methods themselves. A twist on Method Object, however, seems to have provided us with clarity and flexibility. The key was to have a chain of method objects, roughly one for every two levels of the call graph above. Here is a picture of the default chain of instances:

By first creating the chain of method objects and then executing them, we give ourselves and potential extenders the chance to create different chains. By replacing, augmenting, or reordering the links in the chain, different test runners can achieve different test running behavior while easily picking up the necessary bits of the default behavior.

We have had some trouble picking names for these objects. They help run test methods, so TestMethodRunnerHelper makes some sense, but it’s a long and awkward name (see Simple Superclass in Implementation Patterns for more discussion of class naming). For the moment, we have followed the chain metaphor and called the base class Link. The problem is that the metaphor is so generic—“Link” gives readers no clue about the purpose of the chain, just that it is a chain. When we have more experience with the concept, I expect we’ll find a better name.

Another way to look at this design is as an example of Decorator, where the Links are decorators. If we had designed this functionality from scratch as a nested series of objects, we could have gotten to the design that way. However, nested procedures worked pretty well for years, so we had no motivation to objectify them. Even though the final design is the same, it still makes sense to me to have different patterns to describe the different paths to reach that design.

Recasting the method running logic as objects gave us a chance to simplify the flow of control and eliminate some nagging duplication. This is a common consequence of method objects but it was a bit surprising to see it at work in a chain of method objects. We also found other ways to simplify the code, which leads to…

Conditional Factory

Not every test method uses every feature represented by the nested methods objects. The original methods were sprinkled with conditionals to decide whether to apply a feature:

            if (fTestMethod.getTimeout() > 0) {

                        // process the timeout

            } else {

                        // just run the method

            }

Recasting the logic as objects let us use one of my favorite patterns, one that I just flat out forgot to include in the book. The idea is to move conditional code like that above into a constructor, producing different kinds of objects representing the different cases. Following this, the code in the objects can become simpler because it can assume that the condition holds (or doesn’t hold).

Java constructors only return instances of the concrete class on which they are invoked, so some for of indirection is needed to implement this idea in Java. In this case, we used a variant on the Internal Factory pattern, Conditional Factory. Rewriting the code above as a conditional factory yields:

            public Link timeout(Link next) {

                        return fTestMethod.getTimeout() > 0

                                    ? new Timeout(next)

                                    : next;

            }

 In other words, only add a Timeout to the chain if its work is necessary. This allows the logic in Timeout to be several lines shorter because it need only work in the case where there is a timeout attribute attached to a test method.

The above example is an unusual conditional factory in my experience. Usually a conditional factory will return one of two (or more) classes. This appears also in our refactored code:

            public Link handleExceptions(Link next) {

                        return fTestMethod.expectsException()

                                    ? new ExpectedException(next)

                                    : new NoExpectedException(next);

            }

Conditional factories are good for simplifying code where the two legs of the conditional have little in common. Otherwise it’s probably better to keep the conditional in the main flow of control and use the usual methods to share implementation.

 

Conclusion

One lesson of this episode for me is that there are always tricks to learn. Having just spent three years thinking about and writing about implementation patterns, I didn’t expect to find a new one. Silly me. I still don’t expect to see nested method objects every day, but I’m glad I know the pattern now because it did a lot to simplify a complicated situation.

Another lesson I learned is not to expect books to stay finished long, although I wish they lasted long enough to hit the shelves. Remember this as a reader—whatever you are reading is only a snapshot of an ongoing process of learning on the part of the author.

Finally, I learned to keep trying when refactoring. Several times during this refactoring episode either David or I were stuck. Sometimes taking a break was all that was necessary to shake the needed idea loose, sometimes it was talking with each other that did the trick, and sometimes it required passing off responsibility for the code. I wonder how many potentially valuable refactorings stall before they reach a payoff. Since we work on an open source project without deadlines or paying customers (although I do offer commercial licenses and maintenance contracts for JUnit), we can see our ideas through to completion. When coding commercially I sometimes stop refactoring to finish this week’s functionality. Being aware of the tradeoffs will help me make an informed decision about how far to push refactorings.

Acknowledgements

I’d like to thank David Saff for getting into this mess with me and being willing to work out of it.