Come implementare solo una parte di un'interfaccia


14

Quando si sviluppa in OOP, a volte un'interfaccia / contratto viene fornito da una libreria che non è possibile modificare. Chiamiamo questa interfaccia J.

Ora hai un oggetto di classe A che consuma oggetti che implementano questa interfaccia. All'interno È necessaria solo una piccola parte delle definizioni dell'interfaccia. Alcune delle classi di oggetti sono state create da me durante il progetto (chiamiamone una di tipo D), quindi c'è un overhead nell'implementazione di tutto all'interno dell'interfaccia J.

Voglio implementare un sottoinsieme delle funzionalità nell'interfaccia J, ma le mie soluzioni finora non mi soddisfano:

  • implementando ogni aspetto di J e quindi lanciando "notImplementedExceptions" disinforma l'utente dei miei oggetti: sembrerebbe che i miei oggetti di tipo D siano conformi all'interfaccia J, ma non lo fanno - e altri consumatori dei miei oggetti (che accettano oggetti che implementano l'interfaccia J) non posso fare affidamento sull'integrità dei miei oggetti.
  • L'implementazione di un'interfaccia appena definita mi impedisce di utilizzare oggetti che implementano solo l'interfaccia J, sebbene l'interfaccia J sia pienamente compatibile con la mia interfaccia.
  • Lasciare che i miei oggetti personalizzati implementino l'interfaccia J creerebbe un notevole sovraccarico, perché non hanno bisogno di tutte queste funzionalità.

Quando potessi modificare l'interfaccia J, avrei creato una "super-interfaccia" K che aveva questo sottoinsieme della funzionalità dell'interfaccia J e avrei reso l'interfaccia J ereditata dall'interfaccia K. Ma non posso modificare l'interfaccia J.

Qual è una soluzione orientata agli oggetti a questo problema? La soluzione migliore sta ancora implementando l'interfaccia "solo" J? O ci sono modi OOP per "superclassare" un'interfaccia senza alterarla?


1
Scopri come funzionano le classi * Adapter in Swing.

Risposte:


9

se non controlli l'interfaccia J, sei bloccato.

puoi, per chiarezza, implementare la tua interfaccia subJ e l' interfaccia J e usare subJ nel tuo codice per chiarire che i metodi extra nell'interfaccia J non sono richiesti, ma non penso che ti guadagni molto

se possibile, implementare completamente l'intera interfaccia J

se possibile, contattare il proprietario dell'interfaccia J e chiedergli di modificarlo per adattarlo meglio ai propri scopi


2
Come compromesso: puoi chiedere al proprietario dell'interfaccia J di ereditare dalla tua interfaccia più piccola SubJ.
k3b,

27

Il punto centrale dell'implementazione di un'interfaccia è fornire un contratto stabile tra il chiamante e il chiamante che non cambia mai mentre i dettagli dell'implementazione possono variare.

Con l'implementazione dell'interfaccia J stai dicendo al mondo esterno cosa puoi fare.

Se non hai bisogno della metà di ciò che fa J, allora l'interfaccia dovrebbe essere davvero suddivisa in quanto non è piccola come potrebbe essere, oppure devi usare NotImplementedExceptioni metodi e le proprietà che non ti servono. Tuttavia, questa non è la soluzione migliore in quanto confonde le aspettative delle persone su ciò che il tuo codice può fare.


5

Se la tua classe di consumatore A non richiede tutta l'interfaccia J, suggerirei di creare una nuova interfaccia (chiamala K), che descriva in modo completo tutto ciò che richiede.

Ciò fornisce un chiaro segnale a chiunque utilizzi la classe A su quale sia la sua parte del contratto. In tal modo migliorare le possibilità di riutilizzo. Se qualcuno ha bisogno di fornire un oggetto che implementa un'interfaccia ampia e complessa, al fine di fare qualcosa di relativamente semplice, probabilmente finirà per scrivere qualunque classe A faccia da sé.

Per consentire alla classe A di consumare oggetti che implementano solo l'interfaccia J, è possibile fornire una classe wrapper che implementa l'interfaccia K e trasmette tutte le chiamate appropriate a un membro dell'interfaccia J.


3

Mentre sono d'accordo con le risposte esistenti che affermano che devi davvero implementare J completamente (con eccezioni per i metodi non implementati) o per niente, una possibile soluzione alternativa è la seguente:

  • Creare un'interfaccia più piccola K che è un sottoinsieme di J e implementa i metodi richiesti.
  • Crea un wrapper per A che accetta oggetti che implementano J e K.
  • Nel caso di un passaggio in J, basta spingerlo attraverso l'istanza di A.
  • Nel caso di un passaggio in K, istanziare un'implementazione anonima di J, cablando solo i metodi da K a J presenti. Passa il risultato all'istanza di A.

Si noti che questa è una soluzione piuttosto brutta, poiché richiede un wrapper che accetta più cose che sono essenzialmente la stessa cosa. Ma ottiene quanto segue:

  • Nessuna modifica a J o A.
  • Nessuna implementazione "esposta" di J che è incompleta.

Se la tua lingua OO non consente l'istanza dell'interfaccia anonima, puoi creare un'implementazione fittizia dell'interfaccia e creare un'istanza.


1

Quanto tempo impiegheresti a implementare l'intera interfaccia?

Se ti occorresse molto tempo, ciò indica un problema di progettazione e ti consiglierei (come Steven A. prima di me) di contattare il proprietario e vedere se può essere modificato in futuro.

Usi l'interfaccia nel tuo codice, indipendentemente dalla libreria dell'interfaccia?

Come ha suggerito Steven A., puoi usare la tua interfaccia come vorresti vederla nel tuo codice. Questo almeno mantiene pulito il codice interno. Puoi anche inviarlo al proprietario come interfaccia che ti aspetteresti di trovare. Forse è d'accordo con te, o forse ti può spiegare perché l'interfaccia non dovrebbe essere divisa.

La tua implementazione avrebbe mai bisogno dei membri inutilizzati dell'interfaccia?

Nel caso in cui ti aspetti che non vengano mai chiamati poiché non sono supportati, preferisco usare il NotSupportedException. Questo dà una chiara indicazione che non supporterai mai questa interfaccia, piuttosto che non l'hai implementata.


Una buona idea su come usarlo NotSupportedExceptionchiarisce che non si supporta questo metodo.
ChrisF

@ChrisF: Uso solo NotImplementedExceptionper il codice di produzione, dove altrimenti scriverei "TODO: Implementa questo" Purtroppo, non compaiono nell'elenco delle attività di Visual Studio . Ma in pratica, difficilmente lascio un metodo non implementato per più di due giorni. Resharper mostra anche quelle eccezioni in grassetto (almeno con la mia configurazione). :)
Steven Jeuris,

0

Implementa ciò di cui hai bisogno e lancia un'eccezione NoImplementedException sugli altri.

Questa è una questione di utilizzo. Se non usi l'interfaccia o sai che il tuo codice non utilizzerà l'interfaccia, non investire tempo su questo, poiché non è necessario investire tempo sul codice ridondante.

Lavora per l'attività a portata di mano e segui una buona pista che gli altri possano seguire se vogliono usare l'interfaccia.

Molte interfacce in Java non sono completamente implementate.

Questo è l'approccio di Apache alle cose: http://commons.apache.org/lang/api-2.4/org/apache/commons/lang/NotImplementedException.html


Forse discuti sul perché credi che questa sia una soluzione adeguata. Questo non aggiunge nulla alla domanda.
Steven Jeuris,

non è necessario implementare tutto, in quanto non è necessario spiegare tutto. se fosse quello il suo caso, alcune parti dell'interfaccia erano ridondanti e l'avrebbe preso immediatamente.
Visualizza nome
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.