I'm refactoring a project I did for my work and I'm trying to apply the SOLID principles to make the architecture cleaner. I've run into an issue with the Dependency Inversion Principle that I can't seem to solve.
The Dependency Inversion Principle states that higher level modules shouldn't depend on lower level ones, but both should depend on abstractions. In the context of my program, I have an App class that runs the program, and a class for the MLX90614 infrared Sensor that I'm using to take object and ambient temperatures. In the initial release, the app simply depended directly on the MLX90614 driver class. But this violates the DIP.
//MLX90614.h
class MLX90614
{
public:
readTemperature();
}
//app.h
#include "mlx90614.h"
class App
{
public:
//do stuff
private:
MLX90614 sensor;
}
To fix this, I decided to create an abstract ISensor interface to be consumed by App and implemented by MLX90614.
//sensor.h
class ISensor
{
public:
virtual public readTemperature() = 0;
}
class MLX90614 : public ISensor
{
//implements ISensor
}
//app.h
#include "sensor.h"
class App
{
private:
ISensor *sensor;
}
This works to remove the concrete dependency from App, but I find a few problems with this method that no tutorial on DIP seems to cover.
The first is that now MLX90614 has new introduced dependencies. This means that if I ever want to re-use the driver in a completely different application, which is highly likely, I'll have to drag the entire interface system along with it. This seems like a bit of the issue of "wanting a banana but getting the gorilla and the forest with it." If I were to re-use this code and release it as a separate library to the public there would be no use for the application specific ISensor interface, which just seems like bad package design to me.
To make matters worse, many tutorials specifically state that to fully adhere to DIP, the interface must be packaged with the higher level component (this satisfies the second rule). This makes things a whole lot more convoluted and messy, as now MLX90614 is tightly coupled with the higher level App module, and in order to re-use the driver I have to drag not only the interface, but the entire application along with it! This seems like a programming nightmare!
//mlx90614.h
#include "app.h"
class MLX90614 : public ISensor
{
//Implements ISensor, but now is completely dependent on the App module,
//which we definitely don't want.
}
//app.h
class ISensor
{
//do stuff
}
class App
{
private:
ISensor *sensor;
}
Because of this, I fail to see how dependency inversion does more good than harm to my system. If I want the lower level modules to be highly re-useable then it seems odd to make them dependent on an entire higher-level system that's completely irrelevant to their implementation. And considering how in most systems I've programmed the lower level libraries are always far more re-useable than the upper level implementations, I fail to see how dependency inversion is useful at all.