简体   繁体   English

是否可以用Java编写通用多路复用器?

[英]Is it possible to write a general purpose multiplexer in Java?

I've got a lot of listeners that are registered using setListener methods, rather than addListener. 我有很多使用setListener方法注册的监听器,而不是addListener。 So in order to allow multiple listeners to register to an object, I've got to use multiplexers. 因此,为了允许多个侦听器注册到一个对象,我必须使用多路复用器。 That's fine, but now I've got to create a multiplexer for every listener interface I have. 这很好,但现在我必须为我拥有的每个监听器接口创建一个多路复用器。 So my question is: is it possible to implement Mux.create() as required for the following code? 所以我的问题是:是否可以根据以下代码的需要实现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) { }
}

I imagine this might be possible using reflection. 我想这可能是使用反射。 Is there any reason using reflection would be a bad idea? 有没有理由使用反射会是一个坏主意? (performance comes to mind) (表现浮现在脑海中)

It's possible using a dynamic Proxy . 可以使用动态代理

The easiest way is to also pass in the desired interface as the first parameter to your Mux.create() call. 最简单的方法是将所需的接口作为Mux.create()调用的第一个参数传递。 Otherwise, you'll have to use reflection to attempt to guess the desired interface from all the concrete listener instances provided (hard to determine if all listener objects implement several interfaces in common). 否则,您将不得不使用反射来尝试从所提供的所有具体侦听器实例中猜出所需的接口(很难确定所有侦听器对象是否实现了多个共同的接口)。

Here's the short of it: 这是它的缺点:

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

Which you would use, for example, as follows: 您将使用哪种,例如,如下:

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

This works by simply creating a dynamic Proxy that is cast to the target type T . 这可以通过简单地创建一个强制转换为目标类型T的动态Proxy来实现。 That proxy uses an InvocationHandler that merely delegates all method calls to the proxy to given concrete instances. 该代理使用InvocationHandler ,该InvocationHandler仅将对代理的所有方法调用委托给给定的具体实例。

Note that while in general I finalize all parameters and local variables wherever possible, I only finalized T... instances in this case to highlight the fact that if instances was not final, then referencing it within an anonymous inner class wouldn't be allowed (you'll get a "Cannot refer to a non-final variable args inside an inner class defined in a different method"). 请注意,虽然我总是在尽可能最终确定所有参数和局部变量,但在这种情况下我只完成了T... instances ,以突出显示如果instances 不是 final的事实,则不允许在匿名内部类中引用它(你将得到一个“不能引用在另一个方法中定义的内部类中的非最终变量args”)。

Also note that the above assumes that the actual method calls don't return any meaningful (or useful) values, hence the handler also returns null for all method calls. 另请注意,上面假设实际的方法调用不返回任何有意义(或有用)的值,因此handler也为所有方法调用返回null You'll need to add quite a bit more code if you want to collect return values and return those as well in a meaningful way. 如果要收集返回值并以有意义的方式返回它们,则需要添加更多代码。


Alternatively, one can inspect all given instances to determine the common interfaces they all implement, and pass all those to newProxyInstance() . 或者,可以检查所有给定的instances以确定它们都实现的公共接口,并将所有这些接口传递给newProxyInstance() This makes Mux.create() a lot more convenient to use, at the loss of some control over its behavior. 这使得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);
}

I think your solution is a mix of the " Adapter " and " Factory Method " design patterns. 我认为您的解决方案是“ 适配器 ”和“ 工厂方法 ”设计模式的混合。 On how to implement these patterns in Java, I have provided two useful links below: 关于如何在Java中实现这些模式,我在下面提供了两个有用的链接:

Composite pattern works well for your case. 复合图案适用于您的情况。

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

You might have to refactor AppleListener and OrangeListener to implement a Listener interface that contains a method for the subject to notify the listeners. 您可能必须重构AppleListener和OrangeListener以实现一个Listener接口,该接口包含主体通知侦听器的方法。 CompositeListener would also have to extend this listener to implement the composite pattern. CompositeListener还必须扩展此侦听器以实现复合模式。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM