Sto rifattorizzando un progetto che ho fatto per il mio lavoro e sto cercando di applicare i principi SOLID per rendere l'architettura più pulita. Ho riscontrato un problema con il principio di inversione delle dipendenze che non riesco a risolvere.
Il principio di inversione di dipendenza afferma che i moduli di livello superiore non dovrebbero dipendere da quelli di livello inferiore, ma entrambi dovrebbero dipendere dalle astrazioni. Nel contesto del mio programma, ho una classe App che esegue il programma, e una classe per il sensore a infrarossi MLX90614 che sto usando per prendere oggetti e temperature ambientali. Nella versione iniziale, l'app dipendeva semplicemente direttamente dalla classe di driver MLX90614. Ma questo viola il DIP.
//MLX90614.h
class MLX90614
{
public:
readTemperature();
}
//app.h
#include "mlx90614.h"
class App
{
public:
//do stuff
private:
MLX90614 sensor;
}
Per risolvere il problema, ho deciso di creare un'interfaccia ISensor astratta che potesse essere utilizzata da App e implementata da 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;
}
Questo funziona per rimuovere la dipendenza concreta dall'app, ma trovo alcuni problemi con questo metodo che nessun tutorial su DIP sembra coprire.
Il primo è che ora MLX90614 ha nuove dipendenze introdotte. Ciò significa che se voglio riutilizzare il driver in un'applicazione completamente diversa, che è altamente probabile, dovrò trascinare l'intero sistema di interfaccia insieme ad esso. Questo sembra un po 'il problema di "voler una banana ma ottenere il gorilla e la foresta con essa". Se dovessi riutilizzare questo codice e rilasciarlo come libreria separata al pubblico, non ci sarebbe alcun uso per l'interfaccia ISensor specifica dell'applicazione, che mi sembra un pessimo design del pacchetto.
Per peggiorare le cose, molti tutorial specificano specificamente che per aderire completamente al DIP, l'interfaccia deve essere impacchettata con il componente di livello superiore (questo soddisfa la seconda regola). Questo rende le cose molto più complicate e complicate, dato che ora MLX90614 è strettamente associato al modulo App di livello superiore, e per riutilizzare il driver devo trascinare non solo l'interfaccia, ma l'intera intera applicazione insieme! Questo sembra un incubo di programmazione!
//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;
}
Per questo motivo, non riesco a capire in che modo l'inversione di dipendenza fa più bene del danno al mio sistema. Se voglio che i moduli di livello inferiore siano altamente riutilizzabili, allora sembra strano renderli dipendenti da un intero sistema di livello superiore che è completamente irrilevante per la loro implementazione. E considerando come nella maggior parte dei sistemi che ho programmato le librerie di livello inferiore sono sempre molto più riutilizzabili rispetto alle implementazioni di livello superiore, non riesco a vedere come l'inversione di dipendenza sia utile.