使用多种接口的最佳方法是什么?

时间:2012-02-20 13:05:54

标签: java generics interface

我有一种情况,我有很多模型类(~1000),可以实现任意数量的5个接口。所以我有一些实现一个的类和一个实现四个或五个的类。

这意味着我可以对这五个接口进行任何排列。在经典模型中,我必须实现32-5 = 27“元接口”,它们“捆绑”接口中的接口。通常,这不是问题,因为IB通常扩展IA等,但在我的情况下,五个接口是正交/独立的。

在我的框架代码中,我有一些方法需要实现任意数量的接口的实例。因此,我们假设我们有类X和接口IAIBICIDIEX实施IAIDIE

情况变得更糟,因为其中一些接口有formal type parameters

我现在有两个选择:

  1. 我可以定义一个界面IADE(或更确切地说是IPersistable_MasterSlaveCapable_XmlIdentifierProvider;只是为了您的阅读乐趣而强调)

  2. 我可以将通用类型定义为<T extends IPersistable & IMasterSlaveCapable & IXmlIdentifierProvider>,这将为我提供一种方便的混合方式。我需要匹配接口。

  3. 我可以使用这样的代码:IA a = ...; ID d = (ID)a; IE e = (IE)e然后使用具有正确类型的局部变量来调用方法,即使所有三个都在同一个实例上工作。或者在每个第二次方法调用中使用强制转换。

  4. 第一个解决方案意味着我获得了许多名称非常难以理解的空接口。

    第二种使用一种“ad-hoc”打字。而且,当Eclipse正确的时候,Oracle的javac有时会发现它们。

    最后一个解决方案使用强制转换。努夫说。

    问题:

    1. 是否有更好的解决方案来混合任意数量的接口?

    2. 有没有理由避免解决#2为我提供的临时类型(除了Oracle的javac中的缺点)?

    3. 注意:我知道编写不能用Oracle javac编译的代码是一种风险。我们知道我们可以应对这种风险。

      [编辑] 我在这里尝试尝试的东西似乎有些混乱。我的模型实例可以具有以下特征之一:

      • 他们可以是“掌握奴隶主”(想想克隆)
      • 他们可以拥有XML标识符
      • 他们可能支持树操作(父/子)
      • 他们可能支持修订
      • 等。 (是的,模型比那更复杂)

      现在我有支持代码,可以在树上运行。树木的延伸是树木修改。但我也没有树木的修改。

      当我在代码中添加修订树管理器中的子代时,我知道每个实例都必须实现ITtreeIRevisionable,但两者都没有通用接口,因为它们完全是独立的关注。

      但是在实现中,我需要在树的节点上调用方法:

      public void addChild( T parent, T child ) {
          T newRev = parent.createNewRevision();
          newRev.addChild( foo );
          ... possibly more method calls to other interfaces ...
      }
      

      如果界面createNewRevision中有IRevisionable且界面addChild中有ITree,我可以选择定义T吗?

      注意:假设我有其他几个以类似方式工作的接口:有很多地方它们是独立的,但有些代码需要看到它们的混合。 IRevisionableTree不是解决方案,而是另一个问题。

      我可以为每个电话投出类型,但这看起来很笨拙。创建接口的所有排列都很无聊,似乎没有合理的模式来压缩巨大的接口名称。泛型提供了一个很好的出路:

      public
      <T extends IRevisionable & ITree>
      void addChild( T parent, T child ) { ... }
      

      这并不总是适用于Oracle的javac,但它似乎紧凑而有用。还有其他选择/评论吗?

5 个答案:

答案 0 :(得分:3)

松散耦合的功能可能很有趣。一个example here。 这是一种完全不同的方法;解耦事物而不是打字。 基本上,接口是隐藏的,实现为委托字段。

IA ia = x.lookupCapability(IA.class);
if (ia != null) {
    ia.a();
}

它适用于此,与许多接口一样,希望解耦上升,并且您可以更轻松地组合相互依赖的接口(if (ia != null && ib != null) ...)。

答案 1 :(得分:3)

如果你有方法(semicode)

void doSomething(IA & ID & IE thing);

然后我主要担心的是:doSomething能否更好地量身定制?拆分功能可能会更好吗?或者接口本身是否经过严格定制?

我偶然发现了几次相似的事情,每次证明更好地采取 big 后退并重新考虑逻辑的完整分区 - 不仅仅是因为你提到的东西而且由于其他问题。

由于你非常抽象地提出了你的问题(即没有明智的例子),我不能告诉你这是否也适用于你的情况。

答案 2 :(得分:2)

我会避免所有试图表示组合的“仿制”接口/类型。这只是糟糕的设计...如果再添加5个接口会发生什么?组合数量爆炸。

似乎你想知道某个实例是否实现了某些接口。合理的选择是:

  • 使用instanceof - 没有羞耻
  • 使用反射通过object.getClass().getInterfaces()发现接口 - 您可以编写一些通用代码来处理内容
  • 使用反射通过object.getClass().getMethods()发现方法,并调用与接口的已知方法列表匹配的方法(这种方法意味着您不必关心它实现的内容 - 听起来很简单,因此听起来像一个好主意)

您没有向我们提供关于为什么您想要了解的背景信息,因此很难说“最佳”方法是什么。

被修改

行。由于你的额外信息被添加,它开始有意义。这里最好的方法是使用a回调:传入一个接受“child”的接口,而不是传入父对象。

这是访客模式的简单版本。您的调用代码知道它正在调用的内容以及它如何处理子代,但导航和/或决定添加子代的代码没有调用者的上下文。

您的代码看起来像这样(警告:可能无法编译;我只是键入它):

public interface Parent<T> {
    void accept(T child);
}

// Central code - I assume the parent is passed in somewhere earlier
public void process(Parent<T> parent) {
    // some logic that decides to add a child
    addChild(parent, child);
}

public void addChild(Parent<T> parent, T child ) {
    parent.accept(child);
}

// Calling code
final IRevisionable revisionable = ...;
someServer.process(new Parent<T> {
    void accept(T child) {
        T newRev = revisionable.createNewRevision();
        newRev.addChild(child);
    }
}

你可能不得不玩弄周围的东西,但我希望你能理解我想说的话。

答案 3 :(得分:0)

实际上,解决方案1是一个很好的解决方案,但你应该找到一个更好的命名。

实际上你会命名一个实现IPersistable_MasterSlaveCapable_XmlIdentifierProvider接口的类?如果遵循良好的命名约定,它应该具有源自模型实体的有意义的名称。您可以为界面指定与I前缀相同的名称。

我觉得拥有很多接口并不是一个缺点,因为你可以编写模拟实现来进行测试。

答案 4 :(得分:0)

  

我的情况恰恰相反:我知道代码中的某些特定点,   foo必须实现IA,ID和IE(否则,它无法实现   远)。现在我需要在所有三个接口中调用方法。什么类型   应该得到吗?

您是否能够通过传递(例如)三个对象来完全绕过问题?所以而不是:

doSomethingWithFoo(WhatGoesHere foo);
你这样做:

doSomethingWithFoo(IA foo, ID foo, IE foo);

或者,您可以创建一个实现所有接口的代理,但允许您禁用某些接口(即调用'错误'接口会导致UnsupportedOperationException)。

最后一个疯狂的想法 - 可能可以为适当的接口创建动态代理,委托给您的实际对象。