I just read some stuff in Agile Java about factory methods and constructors. Specifically, we (my imaginary class and me, I suppose) made a constructor private and replaced it with a factory method. And… I am not entirely clear on why we did this.
I did read it. He said:
By doing so, you will have control over what happens when new instances of [Class] are created.
But I’m curious. What kind of control?
(I know I should read about design patterns. But I am not ready for that, and I’m curious now!)
The main thing to know about ‘factory’ is the idea you already have. In all the variations, the mechanism is “keep some caller Fred.function() from saying ‘new Barney(…)’ itself.”
Let’s start with an intermal factory method. This is just a method on Fred, call it createBarney(), that makes Barney’s for the rest of Fred’s functionality. There are several good reasons we might want to control how the Barney’s are made:
- ‘new Barney(…)’ takes 82 arguments, but Fred always only needs two variants based on the seventh argument. We write a createBarney() that takes one arg and uses ‘Fred-local’ constants for the other 81.
- ‘Barney has 82 constructors, but there are only five variants Fred ever needs, so we write five creates, createHappyBarney, createSadBarney, createDopeyBarney, and so on. Here we’re taking control over the space of names, so that our intent is as clear as possible.
- When Freds make a new Barney, he always does the same four calls right after the new. We write a createBarney() that does the new AND the four calls.
- Fred’s algorithm is identical to the algorithm its descendants use, except each descendant starts with a different Barney object. Pull the algo up to Fred, but use an overridable createBarney supplied by the children. (This is a Template Method where the createBarney() is the part Fred doesn’t know how to do.)
- Barney doesn’t work or play well with others for microtesting. We write createBarney() so we can override it while testing, and substitute a FakeBarney that makes the testing easy.
- All Barney’s are part of a memory pool, so we want our ‘createBarney’ to always return the oldest Barney object in our pool.
- Fred can come to needing a new Barney through a dozen different paths, but we want him to only ever get one Barney and re-use it, and we don’t want to make it in advance. This is a special case of the previous one, really.
That oughta be reason enough and complication enough to keep you entertained for a while. Notice that only a couple of the above reasons are part of a good TDD approach. The rest are mostly about living with at least one poorly designed class.
I will go on and say that there are at least four variations on the factory theme:
- an internal factory method, as above, be it static or member.
- an external factory method, most commonly a static on Barney, so that any old outsider can use it to create a Barney. (This is usual for the Singleton case the folks mention below. Do not use singletons. At all.)
- a Factory class, BarneyFactory, does nothing else on earth but make Barney’s. (This is what most people mean when they say Factory.)
- an AbstractFactory is the parent class of a whole hierarchy of factories. This is rare, but invaluable when Fred needs to both ‘new’ objects AND connect them to each other without knowing how the relationship is made.
That was fun.