Lโ€™ISP si concentra sulla struttura e lโ€™organizzazione delle interfacce allโ€™interno di unโ€™applicazione, promuovendo l'uso di interfacce piรน piccole e specifiche invece di interfacce grandi e generiche.

Lโ€™ISP รจ stato introdotto da Robert C. Martin e afferma che "le classi che implementano interfacce non dovrebbero essere costrette a implementare metodi di cui non hanno bisogno". In altre parole, le interfacce dovrebbero essere suddivise in componenti piรน piccoli e specifici per garantire che le classi che le implementano abbiano solo le funzionalitร  di cui hanno effettivamente bisogno.

Seguendo lโ€™ISP, gli sviluppatori possono evitare di creare interfacce โ€œgrasseโ€ che combinano molte responsabilitร  diverse, rendendo cosรฌ piรน difficile la manutenzione e la modifica del codice. Invece, lโ€™ISP promuove la creazione di interfacce โ€œsnelleโ€ che hanno una singola responsabilitร  o un gruppo di responsabilitร  strettamente correlate.

Questo principio รจ strettamente legato agli altri principi SOLID, in particolare al Principi SOLID. Single responsibility (SRP) e al Principi SOLID. Dependency Inversion Principle (DIP). Lโ€™SRP si concentra sulla divisione delle classi in modo che abbiano una singola responsabilitร , mentre lโ€™ISP si concentra sulla divisione delle interfacce. Il DIP, dโ€™altra parte, suggerisce che le classi dovrebbero dipendere da astrazioni piuttosto che da implementazioni concrete, il che รจ facilitato dallโ€™uso di interfacce specifiche.

Vantaggi

  • Facilitร  di manutenzione: suddividere le interfacce in parti piรน piccole e specifiche facilita il processo di manutenzione del codice. In questo modo, รจ possibile apportare modifiche a unโ€™interfaccia senza influenzare altre parti del codice che non utilizzano la parte modificata.
  • Minore accoppiamento: lโ€™ISP riduce lโ€™accoppiamento tra le classi, poichรฉ le classi dipendono solo dalle interfacce che utilizzano effettivamente. Questo rende il sistema piรน flessibile e modulare.
  • Maggiore coesione: Lโ€™ISP promuove una maggiore coesione tra le classi e le interfacce, poichรฉ ciascuna interfaccia ha una responsabilitร  ben definita. Avere interfacce piรน specifiche contribuisce a mantenere il codice organizzato e comprensibile.
  • Sostituibilitร : Seguendo lโ€™ISP, le classi diventano piรน intercambiabili, poichรฉ le nuove implementazioni possono essere facilmente sostituite senza interrompere il codice esistente. Questo incoraggia lo sviluppo di componenti riutilizzabili.

Limiti

  1. Aumento del numero di interfacce: Come menzionato in precedenza, lโ€™adozione dellโ€™ISP puรฒ portare ad un aumento del numero di interfacce nel sistema. Un numero eccessivo di interfacce puรฒ aumentare la complessitร  e rendere il codice piรน difficile da gestire se non viene mantenuto correttamente.
  2. Sovraprogettazione: Seguire lโ€™ISP puรฒ portare alla sovraprogettazione del sistema. Creare interfacce troppo specifiche e granulari puรฒ rendere il codice piรน difficile da mantenere e capire, soprattutto se i membri dellโ€™interfaccia sono strettamente correlati tra loro (KISS).
  3. Maggiore curva di apprendimento: Con un numero maggiore di interfacce, gli sviluppatori potrebbero avere difficoltร  a capire la struttura e le relazioni tra le classi e le interfacce. Questo puรฒ portare a una maggiore curva di apprendimento per i nuovi membri del team di sviluppo.
  4. Difficoltร  nella gestione delle dipendenze: Sebbene lโ€™ISP riduca lโ€™accoppiamento tra le classi, la gestione di un gran numero di interfacce puรฒ complicare la gestione delle dipendenze allโ€™interno del sistema. Gli sviluppatori potrebbero dover prestare maggiore attenzione a come le dipendenze sono organizzate e iniettate tra le classi, il che potrebbe richiedere un investimento maggiore di tempo e sforzo nella progettazione del sistema.
  5. Potenziale ridondanza del codice: Lโ€™adozione dellโ€™ISP potrebbe portare a una potenziale ridondanza del codice in alcuni casi. Quando diverse interfacce hanno funzionalitร  simili o condividono metodi comuni, gli sviluppatori potrebbero finire per duplicare il codice tra diverse classi che implementano tali interfacce. Questo puรฒ rendere il codice meno efficiente e aumentare il rischio di errori e inconsistenze.

Esempi

Esempio 1

Immaginiamo di avere un sistema che gestisce diversi tipi di stampanti, alcune delle quali supportano anche la scansione. Un approccio non conforme allโ€™ISP potrebbe utilizzare unโ€™interfaccia generale come questa:

public interface IDevice
{
    void Print();
    void Scan();
}

Tuttavia, seguendo lโ€™ISP, divideremmo lโ€™interfaccia in interfacce piรน specifiche:

public interface IPrinter
{
    void Print();
}
 
public interface IScanner
{
    void Scan();
}

Ora, le classi che implementano solo la funzionalitร  di stampa o scansione possono implementare lโ€™interfaccia appropriata, senza dover implementare metodi non necessari:

public class Printer : IPrinter
{
    public void Print()
    {
        // Logica di stampa
    }
}
 
public class Scanner : IScanner
{
    public void Scan()
    {
        // Logica di scansione
    }
}
public class AllInOnePrinter : IPrinter, IScanner
{
    public void Print()
    {
        // Logica di stampa
    }
 
    public void Scan()
    {
        // Logica di scansione
    }
}

In questo esempio, possiamo vedere come lโ€™ISP renda il codice piรน pulito e flessibile. Le classi Printer e Scanner implementano solo le interfacce pertinenti alle loro funzionalitร , evitando lโ€™implementazione di metodi inutili. La classe AllInOnePrinter, che supporta sia la stampa che la scansione, implementa entrambe le interfacce.

Esempio 2

In questo caso, stiamo suddividendo lโ€™interfaccia piรน grande in parti piรน piccole e piรน specifiche per garantire che le nostre classi implementino solo ciรฒ di cui hanno effettivamente bisogno.

// Violates ISP
public interface IWorker
{
    void Work();
    void Eat();
}
 
public class HumanWorker : IWorker
{
    public void Work()
    {
        // Working
    }
 
    public void Eat()
    {
        // Eating in the break
    }
}
 
public class RobotWorker : IWorker
{
    public void Work()
    {
        // Working much more efficiently
    }
 
    public void Eat()
    {
        throw new NotImplementedException("Robots do not eat.");
    }
}
 
// Adheres to ISP
public interface IWork
{
    void Work();
}
 
public interface IEat
{
    void Eat();
}
 
public class HumanWorker : IWork, IEat
{
    public void Work()
    {
        // Working
    }
 
    public void Eat()
    {
        // Eating in the break
    }
}
 
public class RobotWorker : IWork
{
    public void Work()
    {
        // Working much more efficiently
    }
}