Principio di segregazione dell'interfaccia in Java

1. Introduzione

In questo tutorial, discuteremo il principio di segregazione dell'interfaccia, uno dei principi SOLID. Rappresentando la "I" in "SOLID", la segregazione dell'interfaccia significa semplicemente che dovremmo suddividere le interfacce più grandi in interfacce più piccole.

Assicurando così che le classi di implementazione non debbano implementare metodi indesiderati.

2. Principio di segregazione dell'interfaccia

Questo principio è stato definito per la prima volta da Robert C. Martin come: " I clienti non dovrebbero essere costretti a dipendere da interfacce che non utilizzano ".

L'obiettivo di questo principio è ridurre gli effetti collaterali dell'utilizzo di interfacce più grandi suddividendo le interfacce dell'applicazione in interfacce più piccole . È simile al principio di responsabilità unica, in cui ogni classe o interfaccia ha un unico scopo.

La progettazione precisa dell'applicazione e l'astrazione corretta sono la chiave alla base del principio di segregazione dell'interfaccia. Sebbene richiederà più tempo e impegno nella fase di progettazione di un'applicazione e potrebbe aumentare la complessità del codice, alla fine otteniamo un codice flessibile.

Esamineremo alcuni esempi nelle sezioni successive in cui abbiamo una violazione del principio, quindi risolveremo il problema applicando correttamente il principio.

3. Esempio di interfaccia e implementazione

Diamo un'occhiata a una situazione in cui abbiamo un'interfaccia di pagamento utilizzata da un'implementazione BankPayment :

public interface Payment { void initiatePayments(); Object status(); List getPayments(); }

E l'implementazione:

public class BankPayment implements Payment { @Override public void initiatePayments() { // ... } @Override public Object status() { // ... } @Override public List getPayments() { // ... } }

Per semplicità, ignoriamo l'effettiva implementazione aziendale di questi metodi.

Questo è molto chiaro: finora, la classe di implementazione BankPayment necessita di tutti i metodi nell'interfaccia di pagamento . Quindi, non viola il principio.

4. Inquinamento dell'interfaccia

Ora, mentre andiamo avanti nel tempo e nuove funzionalità sono disponibili, è necessario aggiungere un servizio di pagamento del prestito . Anche questo servizio è una sorta di pagamento ma ha alcune operazioni in più.

Per sviluppare questa nuova funzionalità, aggiungeremo i nuovi metodi all'interfaccia di pagamento :

public interface Payment { // original methods ... void intiateLoanSettlement(); void initiateRePayment(); }

Successivamente, avremo l' implementazione LoanPayment :

public class LoanPayment implements Payment { @Override public void initiatePayments() { throw new UnsupportedOperationException("This is not a bank payment"); } @Override public Object status() { // ... } @Override public List getPayments() { // ... } @Override public void intiateLoanSettlement() { // ... } @Override public void initiateRePayment() { // ... } }

Ora, poiché l' interfaccia di pagamento è cambiata e sono stati aggiunti più metodi, tutte le classi di implementazione ora devono implementare i nuovi metodi. Il problema è che la loro implementazione è indesiderata e potrebbe portare a molti effetti collaterali. Qui, la classe di implementazione LoanPayment deve implementare initiatePayments () senza alcuna effettiva necessità di ciò. E così, il principio è violato.

Allora, cosa succede alla nostra classe BankPayment :

public class BankPayment implements Payment { @Override public void initiatePayments() { // ... } @Override public Object status() { // ... } @Override public List getPayments() { // ... } @Override public void intiateLoanSettlement() { throw new UnsupportedOperationException("This is not a loan payment"); } @Override public void initiateRePayment() { throw new UnsupportedOperationException("This is not a loan payment"); } }

Si noti che l' implementazione BankPayment ora ha implementato i nuovi metodi. E poiché non ne ha bisogno e non ha logica per loro, sta solo generando un'eccezione UnsupportedOperationException . È qui che iniziamo a violare il principio.

Nella prossima sezione vedremo come risolvere questo problema.

5. Applicazione del principio

Nell'ultima sezione abbiamo intenzionalmente inquinato l'interfaccia e violato il principio. In questa sezione vedremo come aggiungere la nuova funzionalità per il pagamento del prestito senza violare il principio.

Analizziamo l'interfaccia per ogni tipo di pagamento. La situazione attuale:

Notare nel diagramma delle classi, e facendo riferimento alle interfacce nella sezione precedente, che i metodi status () e getPayments () sono richiesti in entrambe le implementazioni. D'altra parte, initiatePayments () è richiesto solo in BankPayment e i metodi initiateLoanSettlement () e initiateRePayment () sono solo per LoanPayment .

Con ciò ordinato, suddividiamo le interfacce e applichiamo il principio di segregazione dell'interfaccia. Quindi, ora abbiamo un'interfaccia comune:

public interface Payment { Object status(); List getPayments(); }

E altre due interfacce per i due tipi di pagamento:

public interface Bank extends Payment { void initiatePayments(); }
public interface Loan extends Payment { void intiateLoanSettlement(); void initiateRePayment(); }

E le rispettive implementazioni, a partire da BankPayment :

public class BankPayment implements Bank { @Override public void initiatePayments() { // ... } @Override public Object status() { // ... } @Override public List getPayments() { // ... } }

E infine, la nostra implementazione rivista del prestito :

public class LoanPayment implements Loan { @Override public void intiateLoanSettlement() { // ... } @Override public void initiateRePayment() { // ... } @Override public Object status() { // ... } @Override public List getPayments() { // ... } }

Ora, esaminiamo il nuovo diagramma delle classi:

Come possiamo vedere, le interfacce non violano il principio. Le implementazioni non devono fornire metodi vuoti. Ciò mantiene il codice pulito e riduce la possibilità di bug.

6. Conclusione

In questo tutorial, abbiamo esaminato uno scenario semplice, in cui prima abbiamo deviato dal seguire il principio di segregazione dell'interfaccia e abbiamo visto i problemi causati da questa deviazione. Poi abbiamo mostrato come applicare correttamente il principio per evitare questi problemi.

Nel caso in cui abbiamo a che fare con interfacce legacy inquinate che non possiamo modificare, il pattern dell'adattatore può tornare utile.

Il principio di segregazione dell'interfaccia è un concetto importante durante la progettazione e lo sviluppo delle applicazioni. L'adesione a questo principio aiuta a evitare interfacce gonfie con responsabilità multiple. Questo alla fine ci aiuta a seguire anche il Principio di responsabilità unica.

Come sempre, il codice è disponibile su GitHub.