What is difference between the Open/Closed Principle and the Dependency Inversion Principle?

6.4k Views Asked by At

The DIP states:

  • High-level modules should not depend on low-level modules. Both should depend on abstractions.
  • Abstractions should not depend upon details. Details should depend upon abstractions.

And the OCP states:

Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.

I think if we satisfy the DIP, it will cover the OCP too, So, why we separate these two principles?

5

There are 5 best solutions below

0
On

The OCP makes a dependent class easy to consume. The OCP enables asynchronous consumption of an interface by decoupling old implementations from newer versions. It allows the things that depend upon it to continue to depend on it even in the face of change for other purposes. That way a class never has to care who's calling it.

The DIP does a couple of things. It makes depending on external classes easy. Dependency Injection enables the substitutions of dependencies by encouraging the separation of creation duties from consumption. Instead of creating the external dependency that is to be consumed, the pattern states that it should be provided externally. Ultimately, this encourages code that is idempotent (code that does not change external state). Idempotent code is good because it can be verified that it does only what is immediately visible. It doesn't have external side effects. It's very testable, understandable, and readable.

2
On

I think adhering to the DIP makes it easier to comply with the OCP. However, one does not guarantee the other.

For example, I can create a class that has a method that takes a parameter of base. If base is an abstract class then I'm adhering to the DIP as I have inverted the dependency to the caller. However, if the code in that method does something like:

if (base is derived)
    (derived)base.DoSomethingSpecificToDerived;
elsif (base is evenMoreDerived)
    (evenMoreDerived)base.DoSomethingSpecificToEvenMoreDerived;

Then it's not OCP compliant as I have to modify it every time I add a new derivative.

It's very contrived example, but you get my point.

1
On

The DIP tells you how to organize the dependencies. It doesn't tell you when you are done with a particular interface.

Roughly speaking, the message of OCP is to have complete but minimalistic interfaces. In other words, it tells you when you are done with an interface but it doesn't tell you how to achieve this.

In some sense, DIP and OCP are orthogonal.


So, why we separate these two principles?

As for design patterns and named principles, almost all of them have in common that:

  1. Find what varies and encapsulate (hide) it.

  2. Prefer aggregation over inheritance.

  3. Design to interfaces.

Even if the named patterns and principles partially overlap in some sense, they tell you something more specific (in a more specific situation) than the above three general principles.

0
On

Good answer by @CS. To summarize,

  • The DIP is an extension of the OCP, so
  • When we satisfy the DIP, we generally satisfy the OCP as well.
  • The reverse is not true, and we can conceive of OCP-compliant, DIP violations. Here is one more (Java) example.
public abstract class MyClass {
    DependencyOne d1;
    DependencyTwo d2;

    MyClass() {
        d1 = new DependencyOne();
        d2 = new DependencyTwo();
    }
}

The OCP is satisfied because we can extend the class. The DIP is violated because we directly instantiate dependencies.

Now the challenge is, can we think of a DIP-compliant, OCP violation. The best example I can come up with is an annotation. In Java we use the @Deprecated annotation to mark code which is open for modification, thereby violating the OCP. At the same time, this code may be perfectly DIP compliant in terms of its abstractions and dependencies. Certain libraries use an @Beta annotation to similar effect.

I cannot imagine an example that is DIP-compliant and yet closed to extension, beyond the nullary example of a class which has no dependencies, which is not very interesting. I would say the DIP implies openness to extension. However, there may be edge cases where the DIP does not imply closedness to modification.

0
On

Uncle Bob Martin, who popularized the Open-Closed Principle (OCP) and Dependency Inversion Principles (DIP) as two of the SOLID principles, states himself that DIP arises from an application of OCP and the Liskov Substitution Principle:

In this column, we discuss the structural implications of the OCP and the LSP. The structure that results from rigorous use of these principles can be generalized into a principle all by itself. I call it “The Dependency Inversion Principle” (DIP).

Robert C. Martin, Engineering Notebook, C++ Report, 1996.

So you're right in stating that every instance of DIP will be an instance of OCP, but OCP is much more general. Here's a use-case of OCP but not DIP I ran into recently. Many web frameworks have a notion of signals, where upon one action, a signal is fired. The object sending the signal is completely unaware of the listeners who are registered with the signal. Every time you want to add more listeners to the signal, you can do so without modifying the sender.

This is clearly exemplifying OCP ("closed to modification, open for extension"), but not DIP, as the sender is not depending on anything, so there's no sense in talking about whether it depends on something more abstract or less so.

More generally you can say the Observer Pattern (one of the GoF patterns) describes how to comply with OCP but not DIP. It'd be interesting to go through the GoF book and see which ones have to do with OCP and how many of those are not DIP-related.