Dichiarare l'interfaccia nello stesso file della classe base, è una buona pratica?

28

Per essere intercambiabili e testabili, normalmente i servizi con logica devono avere un'interfaccia, per esempio

public class FooService: IFooService 
{ ... }

Dal punto di vista del design, sono d'accordo con questo, ma una delle cose che mi infastidiscono con questo approccio è che per un servizio dovrai dichiarare due cose (la classe e l'interfaccia), e nel nostro team, normalmente due file (uno per la classe e uno per l'interfaccia). Un altro disagio è la difficoltà nella navigazione perché l'uso di "Vai alla definizione" in IDE (VS2010) punterà all'interfaccia (poiché le altre classi si riferiscono all'interfaccia), non alla classe effettiva.

Stavo pensando che scrivere IFooService nello stesso file di FooService ridurrà la stranezza di cui sopra. Dopotutto, IFooService e FooService sono molto correlati. È una buona pratica? C'è una buona ragione per cui IFooService deve trovarsi nel proprio file?

    
posta Louis Rhys 11.09.2012 - 13:33
fonte

11 risposte

24

Non deve essere nel suo stesso file, ma la tua squadra dovrebbe decidere su uno standard e attenersi ad esso.

Inoltre, hai ragione che "Vai alla definizione" ti porta all'interfaccia, ma se hai Resharper installato , è solo un clic per aprire un elenco di classi / interfacce derivate da tale interfaccia, quindi non è un grosso problema. Ecco perché tengo l'interfaccia in un file separato.

    
risposta data 11.09.2012 - 13:39
fonte
15

Penso che dovresti tenerli separati. Come hai detto, l'idea è di rimanere testabile e intercambiabile. Inserendo l'interfaccia nello stesso file dell'implementazione, si associa l'interfaccia con l'implementazione specifica. Se decidi di creare un oggetto fittizio o un'altra implementazione, l'interfaccia non sarà separata logicamente da FooService.

    
risposta data 11.09.2012 - 14:47
fonte
8

Perché avere file separati è un disagio? Per me è molto più ordinato e pulito. È comune creare una sottocartella denominata "Interfacce" e incollare lì i file IFooServer.cs, se si desidera visualizzare un numero inferiore di file in Solution Explorer.

Il motivo per cui l'interfaccia è definita nel suo stesso file è per la stessa ragione per cui le classi vengono solitamente definite nel loro file: la gestione del progetto è più semplice quando la struttura logica e la struttura del file sono identiche, quindi sai sempre quale file è un dato la classe è definita in. Ciò può semplificarti la vita durante il debug (le tracce dello stack di eccezioni di solito ti danno numeri di file e di linea) o quando unisci il codice sorgente in un repository di controllo del codice sorgente.

    
risposta data 11.09.2012 - 14:21
fonte
8

Secondo SOLID, non solo dovresti creare l'interfaccia, e non solo dovrebbe essere in un file diverso, dovrebbe essere in un altro assembly.

Perché? Poiché qualsiasi modifica a un file di origine che viene compilato in un assieme richiede la ricompilazione dell'assieme e qualsiasi modifica a un assieme richiede la ricompilazione di qualsiasi assembly dipendente. Quindi, se il tuo obiettivo, basato su SOLID, è di essere in grado di sostituire un'implementazione A con un'implementazione B, mentre la classe C dipendente dall'interfaccia I non deve conoscere la differenza, devi assicurarti che l'assembly con I in esso non cambia, proteggendo così gli usi.

"Ma è solo una ricompensa" ti sento protestare. Beh, potrebbe essere, ma nella tua app per smartphone, che è più facile sulla larghezza di banda dei dati degli utenti; scaricare un file binario modificato o scaricare quel file binario e altri cinque con codice che dipende da esso? Non tutti i programmi sono scritti per essere consumati dai computer desktop su una LAN. Anche in questo caso, dove la larghezza di banda e la memoria sono economiche, le patch release più piccole possono avere valore perché sono banali da esportare all'intera LAN tramite Active Directory o livelli simili di gestione dei domini; i tuoi utenti attenderanno solo pochi secondi per essere applicati la prossima volta che accederanno invece di pochi minuti per la reinstallazione del tutto. Senza contare che, meno componenti devono essere ricompilati durante la creazione di un progetto, più veloce sarà il suo sviluppo, rendendoti più produttivo perché trascorri meno tempo rimanendo in attesa di 50 assemblaggi da compilare per ogni modifica apportata.

Ora, il disclaimer: questo non è sempre possibile o fattibile. Il modo più semplice per farlo è creare un progetto di "interfacce" centralizzato. Questo ha i suoi lati negativi; il codice diventa meno riutilizzabile perché il progetto di interfaccia AND il progetto di implementazione devono essere referenziati in altre app riutilizzando il livello di persistenza o altri componenti chiave della tua app. È possibile superare questo problema suddividendo le interfacce in assiemi più strettamente accoppiati, ma in tal caso si hanno più progetti nell'app che rendono molto complessa la creazione completa. La chiave è il bilanciamento e il mantenimento del design liberamente accoppiato; di solito puoi spostare i file in base alle necessità, così quando vedi che una classe avrà bisogno di molte modifiche, o che le nuove implementazioni di un'interfaccia saranno necessarie regolarmente (magari per interfacciarti con le nuove versioni supportate di altri software, o tipi di file, ecc. ) puoi separarlo dalla sua interfaccia e proteggere gli usi dalla conoscenza di tali cambiamenti.

    
risposta data 11.09.2012 - 16:49
fonte
6

È spesso buona norma lasciare che i file di codice contengano solo una singola classe o una singola interfaccia. Ma queste pratiche di codifica sono un mezzo per un fine - per strutturare meglio il tuo codice rendendolo più facile da lavorare. Se tu e il tuo team avete trovato più facile lavorare con le classi mantenute insieme alle loro interfacce, fatelo assolutamente.

Personalmente preferisco avere l'interfaccia e la classe nello stesso file quando c'è solo una classe che implementa l'interfaccia, come nel tuo caso.

Riguardo ai problemi che hai con la navigazione, posso raccomandare caldamente ReSharper . Contiene scorciatoie molto utili per saltare direttamente al metodo che implementa uno specifico metodo di interfaccia.

    
risposta data 11.09.2012 - 15:18
fonte
3

Ci sono molte volte in cui vuoi che la tua interfaccia non sia solo in un file separato alla classe, ma anche in un gruppo separato del tutto.

Ad esempio, un'interfaccia del contratto di servizio WCF può essere condiviso da entrambi i clienti e il servizio se hai il controllo su entrambe le estremità del filo. Spostando l'interfaccia nel proprio assieme, avrà meno dipendenze di assemblaggio. Ciò rende molto più facile per il cliente consumare, allentare l'accoppiamento con la sua implementazione.

    
risposta data 11.09.2012 - 16:49
fonte
1

Raramente, se mai, ha senso avere una singola implementazione di un'interfaccia 1 . Se metti un'interfaccia pubblica e una classe pubblica che implementa quell'interfaccia nello stesso file, ci sono buone probabilità che tu non abbia bisogno di un'interfaccia.

Quando la classe che hai co-localizzato con l'interfaccia è abstract e sai che tutte le implementazioni della tua interfaccia dovrebbero ereditare quella classe astratta, l'individuazione dei due nello stesso file ha senso. Dovresti comunque valutare attentamente la tua decisione di utilizzare un'interfaccia: chiediti se una classe astratta di per sé andrebbe bene e rilascia l'interfaccia se la risposta è affermativa.

In generale, però, dovresti attenersi alla strategia "una classe pubblica / interfaccia corrisponde a un file": è facile da seguire e rende più facile la navigazione nella struttura dei sorgenti.

1 Un'eccezione degna di nota è quando è necessario disporre di un'interfaccia per scopi di test, perché la scelta del framework di simulazione ha posto questo requisito aggiuntivo sul codice.     
risposta data 11.09.2012 - 15:22
fonte
1

Le interfacce appartengono ai loro clienti non alle loro implementazioni, come definito in Agile Principles, Practices and Patterns di Robert C. Martin. Pertanto, la combinazione di interfaccia e implementazione nello stesso posto è contro il suo principio.

Il codice cliente dipende dall'interfaccia. Ciò consente la possibilità di compilare e distribuire il codice client e l'interfaccia senza l'implementazione. Quindi puoi avere implementazioni diverse che funzionano come plugins nel codice client.

UPDATE: Questo non è un principio solo agile qui. La Gang of Four in Design Patterns, già nel '94, parla già di client che aderiscono alle interfacce e programmano interfacce. La loro vista è simile.

    
risposta data 11.09.2012 - 15:17
fonte
0

Personalmente non mi piace l'io davanti a un'interfaccia. Come utente di un tal tipo non voglio vedere che questa è un'interfaccia o no. Il tipo è la cosa interessante. In termini di dipendenze, il tuo FooService è UNA possibile implementazione di IFooService. L'interfaccia dovrebbe essere vicina al luogo in cui viene utilizzata. D'altra parte l'implementazione dovrebbe essere in un punto in cui può essere facilmente modificata senza influenzare il cliente. Quindi, preferirei due file - normalmente.

    
risposta data 11.09.2012 - 14:13
fonte
0

La codifica delle interfacce può essere negativa, viene infatti trattata come un anti-modello per determinati contesti e non è una legge. Di solito un buon esempio di codifica per interfacce anti-pattern è quando si ha un'interfaccia che ha solo un'implementazione nella propria applicazione. Un altro sarebbe se il file dell'interfaccia diventasse così fastidioso da dover nascondere la sua dichiarazione nel file della classe implementata.

    
risposta data 11.09.2012 - 16:09
fonte
0

Mettere una classe e un'interfaccia nello stesso file non pone limiti su come possono essere usati. Un'interfaccia può ancora essere utilizzata per i mock ecc. Nello stesso modo, anche se si trova nello stesso file di una classe che la implementa. La domanda potrebbe essere considerata puramente come una delle convenienze organizzative, nel qual caso direi fare qualsiasi cosa ti semplifica la vita!

Naturalmente, si potrebbe obiettare che avere i due nello stesso file potrebbe portare gli incauti a considerarli più programmaticamente accoppiati di quanto non siano realmente, il che è un rischio.

Personalmente, se mi imbattessi in una base di codice che utilizzava una convenzione sull'altra non sarei troppo preoccupato in alcun modo.

    
risposta data 28.10.2016 - 22:20
fonte

Leggi altre domande sui tag