2009-04-06 8 views
6

Dovresti sempre creare un'interfaccia se esiste la possibilità che possa esserci qualcos'altro che potrebbe usarlo, o aspettare fino a quando non c'è una reale necessità per questo, quindi refactoring per usare un'interfaccia?Dovresti creare un'interfaccia quando lì (attualmente) sarà solo una classe che la implementa?

programmazione a un'interfaccia sembra generalmente come un buon consiglio, ma poi c'è YAGNI ...

Credo che forse dipende dalla situazione. In questo momento ho un oggetto che rappresenta una cartella che può contenere ricette o altre cartelle. Invece di usare la cartella direttamente, dovrei preoccuparmi di implementare qualcosa come IContainer? Nel caso in cui in futuro voglio avere una ricetta che si riferisce ad altre ricette sub (ad esempio una ricetta torta di mele che è anche un contenitore per una ricetta torta crosta)

risposta

8

Dipende sempre dalla situazione. Se SAPETE che ci sarà un'altra classe usando l'interfaccia, allora sì, create la classe di interfaccia per risparmiare tempo in seguito. Tuttavia, se non sei sicuro (e il più delle volte non lo sei), aspetta di averne bisogno.

Ora questo non significa ignorare la possibilità dell'interfaccia: pensate ai metodi pubblici dell'oggetto e ad un occhio per creare un'interfaccia in seguito, ma non ingombrare il vostro codice con qualsiasi cosa che non sia effettivamente BISOGNO.

+0

YAGNI, non ne hai bisogno (ne hai bisogno) -> non ne avrai bisogno –

+1

Precisamente. Ogni volta che ho inserito interfacce o qualsiasi cosa "nel caso in cui" si è rivelato necessario assolutamente mai. Ma il tuo chilometraggio può variare. –

2

Direi che dipende più da quanti posti si sta andando a utilizzare la classe, e meno su quante classi potrebbero eventualmente implementare l'interfaccia. Se utilizzi la Cartella solo in una o due posizioni, allora direi di aspettare finché non ci sarà effettivamente bisogno dell'interfaccia prima di implementarla e refactoring. Tuttavia, se Cartella verrà utilizzata in 100 posizioni diverse, è possibile risparmiare un po 'di tempo programmando un'interfaccia in primo piano.

3

Ci sarà sempre un test che lo usa, giusto (fai test di unità, vero?). Il che significa che sono sempre le classi N + 1 ad usarlo, dove N è il numero di classi che usano la tua classe nell'applicazione.

Un altro scopo dell'interfaccia oltre all'invio delle dipendenze è la separazione delle preoccupazioni in modo che la classe possa effettivamente implementare più interfacce.

Tieni tutto questo in mente ma puoi sempre avere le interfacce introdotte in seguito tramite il refactoring se non implementate all'inizio.

+0

Cosa succede se la classe è abbastanza semplice da non richiedere un oggetto fittizio per il test dell'unità (ad esempio, è abbastanza semplice utilizzare solo un oggetto reale)? – Davy8

+0

Non ho mai detto nulla sugli oggetti finti, no? :-)? Tutto ciò che dico che il numero di classi che lo usano è sempre + 1 (test). E sono d'accordo sul fatto che le interfacce possano essere introdotte in seguito con un semplice refactoring. – topchef

+0

Ah, ho letto male, "utilizza" non "implementa" – Davy8

2

Generalmente, non dovresti preoccuparti di creare un'interfaccia se solo una classe la sta implementando, anche se prevedi una possibile classe perché potrebbe esserci problemi di implementazione che non si presenteranno fino a quando la classe non sarà effettivamente testato in uno scenario, nel qual caso un'interfaccia prematuramente creata potrebbe avere troppi memebr o potrebbe mancare un membro.

Ad esempio, il team .NET Framework Bas Class Library ha ammesso di progettare prematuramente ICollection quando includeva una proprietà SyncRoot. Per il successivo generico ICollection<T> hanno deciso di rimuoverlo (http://blogs.msdn.com/bclteam/archive/2005/03/15/396399.aspx).

Se si intende creare un oggetto fittizio che implementa la stessa interfaccia, questo dovrebbe essere considerato come una seconda implementazione che giustifica la creazione dell'interfaccia. Tuttavia, non tutti i test unitari garantiscono un'interfaccia stile mock.

2

Pensa a un'interfaccia come a un contratto per definire la semantica o un concetto. Questo è un approccio generale e non specifico per la lingua.Nel contesto di OO, se si lavora in un modello di ereditarietà singola, è opportuno creare un caso eccellente per preferire le interfacce rispetto alle classi per definire il proprio modello di oggetto, poiché quel singolo percorso di super classe è piuttosto prezioso e si desidera salvarlo per qualcosa di più "sostanziale" della definizione delle proprietà esposte su un oggetto o metodi.

Per avere la semantica di IContainer (contratto) è un motivo abbastanza scadente per creare un'interfaccia dalla cartella; meglio avere la tua cartella (se sta facendo una logica non banale) "implementa" l'interfaccia IContainer o ICollection (probabilmente già esistente) nei framework core della tua lingua.

Come sempre, la scelta migliore dipende abbastanza dal problema specifico. Nel caso delle tue ricette che sono anche cartelle (?!) Probabilmente stai pensando a una relazione genitore-figlio, o composizione, una semantica che può (e dovrebbe) essere espressa usando le interfacce se avrai altri elementi nel tuo sistema 'operare' su cose che sono composte usando quel tipo di semantica.

C'è un po 'di overhead (programmazione wise) con le interfacce, e, se ti trovi quando hai finito con nient'altro che un set di classi e interfacce Woof e IWoof, allora saprai che probabilmente non l'hai fatto Ho bisogno di esprimere il tuo problema in termini di interfacce - le classi semplici sarebbero state sufficienti.

Come regola generale, per qualsiasi I, è necessario disporre di almeno un paio di classi concrete (con nomi più significativi diversi da IImpl o).

Spero che questo aiuti.

+0

Questa cosa ha mangiato le mie parentesi angolate: per qualsiasi Ixxx ... nomi diversi da IxxxImpl o XXX. – alphazero

+1

SO converte le parentesi angolari '<' '>' come se fosse uno stile html. Per evitare questo, usa i backtick attorno al personaggio, –

1

Molte persone hanno già delineato un consiglio molto valido. Una cosa che vorrei aggiungere è che se si desidera evitare dipendenze dirette dirette su classi concrete, le interfacce aiuteranno fornendo un accoppiamento lento. Se si sta creando un'architettura basata su plug-in, le interfacce sono sicuramente la strada da percorrere. Inoltre, se stai pianificando di scrivere test unitari uno accanto all'altro o più avanti, probabilmente noterai che il codice che chiama nella tua classe di cartelle dovrà portare a termine un'implementazione concreta affinché il codice chiamante sia testabile . Se la tua implementazione concreta della/e classe/e della cartella è a sua volta parlando con un DB o un servizio, allora avrai bisogno di essere trasferito nei tuoi test e diventerà molto veloce. Solo i miei 2 centesimi.