2009-10-28 12 views
37

Lavoriamo su un progetto di medie dimensioni (3 sviluppatori in più di 6 mesi) e dobbiamo prendere una decisione successiva: vorremmo avere interfacce separate dall'implementazione concreta. Il primo è quello di memorizzare l'interfaccia in un file separato.Interfacce separate dall'implementazione della classe in progetti separati?

Vorremmo andare oltre e separare ancora di più i dati: ci piacerebbe avere un progetto (CSPROJ) con interfaccia in un file .CS più un altro file .CS con classi di aiuto (come alcune classi pubbliche utilizzate all'interno di questa interfaccia, alcune enumerazioni ecc.). Quindi, ci piacerebbe avere un altro progetto (CSPROJ) con un modello factory, implementazione di un'interfaccia concreta e altre classi "worker".

Qualsiasi classe che desideri creare un oggetto che implementa questa interfaccia deve includere il primo progetto che contiene le interfacce e le classi pubbliche, non l'implementazione stessa.

Questa soluzione ha un grosso svantaggio: moltiplica il numero di assiemi per 2, perché per ogni progetto "normale" si avrebbe un progetto con interace e uno con implementazione.

Cosa raccomanderesti? Pensi che sia una buona idea collocare tutte le interfacce in un progetto separato anziché in un'interfaccia nel proprio progetto?

+0

Tutte le interfacce sono pubbliche e non considero Remoting, WCF e altre interfacce esterne. –

risposta

52

vorrei distinguere tra le interfacce in questo modo:

  1. interfacce standalone il cui scopo è possibile descrivere, senza parlare del resto del progetto. Inseriscili in un unico "assembly di interfaccia" dedicato, che è probabilmente referenziato da tutti gli altri assembly nel tuo progetto. Esempi tipici: ILogger, IFileSystem, IServiceLocator.

  2. interfacce di classe accoppiata che hanno davvero senso solo nel contesto delle classi del progetto. Metti questi nello stesso assembly delle classi a cui sono accoppiati.

    Un esempio: supponiamo che il modello di dominio abbia una classe Banana. Se recuperi le banane tramite l'interfaccia IBananaRepository, l'interfaccia è strettamente collegata alle banane. È impossibile implementare o utilizzare l'interfaccia senza sapere qualcosa sulle banane. Pertanto, è logico che l'interfaccia risieda nello stesso assembly di Banana.

    L'esempio precedente ha un accoppiamento tecnico, ma l'accoppiamento potrebbe essere solo logico. Ad esempio, un'interfaccia IFecesThrowingTarget può avere senso solo come collaboratore della classe Monkey anche se la dichiarazione dell'interfaccia non ha alcun collegamento tecnico a Monkey.

La mia risposta non dipende sul concetto che è bene avere qualche accoppiamento alle classi.Nascondere tutto ciò che è dietro un'interfaccia sarebbe un errore. Sometimes it's okay to just "new up" a class, invece di iniettarlo o crearlo tramite una fabbrica.

+29

+1 per aver lanciato Feci a problemi di programmazione;) – Nate

+0

Opinione interessante! Probabilmente lo farò come tu proponi. –

+2

È sicuramente una buona idea spostare tipi di infrastruttura (come "ILogger',' IFileSystem', ecc.) In diversi assembly rispetto ai tipi specifici del dominio, ma probabilmente dovresti inserire 'ILogger' nello stesso assembly del tuo concreto' Implementazioni di Logger' a meno che non ci sia una ragione specifica per non farlo (ad esempio, poiché una delle implementazioni concrete cambierà molto, nel qual caso potrebbe essere opportuno isolare quella classe in un assembly separato). –

12

Sì, penso che questa sia una buona idea. In realtà, lo facciamo sempre e alla fine dovremo farlo per una semplice ragione:

Utilizziamo Remoting per accedere alle funzionalità del server. Quindi gli oggetti remoti sul server devono implementare le interfacce e il codice client deve avere accesso alle interfacce per utilizzare gli oggetti remoti.

In generale, penso che tu ti accoppi più liberamente quando metti le interfacce in un progetto separato, quindi vai avanti e fallo. Non è davvero un problema avere 2 assembly, vero?

AGGIUNTA:

appena attraversato la mia mente: Mettendo le interfacce in un assembly separato, è inoltre ottiene il vantaggio di essere in grado di riutilizzare le interfacce se alcuni di loro sono abbastanza generale.

+1

+1 per aver menzionato Remoting. (Modificato per correggere gli errori di battitura) – TrueWill

8

Non lo farei se non offre un vantaggio comprovato per l'architettura dell'applicazione.

È good to keep an eye on the number of assemblies you're creating. Anche se un'interfaccia e la sua implementazione sono nello stesso assembly, puoi comunque ottenere il disaccoppiamento che cerchi giustamente con un po 'di disciplina.

+1

Devo essere d'accordo con te, Jeff. Si è scoperto che un numero elevato di assiemi implica molti problemi. –

+5

Sembra che questo link sia rotto –

7

Penso che dovresti considerare prima se TUTTE le interfacce appartengono alla 'interfaccia pubblica' del tuo progetto.

Se devono essere condivisi da più progetti, eseguibili e/o servizi, ritengo sia giusto inserirli in un assembly separato.

Tuttavia, se sono solo per uso interno e per comodità dell'utente, è possibile scegliere di mantenerli nello stesso assieme dell'implementazione, mantenendo così la quantità totale di assiemi relativamente bassa.

+1

In realtà quello di cui stavo parlando erano solo le interfacce pubbliche. I privati, se ce ne sono, sono contenuti nel progetto con l'implementazione stessa. –

+0

Ah, capisco. In tal caso, attenersi alla risposta accettata :-) –

2

Ci sono pro e contro nell'approccio, e dovrai anche moderare la decisione su come adattarla al tuo approccio architettonico.

Sul lato "pro", è possibile ottenere un livello di separazione per aiutare a implementare le implementazioni corrette delle interfacce.Considera che se hai sviluppatori junior o di livello medio che lavorano su implementazioni, le interfacce stesse possono essere definite in un progetto su cui hanno solo accesso in lettura. Forse un dirigente senior, un team leader o un architetto è responsabile della progettazione e della manutenzione delle interfacce. Se queste interfacce vengono utilizzate su più progetti, questo può aiutare a mitigare il rischio di rotture involontarie di altri progetti quando si lavora solo in uno. Inoltre, se lavori con fornitori di terze parti a cui distribuisci un'API, l'imballaggio delle interfacce è un'ottima cosa da fare.

Ovviamente, ci sono alcuni lati negativi. L'assembly non contiene codice eseguibile. In alcuni negozi in cui ho lavorato, hanno disapprovato di non avere funzionalità in un assembly, indipendentemente dal motivo. C'è sicuramente un sovraccarico aggiuntivo. A seconda di come si imposta il file fisico e la struttura dello spazio dei nomi, è possibile che più assiemi facciano la stessa cosa (anche se non è necessario).

Su una nota semi-casuale, assicurarsi di documentare bene le interfacce. L'ereditarietà della documentazione dalle interfacce che utilizzano GhostDoc è una cosa meravigliosa.

+0

Non abbiamo regole così rigide come la produzione di assemblaggi con solo codice produttivo. Il nostro scopo principale era quello di separare l'assemblaggio dalla sua implementazione. E sì, proviamo a documentarli bene :) –

+1

Sono contento che non abbiamo questa regola in cui lavoro ora anche io. ;-) –

3

Abbiamo utilizzato un numero piuttosto elevato di assembly separati nel nostro codice condiviso. Nel tempo, abbiamo scoperto che quasi invariabilmente ci siamo riferiti a questi in gruppi. Questo ha reso più lavoro per gli sviluppatori, e abbiamo dovuto cercare per trovare quale assembly fosse una classe o un'interfaccia. Abbiamo finito per combinare alcuni di questi assembly basati su modelli di utilizzo. La vita è diventata più facile.

Ci sono molte considerazioni qui - stai scrivendo una libreria per gli sviluppatori, stai distribuendo le DLL ai clienti fuori sede, stai usando i servizi remoti (grazie, Maximilian Mayerl) o scrivendo servizi WCF, ecc. Non c'è nessuno risposta giusta - dipende.

In generale sono d'accordo con Jeff Sternal: non suddividere gli assemblaggi a meno che non offra un beneficio comprovato.

5

Se un'implementazione di un'interfaccia finisce per avere molte dipendenze (su altri assiemi, ecc.), Quindi avere l'interfaccia in un assieme isolato può semplicemente vita per consumatori di livello superiore.

Possono fare riferimento all'interfaccia senza inavvertitamente diventare dipendenti dalle dipendenze dell'implementazione specifica.

Problemi correlati