2011-12-20 8 views
6

Io sto contenendo "l'oggetto primario" (con la maggior parte delle funzionalità) all'interno di un "oggetto helper" che fornirà metodi di convenienza. Ho a disposizione solo un'interfaccia, a parte un oggetto restituito con quell'interfaccia da un metodo factory. Sto pensando che un buon modo per "estendere" questo oggetto sia la composizione, ma il problema per cui la mia superclasse deve implementare l'interfaccia dell'oggetto primario, che sarebbe circa 600 linee di codice stub.Qual è il modo più breve per delegare metodi non implementati a un oggetto contenuto in Java?

Chiaramente una soluzione semplice ma prolissa sarebbe quella di riempire tutti gli stub in modo da chiamare semplicemente i metodi dell'oggetto primario. C'è un modo migliore di questo in Java? In altre lingue che conosco, ci sarebbero modi per fare la delega automatica per i metodi che non sono implementati nell'oggetto helper.

Esempio:

 
class Helper implements Interface { 
    Primary primary; 

    Helper(Primary _primary) { 
     primary = _primary; 
    } 

    void helper() { 
     doStuff(); 
    } 

    // 500 lines of this 
    void stub() { 
     primary.stub(); 
    } 
} 

Nota:

piano originale era di utilizzare solo espressioni regolari per sostituire tutti i TODOs stub in Eclipse con le chiamate effettive. Cercherà uno strumento Eclipse che faccia questo automaticamente però. Inoltre, sembra che estendere l'interfaccia o utilizzare Proxy sia meglio alla fine, quindi lo perseguiremo.

+2

Eclipse può farlo per voi. Tasto destro -> Origine -> Genera metodi delegati. –

risposta

3

Ci sono alcuni possibili.


Primo: Uso un'implementazione astratta che delega le chiamate.

abstract class InterfaceDelegator implements Interface { 
    protected final Interface primary; 
    public InterfaceDelegator() { 
    this.primary = primary; 
    } 
    // implements all the calls as: primary.call(...) 
} 

class Helper extends InterfaceDelegator { 
    // override just the methods you need 
} 

Secondo: utilizzare il proxy (probabilmente più pulito).

Vedi: http://docs.oracle.com/javase/1.5.0/docs/guide/reflection/proxy.html

final Interface primary = ...; 
return (Interface) Proxy.newProxyInstance(Inteface.class.getClassLoader(), 
    new Class[] { Interface.class }, 
    new InvocationHandler() { 
    public Object invoke(Object proxy, Method m, Object[] args) throws Throwable { 
     // if m is a method that you want to change the behaviour of, return something 
     // by default, delegate to the primary 
     return m.invoke(primary, args); 
    } 
    }); 

Il gestore invocazione sarà gestire le chiamate attraverso l'esecuzione di un metodo che avete codificato, un tutti gli altri sarebbe delegata all'attuazione primario. Quindi, è possibile avvolgere un codice come questo in un metodo o in un'altra fabbrica.


Terzo: Utilizzando l'IDE per generare i metodi (come altri hanno sottolineato).

12

Utilizzare Lombok's @Delegate annotation.

Il secondo più veloce è utilizzare l'IDE; molti hanno questa funzionalità.

Terzo più veloce è scrivere un piccolo introspecter e scaricarli da soli.

+0

Sono interessato alla tua soluzione introspecter. Puoi darmi qualche suggerimento su come implementarlo? –

+0

+1 per il collegamento Lombok :) –

+0

Sfortunatamente, il team di lombok non consiglia più l'uso di @Delegate. Hanno in programma di rimuovere dalle versioni future. –

1

Alcuni IDE supportano l'ereditarietà del refactoring della delega. Questo genererà la maggior parte del codice che ti serve.

Se non si desidera utilizzare questo, è possibile creare un proxy dinamico per indirizzare le chiamate del metodo a una selezione di delegati di implementazione.

1

Ci sono due scelte di cui è possibile scegliere solo una: verbosità ridotta o velocità/prestazioni. Ovviamente è possibile utilizzare un approccio generico basato sulla riflessione che potrebbe influire sulle prestazioni del runtime o utilizzare i metodi generati automaticamente da Lombok/eclipse e vivere con la verbosità/magia. :)

0

L'interfaccia è troppo grande. Le classi e le interfacce tendono a crescere col passare del tempo, e di solito è molto più difficile del suo valore di romperle. Una volta che inizi a racchiudere una grande classe in un'altra, tuttavia, alcune pulizie potrebbero essere in ordine.

In termini approssimativi, ciò che si vuole fare è interrompere l'interfaccia in pezzi gestibili. L'interfaccia originale perde un sacco di metodi, ma ne raccoglie alcuni per restituire oggetti che implementano le altre interfacce. Ora, invece di container.getX();, si avrebbe

Interface container = new Helper(primary); 
Letter li = container.getLetterInterface(); // Convenience Method 
              // "li" comes straight from primary 
int x = li.getX(); 

Idealmente Interface scende da oltre 100 metodi per sotto 20. La restituito implementazioni di interfacce possono essere gli oggetti primari sottostanti, o oggetti creati da loro. (E nel secondo caso, come desiderato, possono visualizzare i dati al momento in cui il metodo è stato chiamato o i valori correnti in primary). In pratica questo consente di trasferire le informazioni direttamente da primary al chiamante senza preoccuparsi dei metodi di convenienza .

Fai questo e il tuo codice sarà un lotto più facile da gestire in questa e in altre situazioni. Ma è un sacco di lavoro, quindi se sai che questa è l'ultima cosa che farai con questo codice, usa una delle altre soluzioni.

Problemi correlati