In my last blog entry I discussed how Test Driven Development (TDD) lead me to a greater understanding of the Open-Closed Principle (OCP). The general feedback from the post was very positive. However, several readers asked a question that I would like to answer. They pointed out that although I was discussing OCP, my example really displayed the Dependency Inversion Principle (DIP). Was my blog post misleading?
Just to recap, here is the example I used in the previous post:
1: testDefault(self):2: member = Member("test member")
3: membership_list = MembershipList() 4: membership_list.add_member(member) 5: command = GetMembershipListCommand(membership_list) 6: result = command.execute()7: self.assertEqual(["test member"], result)
This is a simple test for my “GetMembershipCommand” function. When I realized that I didn’t like the coupling created between my test function and the other production classes in my project, I altered the test to this:
1: test Default(self): 2: command = GetMembershipListCommand(DummyList()) 3: result = command.execute()4: self.assertEqual(["test member"], result)
The dependency inversion principle states:
High level modules should not depend upon low level modules. Both should depend upon abstractions.
In my example, I clearly used the dependency inversion principle. My GetMembershipListCommand no longer depends on the lower level Member and MembershipList classes - it only depends on the interface. But does that mean I missed the point of OCP? I don’t think so.
OCP is about the flexibility of your classes, not a specific implementation pattern. Can you construct a class for which the implementation can stay constant, even though you can extend the behavior? What might these techniques be?
- Most examples I have seen focus on sub-classing the parent class and overriding methods
- I claim that OCP is achieved in my example above by injecting classes with behaviors to the parent class
- I have also built classes to which behaviors and extensions are passed directly as functions or command classes
No matter which of these techniques I use, I know one thing to be true: although the base class’ implementation has not changed, its behavior has been extended through these techniques.
In a recent podcast with Scott Hanselman, Robert Martin reviewed the SOLID principles. One point that Martin made is that the dependency inversion principle (DIP) is basically a restatement of OCP with "a 90 degree rotation". OCP states the goal of not having to modify a class to extend behavior. DIP provides an implementation guideline that we should depend on abstract classes as much as possible.
I clearly used dependency injection to modify the behavior of the classes being tested. TDD showed me where I wasn't using techniques like dependency injection to easily and thoroughly test my objects. My effort resulted in classes that satisfy the open-closed principle. It was very satisfying to see multiple principles converge on the single goal of clean code.
So what do you think? Is my definition of the open-closed principle on the mark or too liberal?







