Jean reviews code: The Factory Method Pattern
As is oft required, I took to Google one day to remind myself of some important programming thingies that were once in my brain and have since fallen out along the way.
Challenge: Add a bunch of new screens that are almost visually identical, with the same interface, and chunky amounts of instantiation.
“Yes!”, I thought, “A factory to churn these out will work very nicely.”
The factory creates the screen, and configures the variable bits. All I do is tell it what I’m trying to achieve, and it returns me a concrete object.
The Factory Method Pattern is powerful, useful and wonderfully extensible. It hides boring instantiation and configuration, making your code clearer; less boilerplate cruft hiding its true purpose.
If you’re doing it right.
In my travels across the internet to remind myself of the ins and outs, I came across the following (slightly edited) code from a tutorial website.
I just have a lot of feelings about this, and the many ways in which it violates Good Code.¹
Consider the ridiculously complex tall half-caff, soy latte that we will inevitably be required to add in the future. 🤔
The implementation of
getCoffee(String coffeeType) must be modified. Who knows what horrors await, what bugs will be introduced, what method signature changes may be required? These are the kinds of thoughts that keep me up at night.
Thereby, any additions (
getReallyFancyCoffee()) extend the class. The interface for the rest of the application does not change. The code to instantiate a latte is not going to be accidentally fat-fingered into oblivion. Everyone is much happier.
By passing in “ESPRESSO”, intimate knowledge of the inner workings of the method are exposed to the calling class, tightly coupling the calling class not only to the called class, but to its actual implementation.
Consider the internal update to remove “PUMPKIN_SPICE_SPECIAL”, as we reach the end of Autumn, and head into “PEPPERMINT_SNOW_SPECIAL” season.
Every single class, module, client that wants a special drink will need to update. Have you definitely updated them all? Have your customers definitely updated them all?
The solution? See above — extension, not modification, to hide these internal details.
If you absolutely have to pass in some data, do anything you can to not use a
String. Strings are inexpressive and unsafe; Compilers don’t deal with mistakes, leading to nasty bugs in technically correct, but functionally incorrect code.
The typo of
getCoffee(“TEA”) will return null 😣
The inexpressive nature of
String paired with unlabelled arguments leads to pernicious bugs like a
getCoffeeWithSyrup(“CARAMEL”, “FILTER”) call to
getCoffeeWithSyrup(String coffee, String syrup) 😖
And, lest we forget our security training,
getCoffee(“L33T_HAX0R_SCRIPT”) will bring down the entire database, and bankrupt the company 😫
We can catch these kinds of mistakes in testing, but we should harness every safety mechanism available.
Enumin languages where they are value types; cheap to create and destroy.
getCoffeeWithSyrup(Coffee coffee, Syrup syrup)are expressive, and type-safe.
- Use platform specific solutions, like Android
Enumis a memory-hogging hunk of junk.
Enumwhere you have no other option because, well, at least it’s better than
“Factory Method” Pattern
The final straw? This is not actually the Factory Method Pattern. 😱
Much smarter people than I have covered this in detail, but in brief —
- Define interfaces for getting a drinkable
2. Write their concrete implementations.
3. Select the correct factory keeping the other points from this blog in mind, call
getCoffee() and enjoy the sweet, sweet taste of success.
And coffee. ☕️
CoffeeShop implementations are acceptable; what works in a specific codebase will be impacted by more than just clean code considerations. It is a balance — maintainable, extensible, clear, concise, efficient, YAGNI, and actually delivering a product to users.
Design patterns are invaluable tools in this, providing well understood solutions to common problems.. if we use them correctly, and don’t abuse them.
If you are looking for good tutorials on design patterns, you can’t go wrong with SourceMaking, from which I did not get the original bad code.
- It is impossible to follow every single good code practice. Do not try. That way madness lies. There will always be compromise required, due to factors outwith your control. Some practices even contradict each other. All we can hope for, and strive for, is code that is as readable, maintainable and extensible as we can make it.
2. As above, impossible to be perfect every single time.