【问题标题】:How do I reduce delegation boilerplate?如何减少委托样板文件?
【发布时间】:2013-03-23 10:06:47
【问题描述】:

我有一个实现接口的类。还有另一个类也实现了这个接口,第二个类的一个实例支持我的类的实现。

对于接口指定的许多方法,我的类只是将它们直接转发给第二个类。

public class MyClass implements MyInterface
{
    private OtherClass otherClassInstance; // Also implements MyInterface.
    // ...
    void foo() { otherClassInstance.foo(); }
    void bar() { otherClassInstance.bar(); }
    void baz() { otherClassInstance.baz(); }
    // ...
}

简单地从第二个类派生我的类将消除所有这些,但这没有意义,因为这两个类彼此无关(除了实现一个公共接口之外)。它们代表不同的东西——碰巧我的很多类的实现都复制了另一个类的实现。换句话说,我的类是在第二类之上实现的,但它本身并不是第二类的子集。我们知道,继承是为了表达“is-a”关系,而不是共享实现,所以在这种情况下是不合适的。

This Joshua Bloch 的一段谈话很好地说明了这种情况。

我知道 Java 对委派没有任何语言支持。但是,是否至少有某种方法可以清理我的类的实现,使其不那么多余?

【问题讨论】:

  • 让我直截了当地说...您想扩展 ArrayList,但并非在所有情况下,界面都不会削减它...如果您的 MyList 主要归结为 ArrayList,那么它会扩展 ArrayList 并相应地处理非 arraylist 列表真的很有意义。
  • 通常我会检查“MyList”的原因。大多数时候,它是一个 ArrayList,在某些情况下我需要特殊的行为。对于这种情况,我使用 ArrayList 并构建 Wrapper/Adapter/commands 来提供功能。而且大部分时间我都会查找 Guava,并免费获得所需的扩展。
  • @Shark 不,我不想扩展ArrayList。我正在创建自己的类来实现List,而ArrayList 成员只是支持我的实现。使用MyList 对象代替ArrayList 对象确实有意义 - 永远。
  • “每个 MyList 都不是 ArrayList。它们代表不同的东西。MyList 恰好复制了 ArrayList 的大部分实现。”对我来说,这就是 ArrayList 子对象的定义:它覆盖了一些方法,但它们中的大多数是相等的。
  • 你知道你需要什么以及为什么要这样做 Maxpm,所以也许你应该解释为什么扩展 ArrayList 是一个错误的举动?此外,谁会使用你的MyList 而不是ArrayList

标签: java oop delegation boilerplate


【解决方案1】:

第一种使用http://java.sun.com/javase/6/docs/api/java/lang/reflect/Proxy.html的方法见教程http://docs.oracle.com/javase/1.4.2/docs/guide/reflection/proxy.html

使用 AOP 的第二种方式,您可以创建拦截特定类的所有调用的调度程序

对于这两种方式,您都需要使用反射 API 管理方法处理

为展示想法而编辑 以下从上面的教程中获取的代码只是稍微修改了一下(参见调用方法中的 youListImpl.getRealArrayList())

public class DebugProxy implements java.lang.reflect.InvocationHandler {

    private YouListImpl youListImpl;

    public static Object newInstance(Object obj) {
    return java.lang.reflect.Proxy.newProxyInstance(
        obj.getClass().getClassLoader(),
        obj.getClass().getInterfaces(),
        new DebugProxy(obj));
    }

    private DebugProxy(Object obj) {
    this.obj = obj;
    }

    public Object invoke(Object proxy, Method m, Object[] args)
    throws Throwable
    {
        Object result;
    try {
        System.out.println("before method " + m.getName());
        result = m.invoke(youListImpl.getRealArrayList(), args);
        } catch (InvocationTargetException e) {
        throw e.getTargetException();
        } catch (Exception e) {
        throw new RuntimeException("unexpected invocation exception: " +
                       e.getMessage());
    } finally {
        System.out.println("after method " + m.getName());
    }
    return result;
    }
}

【讨论】:

  • 您能否提供在这种情况下如何使用反射 API 的具体示例?
  • @Maxpm 只是放置一个快照
【解决方案2】:

一个不是你实际问题的真正答案的答案:

我会说,与样板一起生活。让 IDE 为您生成它。示例:在 Netbeans 中,添加私有 ArrayList 字段,将光标设置到您希望方法出现的位置,点击 alt-insert,选择“Generate Delegate Method...”,单击您想要的方法想要在打开的对话框中创建一个委托,提交,检查生成的方法并让它们做正确的事情,你就完成了。

这有点难看,但当你只处理一个类时,它仍然比开始弄乱反射更可取,就像听起来一样。您的课程可能是您将完成并全面测试的课程,然后希望永远不会再碰它。反射会产生不会消失的运行时成本。在这种情况下,最好在源文件中使用自动生成的样板。

【讨论】:

  • 而且反射很混乱,容易出错,并且在运行时崩溃。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-26
  • 2020-03-31
  • 1970-01-01
相关资源
最近更新 更多