Welcome to the last part of my SOLID OOP design pattern series. You may wish to subscribe to the blog to stay tuned in as I post new series..
SOLID is not a language, it’s not a framework, it’s a set of principles to help guide your team to design better applications. Using these principles will help ensure your code is adaptive to change. I’ll be using C# for examples, but the same principles apply to any OOP language: Python, Java, C++, etc.. Using SOLID design principles does require a fundamental understanding of classes, interfaces, and class inheritance.
The fifth letter in SOLID stands for DIP: Dependency Inversion Principle and this principle states:
A. High-level modules should not depend on low-level modules. Both should depend on abstractions.
B. Abstractions should not depend on details. Details should depend on abstractions
Before digging deeper, let’s define the term dependency. Most, if not all, applications have dependencies. These dependencies may be an operating system (Linux/Windows) or a framework (Django/.Net) Another concrete, or low-level, dependency often cited is an application’s database. Dependencies are anything your application depends on. Simple as that.
Sustainable Software Must Be Tolerant of Changes
Our applications should never depend on implementation details. Imagine sprinkling SQL statements in your code which directly connects and queries the company’s MSSQL database. If your company never switches databases, maybe you get away with not being an efficient developer for now, but trust me dependencies will come to bite you in the ass sooner or later. Just imagine if your boss comes to you a week after deployment and tells you that management decided to use MongoDB instead. “How long will the change take?”, he asks. You and your team should shudder just daydreaming about the nightmare of rewiring an entire application, especially when it’s so simple to avoid.
Abstracting Implementation Details
Code dependency is also known as coupling and DIP reduces coupling between different pieces of our code through the use of abstraction. Abstractions allows us to combine specific lists of things into ideas or concepts, an extremely powerful tool and one taken for granted everyday in our language.
If we didn’t use abstractions, every time you or I wanted to talk about a house, we’d have to say something equivalent to, “the thing with a roof, door, windows, plumbing, electricity.” Actually, even these are concepts we’d have to break down into more detail until we end up talking about specific material properties. Thankfully English provides shortcuts for us. Just imagine how inefficient a language would be if we had to explain details each and every time we spoke about common ideas. Well, if you’re coupling classes in software development, that’s exactly what you’re doing. Adding to the MSSQL example, every time your application talks to the database, is just like telling your software over and over what it is and how to use it. .We can instead create a definition, giving our application a common database idea, defining a database’s concepts: i.e., create, read, update, delete and use the concepts instead of the specifics to decouple our code and make it interchangeable. Let’s take a look at some pseudocode:
First we can define the database concept in an IDatabase interface.
Next we can write a specific MSSQLDB class with its implementation details.
Finally, we can use our abstraction, instead of a specific implementation, so we’re not dependent on the type of database
By abstracting our database we can now interchange it with others, we could easily write a class constructor and decide which database to use at run time. We could take it one step further, write a TestDB using the same IDatabase interface, and use it as a completely separate database for Test Driven Development(TDD). Our TestDB doesn’t even have to be MSSQL!
Thanks for hanging with me throughout this SOLID principles series, I hope you’ve enjoyed it and I can’t wait to see you again!