1

I am teaching myself the principles involved in S.O.L.I.D. Object-Oriented Programming and am having trouble understanding all the details in the letter D (Dependency-Inversion Principle.)

I am reading its entry in Wikipedia (http://en.wikipedia.org/wiki/Dependency_inversion_principle) and am not understanding all that is put in the diagram:

http://en.wikipedia.org/wiki/Dependency_inversion_principle#/media/File:DIPLayersPattern_v2.png)

I notice there are two different types of arrows - one is dashed and one is solid.

Based off my current understanding, the dashed line represents the equivalent of the "implements" keyword in Java, and the solid line represents the keyword "extends."

Is this the correct interpretation?

Dave Schweisguth
  • 36,475
  • 10
  • 98
  • 121

2 Answers2

3

This might clear up a few things:

Explanation of the UML arrows

Note the images are for Visual Studio, but I would think most of the information would be equivalent with most UML documentation.

The dashed line represents the equivalent of "implements"

The naming of the entities in the UML is throwing me a bit too... it appears that Policy is dependent on an interface that describes the contract to the lower level module of Mechanism

However on the second (the solid line) that is supposed to be representative of inheritance (at least I believe). Mechanism implements the interface (abstraction) Policy rather than prior to the application of DIP when Policy referenced the concrete Mechanism.

What it's trying to convey mostly is classes shouldn't depend on other classes, they can however depend on abstractions (interfaces) rather than concretes.

The easiest example of this: Original Foo/Logger with dependencies on lower level modules.

// "Low level Module" Mechanism equivilant
public class Logger {
    public void logInformation(String logInfo) {
        System.out.println(logInfo);
    }
}

// "High level module" Policy equivalent.
public class Foo {
    // direct dependency of a low level module.
    private Logger logger = new Logger();

    public void doStuff() {
        logger.logInformation("Something important.");
    }
}

In the above, Foo is dependent on the concrete implementation of Logger. This can be refactored as such (note there are several methods to do this, this is just one)

public interface ILogger {
    void logInformation(String logInfo);
}

public class Logger implements ILogger {
    @Override
    public void logInformation(string logInfo) {
        System.out.println(logInfo);
    }
}

public class Foo {
    private ILogger logger;
    public void setLoggerImpl(ILogger loggerImpl) {
        this.logger = loggerImpl;
    }

    public void doStuff() {
        logger.logInformation("Something important.");
    }
}

In this refactor, Foo is no longer dependent on Logger, but now utilizes the interface ILogger - meaning that you can switch in and out implementations of ILogger at runtime, object instantiation, etc.

You could consume Foo as such:

Foo foo = new Foo();
ILogger logger = new Logger();
foo.setLoggerImpl(logger);
foo.doStuff();

This would of course, print to console "Something important". Now what happens if you don't want to log to console, but to a database?

public class LoggerToDb implements ILogger {
    @Override
    public void logInformation(string logInfo) {
        DbContext databaseContext = new DbContext();
        databaseContext.insertLog(logInfo);
    }
}

and could now be consumed as:

Foo foo = new Foo();
ILogger logger = new LoggerToDb();
foo.setLoggerImpl(logger);
foo.doStuff();

Note how nothing had to change in your Foo implementation, because Foo was not dependent on Logger, but instead ILogger - with this approach we can provide a new concretion to the abstraction, and swap it into Foo without ever even touching Foo! Pretty neato IMO.

Note in the above examples I'm building the object and providing an implementation, this could also be done with an IOC framework like Java's Spring.

Community
  • 1
  • 1
Kritner
  • 13,557
  • 10
  • 46
  • 72
  • @LuiggiMendoza thanks, would have never gotten that syntax correct :) – Kritner Apr 21 '15 at 18:05
  • 1
    I think you are mixing up dependency injection and dependency inversion. DIP applies to inter-module dependencies and is an improvement over the standard layered architecture. The focus is relationships between modules. [More info here](https://books.google.ca/books?id=X7DpD5g3VP8C&pg=PA123&lpg=PA123&dq=improved+layered+architecture+dependency+inversion+principle&source=bl&ots=tRBNLGD9YS&sig=8Zrz_2ANj29vXYNnEAHssA95dGI&hl=fr&sa=X&ei=dD44VdfdGozToAThh4HwAQ&ved=0CCgQ6AEwAQ#v=onepage&q=improved%20layered%20architecture%20dependency%20inversion%20principle&f=false) – plalx Apr 23 '15 at 00:39
  • @plalx hmm... I was under the impression that DI was one method of accomplishing DIP - I did not specifically call it out in my example (as it was called out in the wiki article OP cited), but `ILogger` is "owned" by `Foo`, not by `Logger` or any other implementations of `ILogger`. From what I've read that does describe DIP... can you clarify what is missed? – Kritner Apr 23 '15 at 11:55
  • @Kriten, DI is a method of achieving Inversion of Control, which is not the same as Dependency Inversion. DI is an important component in order to achieve DIP, but DIP is a principle that simply states that: 1. High level modules do not depend on low level modules, but on abstractions. 2. abstractions should not depend on details. Details should depend on abstractions. – plalx Apr 23 '15 at 12:47
3

The dashed line means a source code dependency. The solid line with the empty triangle means a special type of dependency: class inheritance or interface implementation.

In this diagram Policy Service and Mechanism Service are abstractions, Policy Service being the higher level abstraction. Mechanism and Utility are the details.

Policy has an obvious runtime dependency on Mechanism and Utility (the flow of control starting from Policy will reach these details), and on a lower level Mechanism has a similar runtime dependency on Utility. However, at the source code level Policy only depends on Policy Service, and Mechanism also depends on Policy Service. This is the dependency inversion.

lbalazscs
  • 17,474
  • 7
  • 42
  • 50