Devo usare un'interfaccia quando solo una classe la implementerà mai?


243

L'intero punto di un'interfaccia a cui più classi aderiscono a un insieme di regole e implementazioni?



16
O per rendere più semplice unittest.

11
Consentire a più classi di implementare le interfacce e far dipendere il codice dalle interfacce è ESSENZIALE per l'isolamento per i test delle unità. Se stai eseguendo un test unitario, avrai un'altra classe che implementa quell'interfaccia.
Stuper:

2
Discussione su Reddit su questa domanda.
yannis,

6
I campi e i metodi pubblici sono una "interfaccia" a sé stante. Se la mancanza di polimorfismo è intenzionalmente pianificata, allora non c'è motivo di usare un'interfaccia. L'unità che verifica altri citati è un uso pianificato del polimorfismo.
mike30,

Risposte:


205

A rigor di termini, no non lo fai, si applica YAGNI . Detto questo, il tempo che impiegherai a creare l'interfaccia è minimo, soprattutto se hai uno strumento di generazione del codice utile che fa la maggior parte del lavoro per te. Se non sei sicuro che tu abbia bisogno dell'interfaccia o meno, direi che è meglio sbagliare a sostegno della definizione di un'interfaccia.

Inoltre, l'uso di un'interfaccia anche per una singola classe ti fornirà un'altra implementazione finta per i test unitari, uno che non è in produzione. La risposta di Avner Shahar-Kashtan si espande su questo punto.


84
Il test +1 indica che hai quasi sempre due implementazioni
jk.

24
@YannisRizos Non sei d'accordo con quest'ultimo punto a causa di Yagni. Accendere un'interfaccia da metodi pubblici di una classe è banale dopo il fatto, così come sostituire CFoo con IFoo nelle classi consumanti. Non ha senso scriverlo prima della necessità.
Dan Neely,

5
Non sono ancora sicuro di seguire il tuo ragionamento. Dal momento che gli strumenti di generazione del codice rendono l'aggiunta dopo il fatto ancora più economica, vedo ancora meno motivi per creare l'interfaccia prima che tu ne abbia esplicita necessità.
Dan Neely,

6
Penso che un'interfaccia mancante non sia un buon esempio per YAGNI ma per una "finestra rotta" e documentazione mancante. Gli utenti della classe sono praticamente costretti a codificare contro l'implementazione, anziché l'astrazione come dovrebbero.
Fabio Fracassi,

10
Perché mai inquineresti il ​​tuo codebase con cruft insignificante solo per soddisfare un framework di test? Voglio dire sul serio, sono solo un ragazzo JavaScript autodidatta sul lato client che cerca di risolvere WTF è sbagliato con le implementazioni OOD sviluppatore C # e Java che continuo a incontrare nel mio tentativo di diventare più un generalista a tutto tondo, ma perché non Ti schiaffeggiano l'IDE e non ti permettono di riaverlo indietro finché non impari a scrivere codice pulito e leggibile quando ti sorprendono a fare quel genere di cose al college? È solo osceno.
Erik Reppen,

144

Risponderei che la necessità o meno di un'interfaccia non dipende da quante classi la implementeranno. Le interfacce sono uno strumento per la definizione di contratti tra più sottosistemi dell'applicazione; quindi ciò che conta davvero è come la tua applicazione è divisa in sottosistemi. Dovrebbero esserci interfacce come front-end per sottosistemi incapsulati, indipendentemente da quante classi li implementano.

Ecco una regola empirica molto utile:

  • Se fai in modo che la classe faccia Fooriferimento direttamente alla classe BarImpl, ti impegni fortemente a cambiare Fooogni volta che cambi BarImpl. In pratica, li stai trattando come un'unità di codice suddivisa in due classi.
  • Se fai Fooriferimento all'interfaccia Bar , ti impegni invece a evitare modifiche a Fooquando cambi BarImpl.

Se definisci le interfacce nei punti chiave della tua applicazione, rifletti attentamente sui metodi che dovrebbero supportare e quali non dovrebbero, e commenti chiaramente le interfacce per descrivere come dovrebbe comportarsi un'implementazione (e come no), l'applicazione sarà molto più facile da capire perché queste interfacce commentato forniranno una sorta di specificazione della domanda-una descrizione di come è destinato a comportarsi. Questo rende molto più facile leggere il codice (invece di chiedere "cosa diavolo dovrebbe fare questo codice" puoi chiedere "come fa questo codice a fare quello che dovrebbe fare").

Oltre a tutto ciò (o proprio per questo), le interfacce promuovono la compilazione separata. Poiché le interfacce sono banali da compilare e hanno meno dipendenze rispetto alle loro implementazioni, significa che se si scrive classe Fooper utilizzare un'interfaccia Bar, di solito è possibile ricompilare BarImplsenza dover ricompilare Foo. Nelle applicazioni di grandi dimensioni ciò può far risparmiare molto tempo.


14
Voterei molto più di una volta, se potessi. IMO la migliore risposta a questa domanda.
Fabio Fracassi,

4
Se la classe sta svolgendo solo un ruolo (cioè avrebbe solo un'interfaccia definita per essa), allora perché non farlo tramite metodi pubblici / privati?
Matthew Finlay,

4
1. Organizzazione del codice; avere l'interfaccia nel proprio file che ha solo firme e commenti sulla documentazione aiuta a mantenere pulito il codice. 2. Quadri che ti costringono ad esporre metodi che preferiresti mantenere privati ​​(ad esempio, contenitori che iniettano dipendenze attraverso setter pubblici). 3. Raccolta separata, come già detto; se la classe Foodipende interfaccia Barquindi BarImplpuò essere modificato senza dover ricompilare Foo. 4. Controllo degli accessi più preciso rispetto alle offerte pubbliche / private (esporre la stessa classe a due client attraverso interfacce diverse).
sacundim,

3
E l'ultimo: 5. I clienti (idealmente) non dovrebbero preoccuparsi di quante classi ha il mio modulo o quante, o addirittura di essere in grado di dire. Tutto ciò che dovrebbero vedere è un insieme di tipi e alcune fabbriche o facciate per far rotolare la palla. Vale a dire, vedo spesso valore nell'incapsulare anche in quali classi è composta la tua libreria.
sacundim

4
@sacundim If you make class Foo refer directly to class BarImpl, you're strongly committing yourself to change Foo every time you change BarImplQuando si cambia BarImpl, quali sono le modifiche che è possibile evitare in Foo utilizzando interface Bar? Dal momento che le firme e le funzionalità di un metodo non cambiano in BarImpl, Foo non richiederà cambiamenti anche senza interfaccia (l'intero scopo dell'interfaccia). Sto parlando dello scenario in cui solo una classe, ad esempio, BarImplimplementa Bar. Per uno scenario multi-classe, capisco il principio di inversione di dipendenza e come l'interfaccia è utile.
Shishir Gupta,

101

Le interfacce sono designate per definire un comportamento, ovvero un insieme di prototipi di funzioni / metodi. I tipi che implementano l'interfaccia implementeranno quel comportamento, quindi quando hai a che fare con un tale tipo sai (parzialmente) quale comportamento ha.

Non è necessario definire un'interfaccia se si sa che il comportamento da essa definito verrà utilizzato una sola volta. BACIO (mantienilo semplice, stupido)


Non sempre, è possibile utilizzare l'interfaccia in una classe se quella classe è una classe generica.
Giovanni Isaia Carmona,

Inoltre, è possibile introdurre un'interfaccia quando finalmente necessaria facilmente nell'IDE moderno con un refactoring "estrae l'interfaccia".
Hans-Peter Störr,

Prendi in considerazione una classe di utilità in cui esistono solo metodi statici pubblici e un costruttore privato - perfettamente accettabili e promossi da autori come Joshua Bloch et al.
Darrell Teague,

63

Mentre in teoria non dovresti avere un'interfaccia solo per amor di un'interfaccia, la risposta di Yannis Rizos suggerisce ulteriori complicazioni:

Quando si scrivono unit test e si usano framework simulati come Moq o FakeItEasy (per nominare i due più recenti che ho usato), si crea implicitamente un'altra classe che implementa l'interfaccia. La ricerca del codice o l'analisi statica potrebbe affermare che esiste solo un'implementazione, ma in realtà c'è l'implementazione fittizia interna. Ogni volta che inizi a scrivere simulazioni, scoprirai che estrarre interfacce ha senso.

Ma aspetta, c'è di più. Esistono più scenari in cui esistono implementazioni di interfaccia implicite. L'uso dello stack di comunicazione WCF di .NET, ad esempio, genera un proxy per un servizio remoto che, di nuovo, implementa l'interfaccia.

In un ambiente di codice pulito, sono d'accordo con il resto delle risposte qui. Tuttavia, presta attenzione a eventuali framework, schemi o dipendenze che potrebbero utilizzare interfacce.


2
+1: Non sono sicuro che YAGNI si applichi qui, poiché avere un'interfaccia e l'utilizzo di framework (ad esempio JMock, ecc.) Che utilizzano interfacce può effettivamente farti risparmiare tempo.
Deco

4
@Deco: buoni framework di derisione (tra cui JMock) non hanno nemmeno bisogno di interfacce.
Michael Borgwardt,

12
Creare un'interfaccia solo a causa di una limitazione del tuo framework beffardo mi sembra una terribile ragione. Usare, ad esempio, EasyMock beffardo di una classe è comunque facile come un'interfaccia.
Alb

8
Direi che è il contrario. Usare oggetti finti nei test significa, per definizione, creare implementazioni alternative per le tue interfacce. Questo è vero sia che tu crei le tue classi FakeImplementation o lasci che i framework di derisione facciano il lavoro pesante per te. Ci possono essere alcuni framework, come EasyMock, che usano vari hack e trucchi di basso livello per deridere classi concrete - e più potere per loro! - ma concettualmente, gli oggetti finti sono implementazioni alternative a un contratto.
Avner Shahar-Kashtan,

1
Non stai licenziando i test in produzione. Perché un finto per una classe che non ha bisogno di un'interfaccia, ha bisogno di un'interfaccia?
Erik Reppen,

33

No, non ne hai bisogno e lo considero un anti-pattern per creare automaticamente interfacce per ogni riferimento di classe.

La realizzazione di Foo / FooImpl comporta costi reali per tutto. L'IDE può creare l'interfaccia / implementazione gratuitamente, ma quando si naviga nel codice, si ha il carico cognitivo aggiuntivo da F3 / F12 nel foo.doSomething()portarsi alla firma dell'interfaccia e non all'implementazione effettiva desiderata. Inoltre hai il disordine di due file invece di uno per tutto.

Quindi dovresti farlo solo quando ne hai davvero bisogno per qualcosa.

Affrontando ora i controargomenti:

Ho bisogno di interfacce per i framework di iniezione delle dipendenze

Le interfacce per supportare i framework sono eredità. In Java, le interfacce erano un requisito per i proxy dinamici, pre-CGLIB. Oggi di solito non ne hai bisogno. È considerato un progresso e un vantaggio per la produttività degli sviluppatori che non ti servono più in EJB3, Spring, ecc.

Ho bisogno di beffe per i test unitari

Se scrivi le tue beffe e hai due implementazioni effettive, allora un'interfaccia è appropriata. Probabilmente non avremmo questa discussione in primo luogo se la tua base di codice avesse sia un FooImpl che un TestFoo.

Ma se stai usando un framework beffardo come Moq, EasyMock o Mockito, puoi deridere le classi e non hai bisogno di interfacce. È analogo all'impostazione foo.method = mockImplementationin un linguaggio dinamico in cui è possibile assegnare metodi.

Abbiamo bisogno di interfacce per seguire il principio di inversione di dipendenza (DIP)

DIP afferma che costruisci per dipendere da contratti (interfacce) e non da implementazioni. Ma una classe è già un contratto e un'astrazione. Ecco a cosa servono le parole chiave pubbliche / private. All'università, l'esempio canonico era qualcosa di simile a una classe Matrix o Polinomiale: i consumatori hanno un'API pubblica per creare matrici, aggiungerle ecc., Ma non possono preoccuparsi se la matrice è implementata in forma sparsa o densa. Non era richiesto IMatrix o MatrixImpl per dimostrare tale punto.

Inoltre, DIP è spesso applicato in modo eccessivo a tutti i livelli di chiamata di classe / metodo, non solo ai principali limiti del modulo. Un segno che stai applicando eccessivamente DIP è che l'interfaccia e l'implementazione cambiano in fase di blocco in modo tale che devi toccare due file per apportare una modifica anziché una. Se DIP viene applicato in modo appropriato, significa che la tua interfaccia non dovrebbe cambiare spesso. Inoltre, un altro segno è che la tua interfaccia ha solo un consumatore reale (la sua stessa applicazione). Storia diversa se stai costruendo una libreria di classi per il consumo in molte app diverse.

Questo è un corollario del punto di zio Bob Martin sul deridere: dovresti solo deridere i principali confini architettonici. In una webapp l'accesso HTTP e DB sono i limiti principali. Tutte le chiamate di classe / metodo non lo sono. Lo stesso vale per DIP.

Guarda anche:


4
Le classi beffardo piuttosto che le interfacce (almeno in Java e C #) dovrebbero essere considerate deprecate, in quanto non esiste alcun modo per impedire l'esecuzione del costruttore della superclasse, che può far sì che l'oggetto simulato interagisca con l'ambiente in modo non intenzionale. Deridere un'interfaccia è più sicuro e più facile perché non devi pensare al codice del costruttore.
Jules il

4
Non ho riscontrato problemi con le classi beffardo, ma sono stato frustrato dalla navigazione IDE che non funziona come previsto. Risolvere un problema reale batte ipotetico.
wrschneider,

1
@Jules Puoi deridere una classe concreta incluso il suo costruttore in Java.
Assylias,

1
@assylias come si impedisce al costruttore di funzionare?
Jules,

2
@Jules Dipende dal tuo framework beffardo - con jmockit per esempio, puoi semplicemente scrivere new Mockup<YourClass>() {}e l'intera classe, compresi i suoi costruttori, viene derisa, che sia un'interfaccia, una classe astratta o una classe concreta. Puoi anche "sovrascrivere" il comportamento del costruttore se lo desideri. Suppongo che ci siano modi equivalenti in Mockito o Powermock.
Assylias,

22

Sembra che le risposte su entrambi i lati della recinzione possano essere riassunte in questo:

Progetta bene e metti interfacce dove sono necessarie interfacce.

Come ho notato nella mia risposta alla risposta di Yanni , non penso che tu possa mai avere una regola rigida e veloce sulle interfacce. La regola deve essere, per definizione, flessibile. La mia regola sulle interfacce è che un'interfaccia dovrebbe essere usata ovunque tu stia creando un'API. E un'API dovrebbe essere creata ovunque tu attraversi il confine da un dominio di responsabilità a un altro.

Per un esempio (orribilmente inventato), supponiamo che tu stia costruendo una Carclasse. Nella tua classe, avrai sicuramente bisogno di un livello UI. In questo particolare esempio, prende la forma di un IginitionSwitch, SteeringWheel, GearShift, GasPedal, e BrakePedal. Poiché questa macchina contiene un AutomaticTransmission, non è necessario un ClutchPedal. (E dal momento che questa è una macchina terribile, non c'è A / C, radio o sedile. È un dato di fatto, anche le assi del pavimento mancano - devi solo aggrapparti a quel volante e sperare per il meglio!)

Quindi quale di queste classi ha bisogno di un'interfaccia? La risposta potrebbe essere tutta, o nessuna, a seconda del progetto.

Potresti avere un'interfaccia simile a questa:

Interface ICabin
    Event IgnitionSwitchTurnedOn()
    Event IgnitionSwitchTurnedOff()
    Event BrakePedalPositionChanged(int percent)
    Event GasPedalPositionChanged(int percent)
    Event GearShiftGearChanged(int gearNum)
    Event SteeringWheelTurned(float degree)
End Interface

A quel punto, il comportamento di quelle classi diventa parte dell'ICabin Interface / API. In questo esempio le classi (se ce ne sono alcune) sono probabilmente semplici, con poche proprietà e una funzione o due. E ciò che stai implicitamente affermando con il tuo design è che queste classi esistono solo per supportare qualsiasi implementazione concreta di ICabin che hai, e non possono esistere da sole, o sono insignificanti al di fuori del contesto ICabin.

È lo stesso motivo per cui non testate i membri privati ​​delle unità: esistono solo per supportare l'API pubblica e quindi il loro comportamento dovrebbe essere testato testando l'API.

Quindi, se la tua classe esiste solo per supportare un'altra classe, e concettualmente la vedi come se non avesse davvero il proprio dominio, allora va bene saltare l'interfaccia. Ma se la tua classe è abbastanza importante da considerarla abbastanza grande da avere il proprio dominio, allora vai avanti e dagli un'interfaccia.


MODIFICARE:

Spesso (incluso in questa risposta) leggerai cose come "dominio", "dipendenza" (spesso associato a "iniezione") che non significano nulla per te quando inizi a programmare (sicuramente non significhi qualcosa per me). Per dominio, significa esattamente come suona:

Il territorio su cui viene esercitato il dominio o l'autorità; i possedimenti di un sovrano o di un commonwealth, o simili. Utilizzato anche in senso figurato. [WordNet sense 2] [1913 Webster]

Nei termini del mio esempio, consideriamo il IgnitionSwitch. In un'auto spaziale, l'interruttore di accensione è responsabile di:

  1. Autenticare (non identificare) l'utente (hanno bisogno della chiave corretta)
  2. Fornire corrente all'avviatore in modo che possa effettivamente avviare l'auto
  3. Fornire corrente al sistema di accensione in modo che possa continuare a funzionare
  4. Spegnere la corrente in modo che l'auto si fermi.
  5. A seconda di come la vedi, nella maggior parte (tutte?) Delle auto più recenti c'è un interruttore che impedisce alla chiave di essere rimossa dall'accensione mentre la trasmissione è fuori parcheggio, quindi questo potrebbe far parte del suo dominio. (In realtà significa che devo ripensare e riprogettare il mio sistema ...)

Queste proprietà costituiscono il dominio del IgnitionSwitch, o in altre parole, ciò che conosce ed è responsabile.

Il IgnitionSwitchnon è responsabile per il GasPedal. L'interruttore di accensione ignora completamente il pedale del gas in ogni modo. Entrambi funzionano in modo completamente indipendente l'uno dall'altro (anche se un'auto sarebbe abbastanza inutile senza entrambi!).

Come ho affermato in origine, dipende dal tuo design. È possibile progettare un con IgnitionSwitchdue valori: On ( True) e Off ( False). Oppure potresti progettarlo per autenticare la chiave fornita e una miriade di altre azioni. Questa è la parte difficile dell'essere uno sviluppatore nel decidere dove tracciare le linee nella sabbia - e onestamente il più delle volte è completamente relativo. Quelle linee nella sabbia sono tuttavia importanti: è lì che si trova la tua API e quindi dove dovrebbero essere le tue interfacce.


Potresti per favore approfondire di più su cosa intendi per "avere il proprio dominio"?
Lamin Sanneh,

1
@LaminSanneh, elaborato. Questo aiuta?
Wayne Werner,

8

No (YAGNI) , a meno che tu non stia pianificando di scrivere test per altre classi usando questa interfaccia, e quei test trarrebbero beneficio dal deridere l'interfaccia.


8

Da MSDN :

Le interfacce sono più adatte alle situazioni in cui le applicazioni richiedono molti tipi di oggetti possibilmente non correlati per fornire determinate funzionalità.

Le interfacce sono più flessibili delle classi di base perché è possibile definire un'unica implementazione in grado di implementare più interfacce.

Le interfacce sono migliori in situazioni in cui non è necessario ereditare l'implementazione da una classe base.

Le interfacce sono utili nei casi in cui non è possibile utilizzare l'ereditarietà delle classi. Ad esempio, le strutture non possono ereditare dalle classi, ma possono implementare interfacce.

Generalmente nel caso di una singola classe, non sarà necessario implementare un'interfaccia, ma considerando il futuro del tuo progetto, potrebbe essere utile definire formalmente il comportamento necessario delle classi.


5

Per rispondere alla domanda: c'è di più.

Un aspetto importante di un'interfaccia è l'intento.

Un'interfaccia è "un tipo astratto che non contiene dati, ma espone comportamenti" - Interfaccia (informatica) Quindi se questo è un comportamento, o un insieme di comportamenti, che la classe supporta, allora un'interfaccia è probabilmente il modello corretto. Se, tuttavia, il comportamento (i) è (sono) intrinseco al concetto incarnato dalla classe, allora probabilmente non vuoi affatto un'interfaccia.

La prima domanda da porsi è qual è la natura della cosa o del processo che stai cercando di rappresentare. Quindi continua con le ragioni pratiche per attuare quella natura in un determinato modo.


5

Da quando hai posto questa domanda, suppongo che tu abbia già visto gli interessi di avere un'interfaccia che nasconde più implementazioni. Ciò può essere manifestato dal principio di inversione di dipendenza.

Tuttavia, la necessità di avere un'interfaccia o meno non dipende dal numero delle sue implementazioni. Il vero ruolo di un'interfaccia è che definisce un contratto indicando quale servizio dovrebbe essere fornito invece di come dovrebbe essere implementato.

Una volta definito il contratto, due o più team possono lavorare in modo indipendente. Supponiamo che tu stia lavorando su un modulo A e dipende dal modulo B, il fatto di creare un'interfaccia su B consente di continuare il tuo lavoro senza preoccuparti dell'implementazione di B perché tutti i dettagli sono nascosti dall'interfaccia. Pertanto, la programmazione distribuita diventa possibile.

Anche se il modulo B ha una sola implementazione della sua interfaccia, l'interfaccia è ancora necessaria.

In conclusione, un'interfaccia nasconde i dettagli di implementazione dai suoi utenti. La programmazione per l'interfaccia aiuta a scrivere più documenti perché è necessario definire un contratto, scrivere software più modulare, promuovere test unitari e accelerare la velocità di sviluppo.


2
Ogni classe può avere un'interfaccia pubblica (i metodi pubblici) e un'interfaccia privata (i dettagli dell'implementazione). Potrei sostenere che l'interfaccia pubblica di classe è il contratto. Non hai bisogno di un ulteriore elemento per lavorare a quel contratto.
Fuhrmanator,

4

Tutte le risposte qui sono molto buone. In effetti, la maggior parte delle volte non è necessario implementare un'interfaccia diversa. Ma ci sono casi in cui si potrebbe desiderare di farlo comunque. Ecco qualche caso in cui lo faccio:

La classe implementa un'altra interfaccia che non voglio esporre
Happen spesso con la classe adattatore che collega il codice di terze parti.

interface NameChangeListener { // Implemented by a lot of people
    void nameChanged(String name); 
} 

interface NameChangeCount { // Only implemented by my class
    int getCount();
}

class NameChangeCounter implements NameChangeListener, NameChangeCount {
    ...
}

class SomeUserInterface {
    private NameChangeCount currentCount; // Will never know that you can change the counter
}

La classe usa una tecnologia specifica che non dovrebbe trapelare
principalmente quando interagisce con librerie esterne. Anche se esiste una sola implementazione, utilizzo un'interfaccia per assicurarmi di non introdurre accoppiamenti non necessari con la libreria esterna.

interface SomeRepository { // Guarantee that the external library details won't leak trough
    ...
}

class OracleSomeRepository implements SomeRepository { 
    ... // Oracle prefix allow us to quickly know what is going on in this class
}

Comunicazione a più livelli
Anche se solo una classe UI implementa mai una della classe di dominio, consente una migliore separazione tra tali livelli e, soprattutto, evita la dipendenza ciclica.

package project.domain;

interface UserRequestSource {
    public UserRequest getLastRequest();
}

class UserBehaviorAnalyser {
    private UserRequestSource requestSource;
}

package project.ui;

class OrderCompleteDialog extends SomeUIClass implements project.domain.UserRequestSource {
    // UI concern, no need for my domain object to know about this method.
    public void displayLabelInErrorMode(); 

    // They most certainly need to know about *that* though
    public UserRequest getLastRequest();
}

Per la maggior parte degli oggetti dovrebbe essere disponibile solo un sottoinsieme del metodo.
Principalmente accade quando ho un metodo di configurazione sulla classe concreta

interface Sender {
    void sendMessage(Message message)
}

class PacketSender implements Sender {
    void sendMessage(Message message);
    void setPacketSize(int sizeInByte);
}

class Throttler { // This class need to have full access to the object
    private PacketSender sender;

    public useLowNetworkUsageMode() {
        sender.setPacketSize(LOW_PACKET_SIZE);
        sender.sendMessage(new NotifyLowNetworkUsageMessage());

        ... // Other details
    }
}

class MailOrder { // Not this one though
    private Sender sender;
}

Quindi alla fine uso l'interfaccia per lo stesso motivo per cui utilizzo il campo privato: altri oggetti non dovrebbero avere accesso a cose a cui non dovrebbero accedere. Se ho un caso del genere, presento un'interfaccia anche se solo una classe la implementa.


2

Le interfacce sono davvero importanti, ma prova a controllarne il numero.

Avendo intrapreso la strada della creazione di interfacce per quasi tutto, è facile finire con il codice degli "spaghetti tritati". Rinvio alla maggiore saggezza di Ayende Rahien che ha pubblicato alcune parole molto sagge sull'argomento:

http://ayende.com/blog/153889/limit-your-abstractions-analyzing-a-ddd-application

Questo è il suo primo post di un'intera serie, quindi continua a leggere!


Il codice 'spaghetti tagliati' è anche noto come codice ravioli c2.com/cgi/wiki?RavioliCode
CurtainDog

Suona più come il codice lasagne o il codice baklava per me - codice con troppi livelli. ;-)
dodgy_coder

2

Uno dei motivi per cui potresti voler introdurre un'interfaccia in questo caso è seguire il principio di inversione di dipendenza . Cioè, il modulo che utilizza la classe dipenderà da un'astrazione di essa (cioè l'interfaccia) anziché da un'implementazione concreta. Disaccoppia i componenti di alto livello dai componenti di basso livello.


2

Non c'è alcun motivo reale per fare qualcosa. Le interfacce servono per aiutarti e non per il programma di output. Quindi, anche se l'interfaccia è implementata da un milione di classi, non esiste una regola che dice che devi crearne una. Ne crei uno in modo che quando tu o chiunque altro usi il tuo codice, desideri cambiare qualcosa che percola a tutte le implementazioni. La creazione di un'interfaccia ti aiuterà in tutti i casi futuri in cui potresti voler creare un'altra classe che la implementa.


1

Non è sempre necessario definire un'interfaccia per una classe.

Oggetti semplici come oggetti valore non hanno implementazioni multiple. Non hanno nemmeno bisogno di essere derisi. L'implementazione può essere testata da sola e quando vengono testate altre classi che dipendono da esse, è possibile utilizzare l'oggetto valore reale.

Ricorda che la creazione di un'interfaccia ha un costo. Deve essere aggiornato lungo l'implementazione, ha bisogno di un file aggiuntivo e alcuni IDE avranno problemi di zoom dell'implementazione, non dell'interfaccia.

Quindi definirei interfacce solo per classi di livello superiore, dove desideri un'astrazione dall'implementazione.

Nota che con una classe ottieni un'interfaccia gratis. Oltre all'implementazione, una classe definisce un'interfaccia dall'insieme di metodi pubblici. Tale interfaccia è implementata da tutte le classi derivate. Non si tratta strettamente di un'interfaccia, ma può essere utilizzata esattamente allo stesso modo. Quindi non penso che sia necessario ricreare un'interfaccia che esiste già sotto il nome della classe.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.