È possibile utilizzare una dinamica Proxy.
Il modo più semplice è anche di passare nell'interfaccia desiderata come primo parametro della chiamata Mux.create()
. In caso contrario, sarà necessario utilizzare la riflessione per tentare di indovinare l'interfaccia desiderata da tutte le istanze di listener concrete fornite (difficile determinare se tutti gli oggetti listener implementano più interfacce in comune).
Ecco il corto di esso:
public class Mux {
/**
* @param targetInterface
* the interface to create a proxy for
* @param instances
* the concrete instances to delegate to
* @return a proxy that'll delegate to all the arguments
*/
@SuppressWarnings("unchecked")
public static <T> T create(Class<T> targetInterface, final T... instances) {
ClassLoader classLoader = targetInterface.getClassLoader();
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method m, Object[] args)
throws Throwable {
for (T instance : instances) {
m.invoke(instance, args);
}
return null;
}
};
return (T) Proxy.newProxyInstance(classLoader,
new Class<?>[] { targetInterface }, handler);
}
}
Quale si usa, per esempio, come segue:
Apple apple = new Apple();
AppleListener l1 = new AppleListenerA();
AppleListener l2 = new AppleListenerB();
apple.setListener(Mux.create(AppleListener.class, l1, l2));
apple.doSomething(); // will notify all listeners
Questo funziona semplicemente creando una dinamica Proxy
che è lanciato al tipo di destinazione T
. Tale proxy utilizza uno InvocationHandler
che semplicemente delega tutte le chiamate di metodo al proxy a determinate istanze concrete.
Nota che mentre, in generale, io finalizzare tutti i parametri e le variabili locali, ove possibile, ho finalizzato solo T... instances
in questo caso per evidenziare il fatto che se fosse instances
non finale, quindi riferimento entro una classe interna anonima no essere permesso (si otterrà un "Impossibile riferirsi a una variabile non finale args all'interno di una classe interna definita in un metodo diverso").
noti inoltre che quanto sopra presuppone che le chiamate al metodo attuale non restituiscono alcun valori significativi (o utili), quindi la handler
restituisce anche null
per tutte le chiamate di metodo. Dovrai aggiungere un bel po 'di codice in più se vuoi raccogliere i valori di ritorno e restituirli anche in modo significativo.
In alternativa, si può controllare tutto dato instances
per determinare le interfacce comuni tutti implementano, e passare tutti quelli newProxyInstance()
. Questo rende Mux.create()
molto più comodo da usare, con la perdita di un certo controllo sul suo comportamento.
/**
* @param instances
* the arguments
* @return a proxy that'll delegate to all the arguments
*/
@SuppressWarnings("unchecked")
public static <T> T create(final T... instances) {
// Inspect common interfaces
final Set<Class<?>> commonInterfaces = new HashSet<Class<?>>();
commonInterfaces.addAll(Arrays.asList(instances[0].getClass()
.getInterfaces()));
// Or skip instances[0]
for (final T instance : instances) {
commonInterfaces.retainAll(Arrays.asList(instance.getClass()
.getInterfaces()));
}
// Or use ClassLoader.getSystemClassLoader();
final ClassLoader classLoader = instances[0].getClass().getClassLoader();
// magic
final InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(final Object proxy, final Method m, final Object[] args)
throws Throwable {
for (final T instance : instances) {
m.invoke(instance, args);
}
return null;
}
};
final Class<?>[] targetInterfaces = commonInterfaces
.toArray(new Class<?>[commonInterfaces.size()]);
return (T) Proxy.newProxyInstance(classLoader, targetInterfaces,
handler);
}
Se tutti gli ascoltatori implementano l'interfaccia 'AppleListener', non vedo la necessità di a) riflessione, né, b) generici. Prendili tutti e aggiungili ad un 'Elenco' nel tuo 'Mux' da qualche parte e itera. O mi sta sfuggendo qualcosa? –
Puoi spiegare di più su cosa ti passa per il multiplexer? Perché la stessa cosa mi è venuta in mente come ha detto AlistairIsraele. –
Anche se è leggermente al di fuori di quanto previsto, è possibile utilizzare una classe [Proxy] (http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/Proxy.html). Questo crea un oggetto che assomiglia alla classe data ma chiama un gestore per elaborare le sue chiamate, il gestore può quindi iterare attraverso gli ascoltatori. – BevynQ