5

我有很多使用 setListener 方法注册的侦听器,而不是 addListener。因此,为了允许多个侦听器注册到一个对象,我必须使用多路复用器。这很好,但现在我必须为我拥有的每个侦听器接口创建一个多路复用器。所以我的问题是:是否可以按照以下代码的要求实现 Mux.create() ?

AppleListener appleListener1 = new AppleProcessorA();
AppleListener appleListener2 = new AppleProcessorB();
AppleListener appleListenerMux = Mux.create(appleListener1, appleListener2);
Apple apple = new Apple();
apple.setListener(appleListenerMux);

OrangeListener orangeListener1 = new OrangeProcessorA();
OrangeListener orangeListener2 = new OrangeProcessorB();
OrangeListener orangeListenerMux = Mux.create(orangeListener1, orangeListener2);
Orange apple = new Orange();
orange.setListener(orangeListenerMux);

class Mux {
   public static <T> T create(T... outputs) { }
}

我想这可能使用反射。有什么理由使用反射不是一个好主意吗?(想到性能)

4

3 回答 3

9

可以使用动态Proxy

最简单的方法是将所需的接口作为第一个参数传递给您的Mux.create()调用。否则,您将不得不使用反射来尝试从提供的所有具体侦听器实例中猜测所需的接口(很难确定所有侦听器对象是否都实现了几个共同的接口)。

这是它的简短之处:

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);
    }
}

您将使用它,例如,如下所示:

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

这通过简单地创建一个Proxy转换为目标类型的动态来工作T。该代理使用InvocationHandler仅将代理的所有方法调用委托给给定的具体实例。

请注意,虽然一般情况下我会尽可能地完成所有参数和局部变量,但我只T... instances在这种情况下完成了这样一个事实,即如果instances不是最终的,则不允许在匿名内部类中引用它(你会得到一个“不能在以不同方法定义的内部类中引用非最终变量 args”)。

另请注意,以上假设实际方法调用不返回任何有意义(或有用)的值,因此所有方法调用handler也返回null。如果您想收集返回值并以有意义的方式返回这些值,则需要添加更多代码。


或者,可以检查所有给定instances的接口以确定它们都实现的通用接口,并将所有这些传递给newProxyInstance(). 这使得Mux.create()使用起来更加方便,但失去了对其行为的一些控制。

/**
 * @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);
}
于 2013-04-08T06:57:50.153 回答
0

我认为您的解决方案是“适配器”和“工厂方法”设计模式的混合。关于如何在 Java 中实现这些模式,我在下面提供了两个有用的链接:

于 2013-04-08T07:00:20.487 回答
0

复合模式非常适合您的情况。

AppleListener appleListener1 = new AppleProcessorA();
AppleListener appleListener2 = new AppleProcessorB();
CompositeListener composite = CompositeListener.for(appleListener1, appleListener2);
Apple apple = new Apple();
apple.setListener(composite);

您可能必须重构 AppleListener 和 OrangeListener 以实现一个 Listener 接口,该接口包含一个用于通知侦听器的主体方法。CompositeListener 还必须扩展此侦听器以实现复合模式。

于 2013-04-08T09:18:08.527 回答