Prinicipi di sviluppo SOLID in WinterCMS - D

Luca Benati

In questo articolo vediamo il quinto principio SOLID; Dependecy Inversion principle (DIP)


Pubblicato da Luca Benati il 25 settembre 2023

Eccoci al quinto e ultimo articolo di questa serie sui principi di sviluppo SOLID, in questo articolo vedremo per cosa sta e cosa dice il quinto principio definito con la "D" di Dependency Inversion principle abbreviato in DIP

Dependency Inversion Princple

The principle of dependency inversion refers to the decoupling of software modules. This way, instead of high-level modules depending on low-level modules, both will depend on abstractions.

cioè

I moduli di alto livello non devono dipendere da quelli di basso livello. Entrambi devono dipendere da astrazioni. Le astrazioni non devono dipendere dai dettagli; sono i dettagli che dipendono dalle astrazioni.

Leggendolo potrebbe sembrare un po' confuso ma come vedremo in relatà non lo è affatto.

Il principio di inversione delle dipendenze è una tecnica per disaccoppiare im oduli software rovesciando la tecnica di far dipendere mkoduli di alto livello da quelli di basso livello. Per raggiungere questo scopo si andranno ad utilizzare come dipendenze delle astrazioni invece che classi concrete, vediamo come:

Mettiamo il caso che abbiamo una classe per inviare SMS che in caso di errore debba loggare l'eccezione su file, andremo quindi a scrivere una classe che si occupa di salvare il messaggio passato


class FileLogger
{
    public function logError(string $message)
    {
        // ..logica della class per il log su file dell'errore
    }
}

che passaremo al costruttore della classe SmsService in qesto modo


class SmsService
{
    private FileLogger $logger;

    public function __construct(FileLogger $logger)
    {
        $this->logger = $logger;
    }

    public function sendSms()
    {
        try {
            // ..
        } catch (Exception $e) {
            $this->logger->logError($e->getMessage());
        }
    }
}

...o istanziare direttamente nel costruttore


class SmsService
{
    private FileLogger $logger;

    public function __construct()
    {
        $this->logger = new Filelogger();
    }

    public function sendSms()
    {
        try {
            // ..
        } catch (Exception $e) {
            $this->logger->logError($e->getMessage());
        }
    }
}

Questo approccio funzionerà come previsto però viola il principio di dipendency inversion perchè la classe di alto livello dipende direttamente dalla classe di basso livello.

Cosa succederebbe se in futuro dovessimo loggare gli errori su database invece che su file? Dovremmo andare a modificare direttamente la classe SmsService (e tutte le altre che utilizzano il logger) e questa non è una soluzione flessibile e una sostituzione del genere potrebbe diveniere parecchio problematica e violerebbe anche il principio OCP (open/closed principle).

Qundi come dovremmo porcedere?

In accordo con questo principio dobbiamo far dipendere la classe SmsService da un'astrazione invece che di un'implementazione dettagliata, andiamo quindi ad aggiungere un'interfaccia per i Logger


interface LoggerInterface()
{
    public function logError(string $message): void;
}

che andremo ad usare nella classe FileLogger


class FileLogger implements LoggerInterface
{
    public function logError(string $message)
    {
        // ..logica della class per il log su file dell'errore
    }
}

e andremo a cambiare la classe SmsService in questo modo


class SmsService
{
    private FileLogger $logger;

    public function __construct(LoggerInterface $logger)
    {
        $this->logger = $logger;
    }

    public function sendSms()
    {
        try {
            // ..
        } catch (Exception $e) {
            $this->logger->logError($e->getMessage());
        }
    }
}

Facendo in questo modo possiamo andare a sostituire in qualsiasi momento la classe di implementazione dei dettagli basta che implementi l'interfaccia LoggerInterface e non ci sarà bisogno di andare a modificare la classe SmsService perchè non dipende più dalle implementazioni concrete ma dipende solo dall'interfaccia LoggerInterface

Anche per quanto riguarda la "D" è tutto e si conclude così questa serie di articoli sui principi SOLID della programmazione ad oggetti.

L'utilizzo di tutti questi principi insieme hai design patterns ci aiutano ad estendere, modificare e testare il codice in modo molto più efficace, veloce e produttivo.

Happy coding!

Gli altri articoli di questa serie:


Lunga vita e prosperità

Ti interessa un argomento non trattato?