Accoppiamento (Coupling) nello sviluppo mobile — concetti chiave, tipi e come ridurlo

Autore: IT Sectr Pubblicato: 2026-05-13 Tempo di lettura: 9 min

L'accoppiamento (Coupling) è una metrica che mostra quanto un modulo di un'applicazione dipende da un altro. Secondo Wikipedia, un basso accoppiamento (low coupling) è segno di un sistema ben progettato, dove i moduli possono essere modificati senza rompere quelli vicini. Gestire il coupling è uno dei compiti principali dell'architetto nella progettazione di applicazioni mobili.

Punti Chiave

  • Coupling — il grado di dipendenza tra moduli: alto = accoppiamento forte, basso = accoppiamento debole
  • Content coupling — il tipo peggiore, quando un modulo modifica i dati interni di un altro modulo
  • Data coupling — il tipo migliore, quando i moduli scambiano solo dati semplici tramite parametri
  • Dependency Injection — lo strumento principale per ridurre il coupling nello sviluppo mobile
  • Interfacce e astrazioni — il meccanismo principale per ridurre l'accoppiamento tra i livelli dell'applicazione

Cos'è il Coupling

Il coupling (accoppiamento) è una metrica che determina quanto strettamente un modulo o una classe è collegato a un altro. Più un modulo sa sulla struttura interna di un altro, più alto è il coupling e più difficile è modificare il sistema. In un'architettura ben progettata, il coupling dovrebbe essere minimo — i moduli interagiscono solo attraverso interfacce strettamente definite.

Esistono due aspetti del coupling: afferente (dipendenze in entrata — quanti moduli dipendono da questo) ed efferente (dipendenze in uscita — da quanti moduli questo dipende). L'analisi di queste metriche aiuta a identificare i punti caldi nell'architettura dove la modifica di un modulo influenzerebbe molti altri. Strumenti come IntelliJ Dependency Analyzer e Xcode Graph visualizzano queste connessioni.

È importante capire che il coupling zero è impossibile — i moduli devono interagire in qualche modo, altrimenti non è un sistema ma un insieme di programmi isolati. Il compito dell'architetto è rendere il coupling gestibile e trasparente. L'ideale: i moduli interagiscono solo attraverso interfacce e passano solo dati semplici, senza conoscere la struttura interna l'uno dell'altro. Questo si chiama basso accoppiamento (loose coupling).

Tipi di accoppiamento da debole a forte

Sei tipi di coupling formano una scala dal migliore al peggiore. Comprendere questa scala aiuta a valutare il codice esistente e scegliere la direzione del refactoring. La maggior parte dei progetti mobile ha tipi misti di coupling, e il compito dell'architetto è sostituire progressivamente i tipi forti con quelli deboli.

Data coupling — il tipo migliore

Data coupling (accoppiamento di dati) — i moduli scambiano solo dati semplici attraverso parametri di metodi. Il modulo A chiama il metodo del modulo B, passando primitivi o strutture semplici, e riceve un risultato. Il modulo A non sa come B è implementato internamente. Questo è il tipo di coupling più desiderabile: minimizza l'impatto delle modifiche.

Esempio: EmailValidator.isValid(email: String): Boolean. La classe consumatrice passa una stringa e riceve un Booleano, senza alcuna idea delle espressioni regolari o delle regole di validazione all'interno del validatore. Modificare la logica di validazione non richiede di modificare il consumatore — il coupling è minimo. Il data coupling è l'obiettivo per tutte le interfacce pubbliche in un'applicazione.

Stamp coupling — accettabile ma non ideale

Stamp coupling (accoppiamento per timbro) — i moduli scambiano oggetti composti ma usano solo una parte dei loro campi. Il modulo A passa un oggetto User al metodo calculateDiscount, che usa solo user.status. Il problema: se la struttura di User cambia (un campo obbligatorio viene aggiunto), il modulo calculateDiscount non cambia, ma il consumatore che crea l'oggetto User cambia.

In pratica, lo stamp coupling è inevitabile e accettabile se l'oggetto passato è un modello dati standard (Entity). Il problema sorge quando un modulo riceve un intero oggetto solo per un singolo campo. In tali casi, è meglio passare il valore specifico direttamente (data coupling). La soluzione è analizzare l'uso dei campi da parte del ricevente.

Control, External, Common e Content coupling

Control coupling — un modulo passa un flag a un altro che controlla il suo comportamento (calculate(useNewAlgorithm: Boolean)). Questo è peggio dello stamp coupling perché il modulo consumatore deve conoscere le varianti interne di funzionamento del modulo chiamato. Soluzione: dividere il metodo in due — calculateWithNewAlgorithm() e calculateWithLegacyAlgorithm().

External coupling — i moduli dipendono da un protocollo esterno, formato dati o API. Tutti i moduli che analizzano lo stesso JSON o lavorano con lo stesso database hanno external coupling. Non può essere evitato completamente, ma può essere isolato: creare un livello di mappatura tra il formato esterno e i modelli interni. Common coupling — i moduli condividono uno stato globale comune. Content coupling — il tipo peggiore, quando un modulo modifica direttamente i dati interni di un altro modulo.

Tipo di couplingLivelloDescrizione
DataMigliorePassaggio di dati semplici tramite parametri
StampAccettabilePassaggio di oggetti con uso parziale
ControlMedioControllo del comportamento tramite flag
ExternalAltoDipendenza da protocollo/formato esterno
CommonMolto altoCondivisione di stato globale
ContentInaccettabileModifica diretta dei dati interni del modulo

La scala del coupling da data (ideale) a content (disastro) è uno strumento pratico per le revisioni del codice. Se vedi common o content coupling in un progetto — questi sono obiettivi prioritari di refactoring. I data e stamp coupling sono accettabili e presenti in qualsiasi progetto, ma la loro quantità dovrebbe essere controllata.

Perché il coupling è critico nello sviluppo mobile

L'alto coupling trasforma lo sviluppo in un processo lento dove ogni modifica richiede di verificare decine di moduli potenzialmente rotti. Questo è particolarmente critico nello sviluppo mobile: le piattaforme vengono aggiornate annualmente (Android API Level, iOS SDK), le librerie trimestralmente e i requisiti aziendali continuamente. Il basso accoppiamento è l'unico modo per gestire questo flusso di cambiamenti senza regressioni costanti.

Esempio pratico: un'applicazione mobile dove tutte le schermate importano direttamente NetworkingManager e DatabaseManager. Quando si sostituisce il client HTTP da Retrofit a Ktor (Android) o da URLSession ad Alamofire (iOS), lo sviluppatore dovrebbe modificare ogni schermata. Con un basso coupling, basta cambiare un'implementazione nascosta dietro l'interfaccia NetworkDataSource — i consumatori non noteranno la sostituzione.

L'impatto del coupling sui test unitari è anche enorme. Una classe con alto coupling (creazione diretta di dipendenze nel costruttore) non può essere testata isolatamente — trascina con sé database, rete e UI. Per testare tale classe, bisogna avviare un emulatore e attendere i test di integrazione. Una classe con basso coupling accetta dipendenze tramite injection nel costruttore e può essere facilmente mockata.

kotlin
// Alto coupling — la classe crea le proprie dipendenze
class ProfileViewModelHigh {
    private val api = RetrofitApi()
    private val db = RoomDatabase.getInstance()
    private val cache = MemoryCache()
}

// Basso coupling — le dipendenze vengono passate tramite costruttore
class ProfileViewModelLow(
    private val api: ApiService,
    private val db: DatabaseService,
    private val cache: CacheService
)

Nel primo caso, ProfileViewModelHigh è strettamente legato a implementazioni specifiche — sostituire Retrofit con Ktor richiede di modificare il codice della ViewModel. Nel secondo caso, ProfileViewModelLow dipende solo da interfacce, le cui implementazioni sono fornite esternamente. Testare la seconda classe è banale: passare implementazioni mock e verificare la logica senza emulatore.

Pattern per ridurre il coupling

Il Principio di Inversione delle Dipendenze (D in SOLID) è il fondamento per ridurre il coupling. Il principio prescrive di dipendere dalle astrazioni, non dalle implementazioni concrete. Invece di creare direttamente un oggetto RetrofitApi, una classe dovrebbe ricevere un'interfaccia ApiService. Questo sposta la dipendenza da una libreria specifica al livello di astrazione, che può essere sostituito senza modificare il consumatore.

Il pattern Observer (o le sue versioni reattive — StateFlow, Combine Publishers) riduce il coupling tra la fonte dati e gli abbonati. L'abbonato non sa da dove provengono i dati — reagisce semplicemente ai cambiamenti. Questo disaccoppia mittente e destinatario: si può aggiungere una nuova fonte dati senza modificare gli abbonati esistenti. EventBus e SharedFlow funzionano sullo stesso principio.

Il pattern Bridge separa l'astrazione dall'implementazione, permettendo loro di cambiare indipendentemente. Nello sviluppo mobile, Bridge viene usato, ad esempio, per i moduli dipendenti dalla piattaforma: un'interfaccia comune ImageLoader con diverse implementazioni per iOS (Kingfisher, Nuke) e Android (Glide, Coil). Il codice che lavora con ImageLoader non dipende dalla libreria scelta e può sostituirla semplicemente cambiando l'implementazione.

Dependency Injection come strumento di gestione del coupling

Dependency Injection (DI) è lo strumento più pratico per ridurre il coupling nello sviluppo mobile. Invece di creare le proprie dipendenze, un contenitore DI (Hilt, Koin, Dagger per Android; Swinject, Factory per iOS) le fornisce dall'esterno. La classe riceve le dipendenze tramite injection nel costruttore, metodo o proprietà, rimanendo all'oscuro delle implementazioni concrete.

La DI documenta esplicitamente le dipendenze di una classe: basta guardare il costruttore per capire con quali moduli la classe interagisce. Se il costruttore accetta 8 parametri da diversi livelli — questo è un segnale di coupling eccessivo che richiede refactoring. Buona pratica è non più di 3-4 dipendenze per classe. Un numero maggiore indica una violazione del Principio di Responsabilità Singola e un coupling eccessivo.

La DI semplifica anche i test: per ogni test si crea una classe con dipendenze mock, senza bisogno di database o rete reali. In Flutter la DI viene implementata tramite Provider, Riverpod o GetIt. Indipendentemente dal framework, l'obiettivo è uno: ridurre l'accoppiamento tra moduli rendendo le dipendenze esplicite e sostituibili. L'uso della DI in un progetto mobile è lo standard de facto dagli anni 2020.

swift
// Il container DI costruisce il grafo delle dipendenze
protocol AuthServiceProtocol {
    func login(email: String, password: String) async throws -> User
}

final class AuthService: AuthServiceProtocol {
    func login(email: String, password: String) async throws -> User {
        // implementazione
    }
}

// ViewModel non conosce il servizio specifico — solo il protocollo
final class LoginViewModel {
    private let auth: AuthServiceProtocol

    init(auth: AuthServiceProtocol) {
        self.auth = auth
    }
}

// DI Container è l'unico posto dove vengono creati i tipi concreti
final class DIContainer {
    lazy var authService: AuthServiceProtocol = AuthService()
    lazy var loginViewModel: LoginViewModel {
        LoginViewModel(auth: self.authService)
    }
}

Qui, LoginViewModel dipende solo dal protocollo AuthServiceProtocol, non da uno specifico AuthService. Sostituire l'implementazione (ad esempio, passare da Firebase Auth a un server personalizzato) richiede modifiche solo in DIContainer. Tutti i consumatori di AuthServiceProtocol rimangono intatti — il coupling è minimizzato attraverso astrazione e DI.

Domande Frequenti

In cosa differisce il coupling dalla coesione?

La coesione misura la coerenza interna di un modulo, mentre il coupling misura l'interconnessione esterna tra moduli. Una buona architettura cerca alta coesione e basso coupling. Queste metriche sono inversamente proporzionali: aumentare la coesione di solito riduce il coupling, e viceversa.

Quale tipo di coupling è accettabile nel codice di produzione?

Data e stamp sono normali e presenti in qualsiasi progetto. Il control coupling è accettabile in scenari limitati (ad esempio, pattern strategy). L'external coupling è inevitabile quando si lavora con API esterne, ma dovrebbe essere isolato dietro un livello di mappatura. Il common e il content coupling sono segni di problemi architetturali che richiedono refactoring immediato.

Come misurare il coupling in un progetto?

Strumenti di analisi statica: IntelliJ IDEA Dependency Matrix, Xcode Graph, rapporto dipendenze Gradle, SonarQube. Metriche: coupling afferente (Ca), coupling efferente (Ce), Instabilità (Ce/(Ca+Ce)). Un'alta Instabilità (vicina a 1) significa che il modulo è facile da modificare e poche cose lo referenziano — questo è buono.

Un basso coupling può essere dannoso?

Un coupling estremamente basso può significare un numero eccessivo di astrazioni e interfacce che complicano la navigazione nel codice. Se viene creata un'interfaccia separata per ogni classe, il programmatore perde tempo saltando tra i file. Equilibrio: interfacce per l'API esterna del modulo, ma non per ogni classe di supporto interna.

Come ridurre il coupling quando si lavora con codice legacy?

Usa la tecnica Strangler Fig — sostituisci gradualmente le chiamate dirette con interfacce. Inizia estraendo interfacce per le classi più referenziate. Poi introduci un contenitore DI. Copri il codice isolato con test di caratterizzazione per assicurarti che il refactoring non modifichi il comportamento del sistema.

Riepilogo

  • Coupling — metrica di dipendenza tra moduli: il basso accoppiamento è l'obiettivo di una buona architettura
  • Data coupling — il tipo migliore, content coupling — il peggiore, inaccettabile nel codice di produzione
  • Inversione delle Dipendenze e interfacce — i meccanismi principali per ridurre l'accoppiamento
  • Dependency Injection — strumento pratico che rende le dipendenze esplicite e sostituibili
  • Alto coupling rende il codice fragile: un cambiamento rompe molti moduli
  • Basso coupling semplifica i test: ogni modulo viene mockato indipendentemente senza emulatore
  • Bilancia tra coupling e astrazioni — interfacce eccessive complicano il codice

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