Interfaccia vuota per combinare più interfacce

20

Supponi di avere due interfacce:

interface Readable {
    public void read();
}

interface Writable {
    public void write();
}

In alcuni casi gli oggetti di implementazione possono supportare solo uno di questi, ma in molti casi le implementazioni supporteranno entrambe le interfacce. Le persone che usano le interfacce dovranno fare qualcosa del tipo:

// can't write to it without explicit casting
Readable myObject = new MyObject();

// can't read from it without explicit casting
Writable myObject = new MyObject();

// tight coupling to actual implementation
MyObject myObject = new MyObject();

Nessuna di queste opzioni è terribilmente conveniente, ancor più se si considera che si vuole che questo sia un parametro del metodo.

Una soluzione sarebbe dichiarare un'interfaccia di avvolgimento:

interface TheWholeShabam extends Readable, Writable {}

Ma questo ha un problema specifico: tutte le implementazioni che supportano sia Readable che Writable hanno per implementare TheWholeShabam se vogliono essere compatibili con le persone che usano l'interfaccia. Anche se non offre nulla a parte la presenza garantita di entrambe le interfacce.

C'è una soluzione pulita a questo problema o dovrei andare per l'interfaccia wrapper?

Aggiorna

In effetti è spesso necessario avere un oggetto che sia sia leggibile sia scrivibile, in modo da separare semplicemente le preoccupazioni negli argomenti non è sempre una soluzione pulita.

UPDATE2

(estratto come risposta, quindi è più facile commentare)

Update3

Fai attenzione che il caso d'uso principale per questo è not (anche se devono essere supportati). Gli stream fanno una distinzione molto specifica tra input e output e c'è una chiara separazione delle responsabilità. Piuttosto, pensa a qualcosa come un bytebuffer in cui hai bisogno di un oggetto da cui puoi scrivere e leggere, un oggetto che ha uno stato molto specifico ad esso collegato. Questi oggetti esistono perché sono molto utili per alcune cose come l'I / O asincrono, le codifiche, ...

Update4

Una delle prime cose che ho provato era la stessa del suggerimento indicato di seguito (controlla la risposta accettata) ma si è dimostrato troppo fragile.

Supponiamo di avere una classe che deve restituire il tipo:

public <RW extends Readable & Writable> RW getItAll();

Se chiami questo metodo, la RW generica viene determinata dalla variabile che riceve l'oggetto, quindi hai bisogno di un modo per descrivere questa var.

MyObject myObject = someInstance.getItAll();

Funzionerebbe, ma ancora una volta lo legherà a un'implementazione e potrebbe lanciare classcastexceptions in fase di esecuzione (a seconda di cosa viene restituito).

Inoltre se vuoi una variabile di classe del tipo RW, devi definire il generico a livello di classe.

    
posta nablex 16.10.2013 - 13:30
fonte

4 risposte

17

Sì, puoi dichiarare il tuo parametro di metodo come un tipo sottodescritto che estende sia Readable che Writable :

public <RW extends Readable & Writable> void process(RW thing);

La dichiarazione del metodo sembra terribile, ma utilizzarla è più facile che dover conoscere l'interfaccia unificata.

    
risposta data 16.10.2013 - 13:43
fonte
11

Se c'è un posto in cui hai bisogno di myObject poichè sia un Readable che Writable puoi:

  • Refactor quel posto? Leggere e scrivere sono due cose diverse. Se un metodo fa entrambi, forse non segue il principio di responsabilità singola.

  • Supera myObject due volte, come Readable e come Writable (due argomenti). Che cosa importa al metodo se sia o non sia lo stesso oggetto?

risposta data 16.10.2013 - 13:38
fonte
4

Nessuna delle risposte attualmente risolve la situazione quando non è necessario un o leggibile, ma entrambi leggibili. È necessario garantire che durante la scrittura su A, è possibile leggere i dati indietro da A, non scrivere su A e leggere da B e sperare solo che siano effettivamente lo stesso oggetto. I casi di utilizzo sono abbondanti, ad esempio ovunque utilizzi un ByteBuffer.

Comunque, ho quasi finito il modulo su cui sto lavorando e attualmente ho optato per l'interfaccia del wrapper:

interface Container extends Readable, Writable {}

Ora puoi almeno fare:

Container container = IOUtils.newContainer();
container.write("something".getBytes());
System.out.println(IOUtils.toString(container));

Le mie implementazioni di container (attualmente 3) implementano Container in opposizione alle interfacce separate ma, se qualcuno dovesse dimenticarlo in un'implementazione, IOUtils fornisce un metodo di utilità:

Readable myReadable = ...;
// assuming myReadable is also Writable you can do this:
Container container = IOUtils.toByteContainer(myReadable);

So che questa non è la soluzione ottimale ma è ancora la migliore via d'uscita al momento, perché Container è ancora un caso molto grande.

    
risposta data 17.10.2013 - 10:52
fonte
0

Data un'istanza MyObject generica, avrai sempre bisogno di sapere se supporta le letture o le scritture. Quindi avresti il codice come:

if (myObject instanceof Readable)  {
    Readable  r = (Readable) myObject;
    readThisReadable( r );
}

Nel caso semplice, non penso che tu possa migliorare su questo. Ma se readThisReadable vuole scrivere il leggibile su un altro file dopo averlo letto, diventa scomodo.

Quindi probabilmente andrei con questo:

interface TheWholeShabam  {
    public boolean  isReadable();
    public boolean  isWriteable();
    public void     read();
    public void     write();
}

Prendendo questo come parametro, readThisReadable , ora readThisWholeShabam , può gestire qualsiasi classe che implementa TheWholeShabam, non solo MyObject. E può scriverlo se è scrivibile e non scriverlo se non lo è. (Abbiamo un vero "polimorfismo".)

Quindi il primo set di codice diventa:

TheWholeShabam  myObject = ...;
if (myObject.isReadable()
    readThisWholeShebam( myObject );

E potresti salvare una riga qui facendo leggere a readThisWholeShebam () il controllo di leggibilità.

Questo vuol dire che il nostro ex-Solo leggibile deve implementare isWriteable () (restituendo false ) e write () (non facendo nulla), ma ora può andare in tutti i tipi di posti che couldn ' t andare prima e tutto il codice che gestisce gli oggetti TheWholeShabam ci occuperà senza ulteriori sforzi da parte nostra.

Un'altra cosa: se puoi gestire una chiamata a read () in una classe che non legge e una chiamata a write () in una classe che non scrive senza cestinare qualcosa, puoi saltare isReadable ( ) e isWriteable () metodi. Questo sarebbe il modo più elegante per gestirlo - se funziona.

    
risposta data 16.10.2013 - 21:09
fonte

Leggi altre domande sui tag