Just as in any system, when you start coding some structure, you always try to make it as generic as possible to make it easy to later reuse those parts. There is normal complexity when you build your code and as you go, complexity adds up. However, one of the main problem of this anti-pattern is when it’s done consciously.
Let’s give a quick example here. You start building collections and one of your collection need a special feature. The problem is when you extract this functionality and try to make it as generic as possible so that anyone could reuse your class. That is the problem of the unnecessary complexity. You see, as you are making stuff generic for a single class, you are adding structures inside your code. Some of those structure might be proof tested for this specific class but might fail on other classes. There is also the problem that maybe no other class will ever require this functionality.
How do I solve this anti-pattern?
By following YAGNI and Lean Software Development, you delay code and unnecessary complexity until you actually require the complexity. If you have 2 classes that require the same functionality, it is now time to extract this functionality inside a different class and make those 2 classes inherit from it (or any other patterns that are required).
And here, I’m not just talking about inheritance. I’m also talking about unnecessary design patterns. If you built a pipeline component to calculate discount but you only have one discount at the moment, it might be actually relevant to implement it anyway since you are sure you might require it later. However, the client is the one that is supposed to drive the requirements and if you don’t require the pipeline immediately… well… don’t built it!
It doesn’t mean to leave your code in a fixed state. It just means to keep your code clean to ease the implementation of the pipeline.
The best line of codes are those we don’t need to write.
I first started to encounter the "gas factory" anti-pattern about 20 years ago (though I have never heard it called that until now). Since then I have grown to the realization that it is the most serious and insidious problem that we currently have in software development. I believe it is due to the fact that about 20 years ago "code reuse" was being pushed as the software development "silver bullet" and a generation of designers got into the habit of trying to make their designs more general, at the cost of complexity.
ReplyDeleteThe problem is that the "best" designers are often the worst offenders. Just look at the horrendous Windows API (.Net is much better in this regard). It is motivated by the best of intentions and on the surface often seems like the right thing to do. I have identified two thought processes that I have been guilty of in the past that cause this problem:
1. An ambition to build some general purpose library that just happens to solve the problems at hand as a subset of a much more general set of problems. The cost is a library that has many disadvantages (longer to develop, harder to verify etc) but the main cost is in the increased complexity of using it.
The solution is to focus on the problem at hand and not on any more general problem, or possible future changes. I believe this behavior is the motivation in Extreme Programming for the rule that you should never code for the future, only the immediate requirements.
2. When coding, a temptation to add some unneeded facility now, for a perceived negligible cost, which would be hard to add later, and is surely going to be needed. This has some merit, however the key here is the phrase "hard to add later".
It has long been recognized that software must be designed and coded for maintainability. One of the aims of many modern "agile" methods is to be able to completely refactor code with impunity. There are many ways to make code maintainable but by far the most important is having a comprehensive set of automated unit tests. That way, you can make any desired changes and be sure that nothing gets broken.
Making the code maintainable will allow not only anticipated changes to be added later (if they were really required) but also (the far more common) unanticipated changes.
So I agree with you entirely about the problem of unnecessary complexity. One place I will disagree is that deriving new classes is rarely a good solution. In fact much of the unnecessary complexity of "object oriented" designs is due to the gross overuse of inheritance.
Andrew Phillips.
http://www.hexedit.com