Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

SOLID Principles by Examples: Dependency Inversion

DZone's Guide to

SOLID Principles by Examples: Dependency Inversion

In this post, we wrap up our analysis of the SOLID principles of programming with analysis and examples of Dependency Inversion.

· DevOps Zone
Free Resource

Learn more about how CareerBuilder was able to resolve customer issues 5x faster by using Scalyr, the fastest log management tool on the market. 

This is the last blog post about the SOLID principles in object-oriented programming, and we talk about the Dependency Inversion Principle (DIP).

The principle states:

  1. High-level modules should not depend on low-level modules. Both should depend on abstractions.

  2. Abstractions should not depend on details. Details should depend on abstractions.

This definition was written by Robert C. Martin in his "Agile Software Development, Principles, Patterns, and Practices" book.

But what does this really mean? How do we need to organize our code? Let's continue with some examples.

Bad Example

In a traditional application architecture, top-level components rely on the specific implementation of low-level components, for example:

 class Logger {
        private NtfsFileSystem _fileSystem = new NtfsFileSystem ();

        public void Log (string text) {
            var fileStream = _fileSystem.OpenFile ("log.txt");

            fileStream.Write (text);

            fileStream.Dispose ();
        }
    }

Everything looks good, right? We have our Logger class that's able to log a text message into a specific file of the file system. But we have some issues here. Our Logger class depends on the specific implementation of the NtfsFileSystem class. What if that class needs to change because of a new Ntfs version, or because of a bug that has been fixed? It's likely that our class will need to change, too! What if we need to log into another completely different type of file system? Or into a database? We realize that our code is tightly coupled and maintenance is difficult.

Let's apply the DIP to our architecture.

Good Example

We declare an abstraction (interface) like this:

public interface ILoggable
    {
        void Log(string textToLog);
    }

This interface states that ILoggable classes con "Log" a text message.

Our NtfsFileSystem class becomes:

 class NtfsFileSystem : ILoggable {
        public void Log (string textToLog) {
            //file handling, writing and disposing.
        }
    }

And our logger:

 class Logger2 {
        private ILoggable _logService;

        public Logger (ILoggable logService) {
            if (logService == null) throw new ArgumentNullException ();

            _logService = logService;
        }

        public void Log (string text) {
            _logService.Log (text);
        }
    }

What we've done here is to inject into the constructor the log sub-system we want to use. Our Logger2 class can work with every class that implements ILoggable. This is also very useful for unit testing purposes when we would like to remove external dependencies and test our code in isolation (but that's outside the scope of this post). Our code is now loosely-coupled because the Logger2 class doesn't depend on a specific implementation, but only on an interface.

Now we can write a program like this, for example:

class Program () {
        void Main () {

            var ntfsLogger = new Logger2 (new NtfsFileSystem ());

            var noSqlLogger = new Logger2 (new DbNoSql ());

            ntfsLogger.Log("some text");

            noSqlLogger.Log("other text");
        }
    }

TL;DR

In this post, we explored the Dependency Inversion Principle with some examples, turning some highly-coupled code into a better architecture without hard dependencies on a specific implementation. Our Logger class is more reusable and even testable!

The word "inversion" comes from the fact that both the high-level and the low-level depend on abstraction, and this is the opposite of the classical approach where the high-level classes depend on the low-level classes.

Find out more about how Scalyr built a proprietary database that does not use text indexing for their log management tool, allowing customers to search 1TB of data in under a second. 

Topics:
solid principles ,agile ,devops ,procedural programming

Published at DZone with permission of Michele Ferracin, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}