The value of factories

I will not reason and compare: my business is to create. - William Blake

At Geek Night Joe Walnes, Steve Freeman and myself tried to come up with an example to use in live demos of using test-first programming with mock objects to drive iterative, top-down design. Our example was a fruit shopping application where the user could ask a directory to find the shop with a fruit at the lowest price, the nearest shop with a fruit at an affordable price, and so on, and then buy the fruit from the shop that was found. Our demo would be to pair-program the search functionality in front of a live audience.

Our design evolved well. The ShopFinder would search a list of shops by calling through an interface, ShopList. The ShopFinder would search the ShopList using an Internal Iterator as familiar to users of Ruby, Smalltalk or functional languages: the ShopFinder would create a "collector" object and pass it to the forEach method of the ShopList, which would call back to the collector and allow it to ask each shop for an offer on the fruit the user wanted.

I was pretty happy with this design. It follows the "Tell, Don't Ask" principle, which makes testing easy with mock objects. However, it required us to introduce an advanced feature of the jMock API too early in the demo - specifically, how to mock the side effect of the forEach method of the ShopList. So we abandoned that approach and instead tried others that were not very good, eventually giving up due to post-workday tiredness and general brain fade.

Our mistake was to hide the creation of a collector object within the ShopFinder that used it. This meant that we couldn't intercept the creation of the collector and replace it with a mock. By replacing a real collector with a mock we could have tested that the ShopFinder passed it to the ShopList by setting expectations on the ShopList and returned data to the ShopFinder by stubbing the collector instead of making the mock ShopList call back to the collector it received.

How could we have replaced a real collector with a mock? By giving the ShopFinder a factory with which it could create collectors and mocking the factory to return the mock collector(s) we needed in our tests. Even ignoring the needs of our demo, this change would have made our tests more readable. The factory would also act as a "flex point" which, experience has taught me, will make it easier to evolve the code in the future.

But it is very common for one object to create another. So common that one cannot practically replace every object instantiation with a call to a factory. Which object instantiations should be replaced by factories?

My initial conclusion is that value objects can be instantiated directly without causing problems because a test doesn't care about the value of a reference to a value object: the identity of a value object is defined by its state, not its reference. Neither does a test need to mock a value object because it doesn't care how it changes over time; after all value objects should be immutable. Behavioural objects (a.k.a. reference objects) are different: a test case need to compare references to test that the object is passed around correctly, and a test needs to mock the object in order to test that it is modified correctly while being passed around. So direct instantiations of behavioural objects should be replaced by the use of factory.

I have no idea whether this is useful, general design rule. And I don't really care! If my code needs a factory, the TDD process with mock objects soon drives that factory into existence - ignoring the need for a factory makes testing too painful otherwise. If my code doesn't need a factory the issue doesn't come up. TDD seems like magic sometimes; it's rather scary.

Copyright © 2003 Nat Pryce. Posted 2003-11-24. Share it.

Comments powered by Disqus