È meglio passare una * interfaccia * o un * oggetto * come parametro a una funzione?
-
05-07-2019 - |
Domanda
Sto cercando di convincere un collega che una funzione dovrebbe prendere un'interfaccia come parametro e non l'oggetto stesso. Penso che piccoli oggetti possano andare bene a passare, ma per quelli grandi darei loro un'interfaccia e passerei semplicemente l'I / F, non il tutto.
Nota che ci sarà sempre una di queste classi di grandi dimensioni: l'i / f non verrà mai usato per un oggetto diverso. Questo serve solo a nascondere l'implementazione di un oggetto.
Sei d'accordo sul fatto che separare una grande classe in un'interfaccia (s) è una buona pratica?
Ci sono degli svantaggi nel fare questo?
Esempio:
public interface class IVeryLargeClass
{
void DoSomething();
...
};
public ref class VeryLargeClass : public IVeryLargeClass
{
public:
virtual void DoSomething() { ... }
...
};
public ref class AnotherClass
{
public:
AnotherClass(VeryLargeClass^ vlc) { vlc->DoSomething(); }
// OR
AnotherClass(IVeryLargeClass^ vlc) { vlc->DoSomething(); }
};
Soluzione
Uno dei primi principi che impari nello sviluppo di OO:
Programma su un'interfaccia, non su implementazione.
Indichi che " ci sarà sempre e solo una di queste grandi classi: l'i / f non verrà mai usato per un oggetto diverso " ;. Questo potrebbe essere vero nel tuo caso, ma vorrei avere un nickel per ogni volta che una simile affermazione si rivelava errata.
Oltre a considerare se potrebbero esserci più implementazioni della tua interfaccia, dovresti anche considerare se il tuo oggetto concreto esporta (o potrebbe esportare) metodi aggiuntivi che non condividono un'affinità logica con le operazioni dichiarate nell'interfaccia. In tal caso, è possibile semplicemente dichiarare le operazioni aggiuntive in una o più interfacce aggiuntive. Un client, quindi, deve solo accoppiarsi con l'interfaccia che esporta le operazioni a cui è interessato.
In parole semplici, le interfacce forniscono un mezzo per gestire l'accoppiamento tra client e provider.
Altri suggerimenti
Il Principio di inversione di dipendenza può essere sintetizzato come: è meglio dipendono dalle astrazioni che dalle concrezioni.
È quasi sempre meglio passare un'interfaccia piuttosto che passare una classe concreta.
Detto questo, l'elevata coesione con i tipi interni va bene all'interno di un modulo particolare, ma è molto soggettivo su quando e come si dovrebbero passare oggetti concreti.
Preferirei evitare di creare e interfaccia per il gusto di creare un'interfaccia. Se puoi usare quell'interfaccia in più di un posto, allora hai un vincitore - o se si tratta di una funzione pubblica e di una classe e vuoi semplificare in modo specifico.
Se si passa a un'implementazione, si perde uno dei vantaggi dell'utilizzo delle interfacce, il che significa separare la logica dall'attuazione effettiva.
L'interfaccia di un modulo software A è deliberatamente tenuto separato dal implementazione di quel modulo. Il quest'ultimo contiene il codice effettivo di procedure e metodi descritti in l'interfaccia, così come altri & Quot; private " variabili, procedure, ecc. Qualsiasi altro modulo software B (che può essere indicato come cliente di A) che interagisce con A è costretto a farlo solo attraverso l'interfaccia. Uno vantaggio pratico di questo accordo è che sostituendo il implementazione di A da parte di un altro che soddisfa le stesse specifiche di l'interfaccia non dovrebbe causare B a fallire - fintanto che il suo uso di A è conforme con le specifiche del interfaccia (vedi anche Liskov principio di sostituzione).
L'altro problema è la tua stessa classe molto grande. Questo potrebbe non rientrare nell'ambito di ciò che stai facendo, ma una classe molto ampia implica che potrebbe fare troppo in primo luogo. Puoi trasformarlo in classi più piccole, dove le informazioni richieste all'interno della funzione che stai chiamando sono incapsulate nella sua classe più piccola? Se crei un'interfaccia con quella classe potresti trovarla molto più riutilizzabile rispetto alla classe molto grande che hai attualmente.