Esiste un principio dell'interfaccia "chiedi solo ciò che ti serve"?

9

Sono cresciuto usando un principio per progettare e consumare interfacce che dice fondamentalmente "chiedi solo ciò di cui hai bisogno".

Ad esempio, se ho un gruppo di tipi che possono essere eliminati, creerò un'interfaccia Deletable :

interface Deletable {
   void delete();
}

Quindi posso scrivere una classe generica:

class Deleter<T extends Deletable> {
   void delete(T t) {
      t.delete();
   }
}

Altrove nel codice chiederò sempre la minima responsabilità possibile per soddisfare le esigenze del codice cliente. Quindi, se ho solo bisogno di cancellare un File , chiedo comunque un Deletable , non un File .

Questo principio è di conoscenza comune e ha già un nome accettato? È controverso? È discusso nei libri di testo?

    
posta glenviewjeff 25.08.2012 - 22:43
fonte

2 risposte

16

Credo che ciò si riferisca a ciò che Robert Martin chiama il principio di segregazione dell'interfaccia . Le interfacce sono separate in piccole e concise in modo che i consumatori (clienti) debbano solo conoscere i metodi che sono di loro interesse. Puoi controllare di più su SOLID .

    
risposta data 26.08.2012 - 00:08
fonte
4

Per espandere l'ottima risposta di Vadim, risponderò alla domanda "è controversa" con "no, non proprio".

In generale, la segregazione dell'interfaccia è una buona cosa, riducendo il numero complessivo di "ragioni per cambiare" dei vari oggetti coinvolti. Il principio fondamentale è che quando un'interfaccia con più metodi deve essere cambiata, ad esempio per aggiungere un parametro a uno dei metodi dell'interfaccia, tutti i consumatori dell'interfaccia devono essere ricompilati, anche se non hanno usato il metodo che è cambiato . "Ma è solo una ricompilazione!", Ti sento dire; potrebbe essere vero, ma tieni a mente che in genere qualsiasi cosa che ricomponi deve essere eliminata come parte di una patch software, indipendentemente da quanto sia significativo il passaggio al file binario. Queste regole sono state originariamente concettualizzate nei primi anni '90, quando la workstation desktop media era meno potente del telefono in tasca, il dial-up a 14.4k baud era blazin ', e 3.5 "1.44MB" floppies "erano i media rimovibili primari. Anche nell'era attuale del 3G / 4G, gli utenti di Internet wireless hanno spesso piani dati con limiti, quindi quando si rilascia un aggiornamento, meno binari devono essere scaricati, meglio è.

Tuttavia, come tutte le buone idee, la segregazione dell'interfaccia può andare male se implementata in modo non corretto. Prima di tutto, c'è la possibilità che, separando le interfacce mantenendo l'oggetto che implementa tali interfacce (adempiendo alle dipendenze) relativamente invariato, si possa finire con un "Hydra", un parente dell'anti-pattern "Oggetto Dio" in cui il la natura onnisciente e onnipotente dell'oggetto è nascosta dal dipendente dalle interfacce strette. Finisci con un mostro dalle molte teste che è tanto difficile da mantenere quanto l'oggetto di Dio, oltre al sovraccarico di mantenere tutte le sue interfacce. Non esiste un numero elevato di interfacce che non dovresti superare, ma ogni interfaccia che implementerai su un singolo oggetto dovrebbe essere preceduta rispondendo alla domanda: "Questa interfaccia contribuisce allo scopo del core singolo e ristretto dell'oggetto, o lo fa? dare all'oggetto un nuovo scopo? ". Se è il primo, implementa via; se è il secondo, dividi l'oggetto in due per mantenere il principio di responsabilità singola.

Secondo, un'interfaccia per metodo potrebbe non essere necessaria, nonostante ciò che SRP potrebbe dirti. Si può finire con "codice ravioli"; così tanti pezzetti che sono difficili da rintracciare per scoprire esattamente dove accadono le cose. Non è inoltre necessario dividere un'interfaccia con due metodi se tutti gli utenti correnti di quell'interfaccia necessitano entrambi i metodi. Anche se una delle classi dipendenti ha bisogno solo di uno dei due metodi, è generalmente accettabile non dividere l'interfaccia se i suoi metodi concettualmente hanno una coesione molto alta (i buoni esempi sono "metodi antonimi" che sono opposti l'uno dell'altro).

La segregazione dell'interfaccia dovrebbe essere basata sulle classi che dipendono dall'interfaccia:

  • Se c'è solo una classe dipendente dall'interfaccia, non segregare. Se la classe non utilizza uno o più metodi di interfaccia ed è l'unico consumatore dell'interfaccia, le probabilità sono che non dovresti aver esposto quei metodi in primo luogo.

  • Se esiste più di una classe che dipende dall'interfaccia e tutti i dipendenti utilizzano tutti i metodi dell'interfaccia, non eseguire la segregazione; se è necessario modificare l'interfaccia (per aggiungere un metodo o modificare una firma), tutti i consumatori attuali saranno interessati dal cambiamento se si segrega o meno (anche se si sta aggiungendo un metodo che almeno un dipendente non avrà bisogno, considerare attentamente se la modifica dovrebbe invece essere implementata come una nuova interfaccia, possibilmente ereditando da quella esistente).

  • Se c'è più di una classe dipendente dall'interfaccia, e fanno non usano tutti gli stessi metodi, è un candidato per la segregazione. Guarda la "coerenza" dell'interfaccia; fare tutti i metodi ulteriormente un unico obiettivo di programmazione molto specifico? Se è possibile identificare più di uno scopo principale per l'interfaccia (e i relativi implementatori), si consiglia di suddividere le interfacce lungo tali linee per creare interfacce più piccole con meno "motivi per cambiare".

risposta data 27.08.2012 - 18:05
fonte