Brook Preloader

Principi di programmazione SOLID in Java

I principi SOLID sono un insieme di linee guida sviluppate per la scrittura di codice software ben strutturato e facile da mantenere. Il termine SOLID è un acronimo che rappresenta cinque principi chiave della programmazione orientata agli oggetti: Single Responsibility Principle (SRP), Open/Closed Principle (OCP), Liskov Substitution Principle (LSP), Interface Segregation Principle (ISP), e Dependency Inversion Principle (DIP).

L’obiettivo di questo approccio è di migliorare la qualità del codice sorgente, renderlo facilmente ampliabile e di ridurre i problemi di manutenzione a lungo termine.

Single Responsibility Principle (SRP)

 

Ogni classe dovrebbe avere una sola responsabilità.

 

 

class EmailSender {
   public void sendEmail(String message) {
     // ... implementation
   }
}   

Nell’esempio appena illustrato, questa classe ha solo una responsabilità, ovvero inviare email.

 

Open/Closed Principle (OCP)

 

Una classe dovrebbe essere aperta per l’estensione e chiusa per la modifica.

 

 

interface Shape {
  double calculateArea();
}

class Rectangle implements Shape {
  // ... implementation
}

class Circle implements Shape {
  // ... implementation
}

La classe Shape (figura geometrica) è aperta per l’estensione, poiché possiamo creare nuove forme implementando l’interfaccia, ma chiusa per la modifica, poiché non dobbiamo modificare la classe Shape per aggiungere nuove forme.

 

Liskov Substitution Principle (LSP)

 

Gli oggetti di una sottoclasse dovrebbero essere utilizzabili dove si aspetta un oggetto della superclasse.

 

 

class Animal {
  // ... implementation
}

class Bird extends Animal {
  // ... implementation
}

Possiamo usare un oggetto di tipo Bird dove ci aspettiamo un oggetto di tipo Animal, poiché Bird estende Animal.

 

Interface Segregation Principle (ISP)

 

Molte interfacce specifiche sono meglio di una interfaccia unica che comprende tutte le funzionalità.

 

 

interface Flyable {
  void fly();
}

interface Walkable {
  void walk();
}

class WingedAnimal implements Flyable, Walkable {
  // ... implementation
}

In questo esempio, abbiamo due interfacce separate, Flyable e Walkable, anziché una interfaccia unica che comprende entrambe le funzionalità.

 

Dependency Inversion Principle (DIP)

 

I moduli a livello elevato non dovrebbero dipendere da moduli a livello basso, ma entrambi dovrebbero dipendere da abstrazioni.

 

 

interface Database {
  void connect();
  void disconnect();
  // ... other methods
}

class MySQLDatabase implements Database {
  // ... implementation
}

class UserRepository {
  private final Database database;

  public UserRepository(Database database) {
    this.database = database;
  }

  public void saveUser(User user) {
    // ... implementation using database instance
  }
}

In questo esempio, la classe UserRepository dipende da un’interfaccia Database anziché da una implementazione specifica come MySQLDatabase. Questo ci permette di cambiare l’implementazione del database senza modificare la classe UserRepository.