SOLID: principi, 5 regole OOP e applicazione nello sviluppo

Autore: IT Sectr Pubblicato: 2026-05-11 Tempo di lettura: 10 min

SOLID — cinque principi di programmazione orientata agli oggetti formulati da Robert C. Martin (Uncle Bob) all’inizio degli anni 2000. Secondo DigitalOcean, 2024, SOLID sta per Single Responsibility, Open-Closed, Liskov Substitution, Interface Segregation e Dependency Inversion. Questi principi costituiscono il fondamento di Clean Architecture e vengono applicati nello sviluppo Android (MVP, MVVM, Clean Architecture) e iOS (VIPER, TCA).

Punti chiave

  • SOLID — acronimo di cinque principi OOP: SRP, OCP, LSP, ISP, DIP, formulati da Robert C. Martin per creare codice flessibile e manutenibile.
  • SRP (Single Responsibility) — ogni classe ha una sola ragione per cambiare, una responsabilità per modulo.
  • OCP (Open-Closed) — le classi sono aperte all’estensione ma chiuse alla modifica, implementato tramite ereditarietà e polimorfismo.
  • LSP (Liskov Substitution) — gli oggetti delle sottoclassi devono sostituire gli oggetti della classe base senza alterare la correttezza del programma.
  • ISP (Interface Segregation) — i clienti non devono dipendere da interfacce che non usano, le interfacce devono essere strette e specifiche.
  • DIP (Dependency Inversion) — i modelli di alto livello non dipendono da modelli di basso livello, entrambi dipendono da astrazioni.

Cos’è SOLID? Panoramica dei cinque principi

SOLID — un acronimo mnemonico che rappresenta cinque principi di progettazione orientata agli oggetti. Il termine è stato introdotto da Robert C. Martin nell’articolo «Design Principles and Design Patterns» (2000) e successivamente reso popolare nel libro «Agile Software Development: Principles, Patterns, and Practices» (2002). SOLID non è un framework o una libreria — è un insieme di pratiche che rendono il codice meno accoppiato, più testabile e più facile da modificare.

Secondo Clean Coder Blog, 2014, ogni principio SOLID risolve un problema di progettazione specifico: SRP combatte le classi God, OCP previene modifiche a cascata, LSP protegge da ereditarietà errata, ISP evita interfacce grasse e DIP riduce l’accoppiamento forte. Insieme formano il fondamento di Clean Architecture, utilizzata nei progetti Android con MVP, MVVM e MVI.

SRP: Principio di Responsabilità Unica

Single Responsibility Principle (SRP) — principio di responsabilità unica. La formulazione: «Una classe dovrebbe avere una sola ragione per cambiare». Ciò significa che ogni modulo o classe è responsabile di esattamente una funzionalità o un’entità del dominio. Se una classe gestisce sia gli utenti che l’invio di email — ha due ragioni per cambiare, violando SRP.

Secondo Robert C. Martin, 2002, SRP è il principio più importante e allo stesso tempo il più violato. Nello sviluppo mobile, SRP viene spesso violato in Activity/Fragment combinando logica UI, navigazione, rete e logica di business. La soluzione è estrarre ogni livello in una classe separata: ViewModel per la logica UI, Repository per i dati, NavController per la navigazione.

Esempio SRP: Decomposizione di UserManager

Considera la classe UserManager, che carica un profilo, salva le impostazioni e invia email. Sono tre responsabilità distinte, ciascuna delle quali dovrebbe essere estratta in una classe separata: UserProfileRepository (caricamento), UserSettingsStorage (salvataggio) e EmailService (invio). Il codice cliente (ViewModel) utilizza tutte e tre tramite Dependency Injection, e ogni classe è facilmente testabile isolatamente e cambia senza influenzare le altre.

kotlin
// ❌ Violazione SRP: Activity conosce rete, DB e UI
class ProfileActivity : AppCompatActivity() {
    fun loadProfile() {
        api.getUser() // Chiamata di rete
        db.saveUser()    // Operazione DB
        updateUI()         // Aggiornamento UI
    }
}

// ✅ SRP rispettato: i livelli sono separati
class ProfileViewModel : ViewModel() {
    private val repo = UserRepository()
    fun loadProfile() { repo.getUser() }
}

Segni di violazione di SRP: una classe con più di 200 righe, metodi di diversi domini, cambiamenti frequenti per ragioni diverse. Per lo sviluppo Android, la regola è semplice: Activity gestisce solo il ciclo di vita dello schermo, ViewModel gestisce lo stato UI, Repository gestisce le fonti dati.

SRP e Architettura a Microservizi

Il principio SRP si applica non solo alle classi ma anche all’architettura a livello di servizio. Ogni microservizio gestisce un’entità del dominio: UserService — solo utenti, PaymentService — solo pagamenti, NotificationService — solo notifiche. Ciò consente di scalare, distribuire e testare i servizi in modo indipendente. Nelle applicazioni mobili, SRP a livello di microservizio si manifesta nella separazione dei client API per dominio.

OCP: Principio Aperto/Chiuso

Open-Closed Principle (OCP) — le classi dovrebbero essere aperte all’estensione (nuovo comportamento può essere aggiunto) e chiuse alla modifica (il codice esistente non viene modificato). Ciò si ottiene attraverso polimorfismo, classi astratte e interfacce. Invece di aggiungere if-else a un metodo esistente, viene creata una nuova implementazione dell’interfaccia.

Secondo Clean Coder Blog, 2014, OCP funziona meglio con il pattern Strategy. Ad esempio, se un’app supporta diversi metodi di pagamento (Google Pay, Apple Pay, PayPal), non è necessario aggiungere uno switch-case al processore di pagamento. Ogni metodo di pagamento implementa un’interfaccia comune PaymentGateway, e un nuovo sistema di pagamento viene aggiunto come nuova classe senza modificare quelle esistenti.

kotlin
// ✅ OCP: aperto all’estensione, chiuso alla modifica
interface PaymentGateway {
    fun processPayment(amount: Double): Boolean
}

class GooglePayGateway : PaymentGateway {
    override fun processPayment(amount: Double) = true
}

// Nuovo sistema di pagamento — senza modificare il codice esistente
class ApplePayGateway : PaymentGateway {
    override fun processPayment(amount: Double) = true
}

LSP: Principio di Sostituzione di Liskov

Liskov Substitution Principle (LSP) — principio di sostituzione di Barbara Liskov. Se S è un sottotipo di T, allora gli oggetti di tipo T possono essere sostituiti da oggetti di tipo S senza alterare le proprietà del programma. Formalmente: una funzione che utilizza una classe base dovrebbe funzionare correttamente con qualsiasi sua sottoclasse. Se una sottoclasse lancia un’eccezione dove la classe base non la lancia — LSP è violato.

Secondo Robert C. Martin, 2002, LSP è il principio SOLID più difficile da comprendere. L’esempio classico di violazione è la classe Square che eredita da Rectangle. Se setWidth su Square imposta sia larghezza che altezza, il codice cliente che si aspetta il comportamento di Rectangle ottiene un risultato inaspettato. Nello sviluppo mobile, LSP viene spesso violato quando si eredita ViewModel — quando una ViewModel figlia aggiunge dipendenze obbligatorie.

kotlin
// ❌ Violazione LSP: Square rompe il comportamento di Rectangle
open class Rectangle(open var width: Int, open var height: Int)

class Square(side: Int) : Rectangle(side, side) {
    override var width
        get() = super.width
        set(value) { super.setBoth(value, value) }
}

ISP: Principio di Segregazione delle Interfacce

Interface Segregation Principle (ISP) — i clienti non dovrebbero dipendere da interfacce che non usano. Invece di un’unica interfaccia «grassa», crea diverse interfacce strette e specializzate. Se una classe implementa un’interfaccia ma alcuni metodi lanciano UnsupportedOperationException o rimangono vuoti — è un chiaro segno di violazione di ISP.

Secondo DigitalOcean, 2024, ISP è particolarmente rilevante nello sviluppo mobile quando si progettano ViewModel e Repository. Invece di un’unica interfaccia UserRepository con tutti i metodi CRUD, è meglio creare QueryUserRepository (sola lettura) e CommandUserRepository (scrittura). Quindi un cliente di sola lettura (elemento UI) dipende solo dall’interfaccia Query e non sa nulla dei metodi di scrittura.

kotlin
// ❌ Interfaccia grassa — il cliente è costretto a implementare metodi non necessari
interface UserOperations {
    fun getUser(id: String): User
    fun saveUser(user: User)
    fun deleteUser(id: String)
    fun exportUsers(): File
}

// ✅ ISP: interfacce separate
interface UserReader { fun getUser(id: String): User }
interface UserWriter { fun saveUser(user: User) }
interface UserDeleter { fun deleteUser(id: String) }

DIP: Principio di Inversione delle Dipendenze

Dependency Inversion Principle (DIP) — i moduli di alto livello non dovrebbero dipendere da moduli di basso livello. Entrambi dovrebbero dipendere da astrazioni (interfacce). Le astrazioni non dovrebbero dipendere dai dettagli — i dettagli dovrebbero dipendere dalle astrazioni. Questo non è «Dependency Injection» (DI), sebbene DI sia un modo comune per implementare DIP.

Secondo Robert C. Martin, 2019, DIP è il fondamento di Clean Architecture. ViewModel (alto livello) non dovrebbe creare direttamente un’istanza di RetrofitApi (dettaglio). Invece, ViewModel dipende da un’interfaccia UserRepository, e l’implementazione concreta UserRepositoryImpl con Retrofit viene passata attraverso il costruttore. In Android, DIP viene implementato tramite Hilt/Dagger o Koin: tutte le dipendenze sono fornite tramite il contenitore DI.

kotlin
// ✅ DIP: Module dipende dall’astrazione, non dai dettagli
class UserRepositoryImpl(
    private val api: UserApi,   // Dipende dall’interfaccia
    private val db: UserDao     // Dipende dall’interfaccia
) : UserRepository {

    override suspend fun getUser(id: String): User {
        return api.fetchUser(id)
    }
}

// Hilt DI: i dettagli sono collegati tramite il modulo DI
@Module
object NetworkModule {
    @Provides
    fun provideUserApi(retrofit: Retrofit): UserApi =
        retrofit.create(UserApi::class.java)
}

Applicazione di SOLID nello sviluppo mobile

SOLID nello sviluppo mobile viene applicato a tutti i livelli: dall’architettura dell’applicazione alle singole classi. Nei progetti Android, Clean Architecture divide il codice in tre livelli: domain (logica di business — indipendente dai framework), data (repository, API, DB) e presentation (UI, ViewModel). Il livello domain utilizza i principi SOLID: casi d’uso (SRP), interfacce di repository (DIP), classi entità (OCP + LSP).

Secondo Android Developers Guide, 2025, SRP in Android si manifesta nella separazione di ViewModel, Repository e Mapper. OCP — nell’aggiunta di nuove fonti dati tramite l’interfaccia DataSource. LSP — nella gestione uniforme di Result su diversi repository. ISP — nell’approccio CQRS (separazione dei repository Read/Write). DIP — tramite Hilt/Koin per l’iniezione delle dipendenze.

PrincipioProblema senzaSoluzione nel progetto mobile
SRPActivity di 1000+ righeViewModel + UseCase + Repository
OCPswitch-case per tipo di pagamentoStrategy: interfaccia PaymentGateway
LSPErrore nella sostituzione di BaseViewModelVerificare il contratto delle sottoclassi
ISPUnsupportedOperationExceptionSeparazione Reader / Writer
DIPViewModel crea Retrofit manualmenteContenitore DI Hilt / Koin

Errori comuni nell’applicare SOLID

Errori SOLID sono spesso legati a un’eccessiva complicazione del codice. Il primo — seguire i principi alla lettera senza considerare il contesto. Dividere una classe UserService in 10 interfacce e 15 classi solo per un ISP «pulito» è overengineering. SOLID è uno strumento, non un obiettivo. Il secondo errore — confondere SRP con «un metodo = una responsabilità». Una classe può avere più metodi se appartengono tutti alla stessa area di responsabilità.

Secondo Simple Thread, 2024, il terzo errore — ignorare LSP quando si eredita ViewModel in Android. Se la ViewModel base si aspetta LiveData ma la figlia usa StateFlow — il codice cliente iscritto a LiveData non riceverà aggiornamenti. Il quarto — violare DIP per i test: RepositoryImpl crea direttamente un’istanza OkHttpClient, rendendo impossibili i test unitari.

La regola d’oro: applica SOLID quando risolve un problema reale (cambiamenti frequenti, difficoltà di test, duplicazione). Per schermate CRUD semplici, l’adesione rigorosa a tutti e cinque i principi è eccessiva. Per logica di business, calcoli finanziari e interazioni API, SOLID è essenziale.

Collegamento tra SOLID e Clean Architecture

Clean Architecture (Robert C. Martin, 2012) — applicazione diretta di SOLID a livello di strati dell’applicazione. SRP definisce i confini dei casi d’uso (ogni caso d’uso — una classe). OCP viene implementato tramite interfacce di repository (Data Layer può cambiare senza modificare Domain). ISP fornisce la separazione del caso d’uso in confini di input/output. DIP — direzione delle dipendenze verso l’interno del livello Domain. LSP garantisce che qualsiasi implementazione di repository sia sostituibile senza rompere i casi d’uso.

Domande frequenti

Cos’è SOLID in parole semplici?

SOLID — cinque regole per scrivere codice facile da modificare, testare e comprendere. Ogni lettera è un principio: non scrivere classi grandi (SRP), non modificare codice esistente — aggiungine di nuovo (OCP), non rompere il comportamento delle sottoclassi (LSP) e altri.

Qual è il principio SOLID più importante?

SRP (Single Responsibility) è considerato il più importante perché la sua violazione porta a classi God — classi enormi difficili da testare e modificare. Tuttavia, senza DIP (Dependency Inversion) il codice rimane fortemente accoppiato, il che è altrettanto critico.

SOLID è obbligatorio per lo sviluppo mobile?

Non obbligatorio, ma altamente raccomandato per progetti commerciali con un lungo ciclo di vita. Per app semplici (una schermata, nessuna logica di business), SOLID può essere eccessivo. Per progetti con 50+ schermate e 3+ sviluppatori, SOLID è il minimo necessario.

Cosa succede se non si segue SOLID?

Conseguenze: le classi diventano «grasse» (1000+ righe), un cambiamento in un punto ne rompe altri tre, i test unitari diventano impossibili, aggiungere una nuova funzionalità richiede settimane invece di giorni. Con il tempo, il codice diventa un «Big Ball of Mud» — aggrovigliato e fragile.

Come verificare se SOLID viene seguito in un progetto?

Segni di conformità: ogni classe ha meno di 200 righe, modificare una funzionalità non tocca 5+ file, i test possono essere scritti senza mockare 10 dipendenze, un nuovo sviluppatore capisce la struttura in un giorno. Strumenti come SonarQube e detekt aiutano a identificare violazioni di SRP e DIP.

Riepilogo

  • SOLID — cinque principi OOP (SRP, OCP, LSP, ISP, DIP) per creare codice flessibile e manutenibile
  • SRP — ogni entità è responsabile di un compito, risolve il problema delle classi God
  • OCP — estensione tramite polimorfismo, non modifica del codice esistente
  • LSP — le sottoclassi non devono rompere il comportamento della classe base
  • ISP — interfacce strette invece di «coltellini svizzeri» universali
  • DIP — dipendenza dalle astrazioni, iniezione tramite Hilt/Koin in Android
  • SOLID è essenziale per Clean Architecture e progetti mobili commerciali

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche