例如,假设我有这样的代码:
public class Example { // QuietFoo is from another library that I can't change private static QuietFoo quietFoo; // LoudFoo is my own code and is meant to replace QuietFoo private static LoudFoo loudFoo; public static void main(String[] args) { handle(foo); } private static void handle(Object foo) { if (foo instanceof QuietFoo) ((QuietFoo) foo).bar(); else if (foo instanceof LoudFoo) ((LoudFoo) foo).bar(); } }
我不能改变QuietFoo:
public class QuietFoo { public void bar() { System.out.println("bar"); } }
但我可以改变LoudFoo:
public class LoudFoo { public void bar() { System.out.println("BAR!!"); } }
问题是,在许多类中可能还有许多其他的bar实现,并且可能有更多的方法而不仅仅是bar,所以不仅我的handle方法会因为大量的instanceof语句而变得缓慢而丑陋,但是我不得不写一个这些处理QuietFoo和LoudFoo上每个方法的方法.扩展不是一个可行的解决方案,因为它违反了整个合同,因为LoudFoo不是QuietFoo.
基本上,给予Foo:
public interface Foo { void bar(); }
解决方法
>使用适配器模式
>使用Proxy
适配器方法将更简单但灵活性更低,并且代理方法将更复杂但更灵活.尽管代理方法更复杂,但这种复杂性仅限于几个类.
适配器
adapter pattern很简单.对于您的示例,它只是一个类,如下所示:
public class QuietFooAdapter implements Foo { private QuietFoo quietFoo; public QuietFooAdapter(QuietFoo quietFoo) { this.quietFoo = quietFoo; } public void bar() { quietFoo.bar(); } }
然后使用它:
Foo foo = new QuietFooAdapter(new QuietFoo()); foo.bar();
这很好,但如果你有一个以上的类来制作适配器,这可能是单调乏味的,因为你需要为每个必须包装的类添加一个新的适配器.
Java的代理类
Proxy是一个本机Java类,它是反射库的一部分,允许您创建更通用的反射解决方案.它涉及3个部分:
>界面(在这种情况下,Foo)
> InvocationHandler
>创建代理(Proxy.newProxyInstance
)
我们已经有了界面,所以我们在那里很好.
InvocationHandler是我们通过反射“自动适应”的地方:
public class AdapterInvocationHandler implements InvocationHandler { private Object target; private Class<?> targetClass; public AdapterInvocationHandler(Object target) { this.target = target; targetClass = target.getClass(); } public Object invoke(Object proxy,Method method,Object[] args) throws Throwable { try { Method targetMethod = targetClass.getMethod(method.getName(),method.getParameterTypes()); if (!method.getReturnType().isAssignableFrom(targetMethod.getReturnType())) throw new UnsupportedOperationException("Target (" + target.getClass().getName() + ") does not support: " + method.toGenericString()); return targetMethod.invoke(target,args); } catch (NoSuchMethodException ex) { throw new UnsupportedOperationException("Target (" + target.getClass().getName() + ") does not support: " + method.toGenericString()); } catch (IllegalAccessException ex) { throw new UnsupportedOperationException("Target (" + target.getClass().getName() + ") does not declare method to be public: " + method.toGenericString()); } catch (InvocationTargetException ex) { // May throw a NullPointerException if there is no target exception throw ex.getTargetException(); } } }
这里重要的代码在try块中.这将处理将代理上调用的任何方法调用适配到内部目标对象的过程.如果在不支持的接口上调用了一个方法(非公共,错误的返回类型,或者只是不存在),那么我们抛出UnsupportedOperationException.如果我们捕获InvocationTargetException,我们将通过InvocationTargetException.getTargetException重新抛出导致它的异常.当我们调用的方法反射抛出异常时会发生这种情况. Java将其包装在一个新的异常中并抛出该新异常.
接下来,我们需要一些东西来创建适配器:
public class AdapterFactory { public static <T> T createAdapter(Object target,Class<T> interfaceClass) { if (!interfaceClass.isInterface()) throw new IllegalArgumentException("Must be an interface: " + interfaceClass.getName()); return (T) Proxy.newProxyInstance(null,new Class<?>[] { interfaceClass },new AdapterInvocationHandler(target)); } }
如果愿意,您还可以将AdapterInvocationHandler类嵌套在AdapterFactory类中,以便AdapterFactory中的所有内容都是自包含的.
然后使用它:
Foo foo = AdapterFactory.createAdapter(new QuietFoo(),Foo.class); foo.bar();
这种方法比实现单个适配器需要更多的代码,但是它足够通用,可以用于为任何类和接口对创建自动适配器,而不仅仅是QuietFoo和Foo示例.当然,这种方法使用反射(Proxy类使用反射,我们的InvocationHandler也是如此),这可能会更慢,但JVM的最近改进使得反射比以前快得多.