Discussion on OpenClosedPrinciple
If your current story card doesn't have a direct need for the DependencyInversionPrinciple
, then as I understand it the XP rules are saying don't put it in. Put it in when you need it, not before. After all, putting it in is a small change, at least in Smalltalk.
I think there is some language specific stuff going on here. I've written more about it on the DependencyInversionPrinciple
It seems to me that the OpenClosedPrinciple
is not an aim of XP, and should not be an aim. It is an invitation to premature generalisation. Fear of introducing bugs is due to not having good UnitTest
s; get over it. If the code wants to be stable, stability will spontaneously emerge from the other XP practices. -- DaveHarris
Exactly. We might better talk about an OpenOpenPrinciple
. The DependencyInversionPrinciple is
a language issue. In C++, where cost of change is a problem if left untended, you can adopt something like the ExtremeFormsForCppCode
where the DependencyInversionPrinciple
obeyed in a silent mechanical way. In Java, it may not be a big deal at all.
There is a difference between PrematureAbstraction
and creating code you know to be flexible. Given two alternatives, one inflexible and the other flexible, the OCP directs us to choose the more flexible. Two XP mantras seem to contradict this. They are 1) YouArentGonnaNeedIt
, and 2) DoTheSimplestThingThatCouldPossiblyWork
. One must make an engineering decision between these.
The first tells us we should not add code in anticipation of future features. But that's not to say we can't put in place the flexibility that will make it easier to add that code later. Inflexibility, after all, is a bad code smell. The second tells us that we should keep things simple. But a flexible design *is* simpler to maintain than an inflexible design.
OCP is not a law that we must follow blindly. Neither are any of the other XP mantras. Engineering is about making tradeoffs. RobertCecilMartin
The disagreement seems to center on "Inflexibility, after all, is a bad code smell." In XP, any unused
flexibility (or any unused code) is a worse smell. Many XP users would agree that flexibility is *often* simpler. (For instance, using a foreach loop is both simpler and more flexible than a loop indexing from 1 to MAX_ITEM.) XP does not oppose flexibility, but says simplicity should be measured regardless of future flexibility.
In a "pure" XP environment, one does not add code in anticipation of the future (for any future longer than the development time of the current story--preferably not beyond the current coding session). In this environment, one writes similar code twice and tests it (as always) until it works. At some time a developer will notice that the two instances violate OnceAndOnlyOnce
, and RefactorMercilessly
until the duplicate smell is gone. (In less-"pure" XP, one might prematurely/speculatively refactor before writing the second version.) OnceAndOnlyOnce
will introduce the abstractions that are actually used by the system, rather than anticipated ideas from an earlier coding session.
seems most useful when applied to "library" code like a GUI framework or an operating system interface. In these cases, the library is very difficult to change effectively, so it is important to provide flexibility and extensibility as early as possible. (Even OpenSource
doesn't get rid of versioning/patching problems: many projects BurnTheDiskpacks
.) When an XP project has stories which *require* a library (which should not be changed frequently), OCP may be a useful tool.
As in other tradeoffs, the XP PlanningGame
can be used to expose the costs and benefits of flexibility. If flexibility saves work for the XP team, and
does not impact other stories like speed or system resource usage, it can be an internal detail. If the flexibility has any non-beneficial impact, the customer can decide the issue. (Some customers would rather be 5% faster now or deliver 3 weeks sooner than be more flexible. Others may be willing to make different tradeoffs, such as being more flexible, up to 10% faster, but 9 weeks later (or fewer stories) with 15% more memory usage.) The ability for the *customer* to make these tradeoffs is an attractive feature of XP.--CliffordAdams
Let's consider two extremes. On the one hand, we all know that if you hack without refactoring dependencies between pieces of code will slow you down more and more. Every new piece of functionality requires changes to existing code. These changes are scattered all over the code. The larger your program gets, the more code needs to be changed, so productivity eventually drops to nearly zero.
One the other hand, if you somehow succeed it getting your design open for extension and closed for modification the first time, you won't have to spend time on changing old code and your productivity remains constant.
Taken literally, this is impossible. You can always find new functionality that will require changes to existing code or duplication of it. Robert Martin points out you mustn't try to close a piece of code against all changes, but that you have to aim for strategic closure. Even that is hard and takes effort. And some of it will be wasted because the envisioned change never happens.
But the time spent on changing existing code doesn't have to be zero for productivity to remain constant. It is enough if the fraction of development time spent on changes to existing code in every iteration remains bounded over time. Since the code grows this can only happen if existing code stabilizes. I think this is precisely what happens if you do RefactorMercilessly
If all this is true, you could say that the OCP is something that happens naturally in XP, it only takes a bit of time.
In my opinion, XP's "conflict" with the OpenClosedPrinciple
is more theoretical than practical. I find that Test Driven Development pushes me to create lots of abstractions anyway, before the features push me to do it. For example, if I have a class that uses a database and I want to test it, I will anyway need to create an IDataSource interface with a mock data source so that I can test the class. Then if the users come along and
say they want me to use the data they saw on the internet, there's a good chance I can already handle that feature with my IDataSource
abstraction (i.e. no conflict with open-closed).
However, there are still some theoretical points that haven't been covered. Christian Wege's 2004 dissertation makes the point that there are different ways of conflicting with the OpenClosedPrinciple
, and some are worse than others. (Source: Appendix of http://w210.ub.uni-tuebingen.de/dbt/volltexte/2004/1392/pdf/thesis.pdf
A closure violation
is the truly bad conflict. You go in and change existing classes to introduce your new feature without introducing new
A closure extension
is the better conflict. When your new feature doesn't fit into the existing closure (interface and abtraction), you extend the closure by adding new interfaces and abstract classes. That way, next time that you add a similar feature, you don't have a conflict with the OpenClosedPrinciple
. This is what agile design recommends.
The alternative is try to plan ahead, to develop interfaces and abstractions that you think you will need. XP says the danger is that you guess wrong and get stuck with NeedlessComplexity?
that makes your code hard to work with. And you might still encounter new features that violate your existing abstractions, general though they are. You might get a conflict with the OpenClosedPrinciple
Caveat: If certain decisions are hard to change (i.e. refactor), I'm more likely to create abstractions to protect me ahead of time. For
example, if I'm about to release an interface to the public, I'm more likely to try to ensure it's as general as possible.