lundi 29 juin 2015

Dependency Inversion Principle (SOLID) vs Encapsulation (Pillars of OOP)

I was recently having a debate about the Dependency Inversion Principle, Inversion of Control and Dependency Injection. In relation to this topic we were debating whether these principles violate one of the pillars of OOP, namely Encapsulation.

My understanding of these things is:

  • The Dependency Inversion Principle implies that objects should depend upon abstractions, not concretions - this is the fundamental principle upon which the Inversion of Control pattern and Dependency Injection are implemented.
  • Inversion of Control is a pattern implementation of the Dependency Inversion Principle, where abstract dependencies replace concrete dependencies, allowing concretions of the dependency to be specified outside of the object.
  • Dependency Injection is a design pattern that implements Inversion of Control and provides dependency resolution. Injection occurs when a dependency is passed to a dependent component. In essence, the Dependency Injection pattern provides a mechanism for coupling dependency abstractions with concrete implementations.
  • Encapsulation is the process whereby data and functionality that is required by a higher level object is insulated away and inaccessible, thus, the programmer is unaware of how an object is implemented.

The debate got to a sticking point with the following statement:

IoC isn't OOP because it breaks Encapsulation

Personally, I think that the Dependency Inversion Principle and the Inversion of Control pattern should be observed religiously by all OOP developers - and I live by the following quote:

If there is (potentially) more than one way to skin a cat, then do not behave like there is only one.

Example 1:

class Program {
    void Main() {
        SkinCatWithKnife skinner = new SkinCatWithKnife ();
        skinner.SkinTheCat();
    }
}

Here we see an example of encapsulation. The programmer only has to call Main() and the cat will be skinned, but what if he wanted to skin the cat with, say a set of razor sharp teeth?

Example 2:

class Program {
    ICatSkinner skinner;

    public Program(ICatSkinner skinner) {
        this.skinner = skinner;
    }

    void Main() {
        this.skinner.SkinTheCat();
    }
}

... new Program(new SkinCatWithTeeth());

Here we observe the Dependency Inversion Principle and Inversion of Control since an abstract (ICatSkinner) is provided in order to allow concrete dependencies to be passed in by the programmer. At last, there is more than one way to skin a cat!

The quarrel here is; does this break encapsulation? technically one could argue that .SkinTheCat(); is still encapsulated away within the Main() method call, so the programmer is unaware of the behavior of this method, so I do not think this breaks encapsulation.

Delving a little deeper, I think that IoC containers break OOP because they use reflection, but I am not convinced that IoC breaks OOP, nor am I convinced that IoC breaks encapsulation. In fact I'd go as far as to say that:

Encapsulation and Inversion of Control coincide with each other happily, allowing programmers to pass in only the concretions of a dependency, whilst hiding away the overall implementation via encapsulation.

Questions:

  • Is IoC a direct implementation of the Dependency Inversion Principle?
  • Does IoC always break encapsulation, and therefore OOP?
  • Should IoC be used sparingly, religiously or appropriately?
  • What is the difference between IoC and an IoC container?

Aucun commentaire:

Enregistrer un commentaire