2009-05-30 17 views
6

Similar question but not quite the same thingPseudo-multipla ereditarietà con metodi di estensione su interfacce in C#?

Stavo pensando che, con metodi di estensione nello stesso spazio dei nomi come interfaccia si potrebbe ottenere un effetto simile a ereditarietà multipla in quanto non è necessario avere il codice duplicato attuazione della stessa interfaccia allo stesso modo in 10 classi diverse.

Quali sono alcuni aspetti negativi di questo? Penso che i pro siano abbastanza ovvi, sono i contro che di solito tornano a morderti più tardi.

Uno degli svantaggi che vedo è che i metodi di estensione non possono essere virtuali, quindi è necessario essere sicuri di volere che vengano implementati allo stesso modo per ogni istanza.

risposta

4

Il problema che vedo con la creazione di funzionalità di interfaccia tramite i metodi di estensione è che non si sta più implementando l'interfaccia e quindi non si può usare l'oggetto come tipo di interfaccia.

Dire che ho un metodo che accetta un oggetto di tipo IBar. Se implemento l'interfaccia IBar sulla classe Foo tramite i metodi di estensione, allora Foo non deriva da IBar e non può essere usato in modo intercambiabile con esso (principio di sostituzione di Liskov). Certo, ottengo il comportamento che voglio aggiungere a Foo, ma perdo l'aspetto più importante della creazione di interfacce in primo luogo - essere in grado di definire un contratto astratto che può essere implementato in vari modi da varie classi in modo che le classi dipendenti non devono conoscere le implementazioni concrete.

Se avessi bisogno di ereditarietà multipla (e finora ho vissuto senza di essa) abbastanza male, penso che userei invece la composizione per ridurre al minimo la quantità di duplicazione del codice.

+0

Puoi spiegare "allora Foo non deriva da IBar e non può essere usato in modo intercambiabile con esso"? Se ho una classe pubblica Foo: IBar, non sarei in grado di passare in un Foo ovunque sia previsto un IBar? O intendevi qualcosa di diverso? Mi rendo conto che smette di funzionare come un'interfaccia. Funzionalmente immagino che agisca più come una classe base concreta senza metodi virtuali di un'interfaccia.(Che sia una buona idea o no è forse una domanda a parte, ma sembra utile mantenere DRY) – Davy8

+0

E la maggior parte delle volte non vedo davvero troppo bisogno di ereditarietà multipla, ma c'è una porzione di codice al lavoro che sembra un pasticcio con codice ripetuto dappertutto che implementa la stessa interfaccia su letteralmente 10-20 classi diverse con lo stesso codice, e non può essere inserito in una classe base perché già derivano da un'altra classe base che ha metodi comune a un'interfaccia diversa. – Davy8

+0

Se Foo deriva da IBar, è necessario disporre di metodi di istanza, non di metodi di estensione che implementano IBar. Ti aspetti di delegare questi metodi ai metodi di estensione? Supponevo che l'implementazione fosse fornita esclusivamente dalle estensioni. Se si sta eseguendo la delega, penso che la composizione dalle classi base che implementano la funzionalità dell'interfaccia desiderata sia più preferibile rispetto ai metodi di estensione. Forse potresti fornire la funzionalità utilizzando il modello di decoratore. – tvanfosson

1

Un modo decente per pensare a questo è che i metodi di istanza sono qualcosa di fatto dal l'oggetto, mentre i metodi di estensione sono qualcosa fatto - l'oggetto. Sono abbastanza sicuro che le linee guida sulla struttura del framework dicono che dovresti implementare un metodo di istanza ogni volta che è possibile.

Un'interfaccia dichiara "Mi interessa utilizzare questa funzionalità, ma non come viene eseguita". Ciò lascia agli implementatori la libertà di scegliere il come. Scollega l'intento, un'API pubblica, dal meccanismo, una classe con codice concreto.

Poiché questo è il vantaggio principale delle interfacce, implementarle interamente come metodi di estensione sembra vanificare il loro scopo. Anche IEnumerable<T> ha un metodo di istanza.

Modifica: Inoltre, gli oggetti hanno lo scopo di agire sui dati che contengono. I metodi di estensione possono vedere solo l'API pubblica di un oggetto (poiché sono solo metodi statici); dovresti esporre tutto lo stato di un oggetto per farlo funzionare (un OO no-no).

+0

Probabilmente ho formulato la domanda male, ma fondamentalmente ho intenzione di non trattarlo come un'interfaccia. L'interfaccia è semplicemente il mezzo tecnico per svolgere il compito, che è il riutilizzo del codice e l'implementazione dell'ereditarietà di un povero uomo in un linguaggio che non lo supporta. – Davy8

+0

Ok, vedo cosa stai chiedendo. Alla fine vorresti allegare un comportamento alle classi che "optano per" implementando un'interfaccia. Interfacce multiple = comportamenti multipli = ereditarietà multipla. Buon ragionamento, ma cosa significa? Devi ancora esporre lo stato di un oggetto in modo che i metodi di estensione possano lavorare con esso. Ma ora chiunque può vedere quello stato, quindi nessun incapsulamento. Anche allora, il metodo otterrà solo un IBar, che non ha membri, e quindi non è molto utile. Cosa potrebbero fare i metodi di estensione? Come ha detto @tvanfosson, la composizione è la soluzione migliore. –

Problemi correlati