我有一种情况,我有很多模型类(~1000),可以实现任意数量的5个接口。所以我有一些实现一个的类和一个实现四个或五个的类。
这意味着我可以对这五个接口进行任何排列。在经典模型中,我必须实现32-5 = 27“元接口”,它们“捆绑”接口中的接口。通常,这不是问题,因为IB
通常扩展IA
等,但在我的情况下,五个接口是正交/独立的。
在我的框架代码中,我有一些方法需要实现任意数量的接口的实例。因此,我们假设我们有类X
和接口IA
,IB
,IC
,ID
和IE
。 X
实施IA
,ID
和IE
。
情况变得更糟,因为其中一些接口有formal type parameters。
我现在有两个选择:
我可以定义一个界面IADE
(或更确切地说是IPersistable_MasterSlaveCapable_XmlIdentifierProvider
;只是为了您的阅读乐趣而强调)
我可以将通用类型定义为<T extends IPersistable & IMasterSlaveCapable & IXmlIdentifierProvider>
,这将为我提供一种方便的混合方式。我需要匹配接口。
我可以使用这样的代码:IA a = ...; ID d = (ID)a; IE e = (IE)e
然后使用具有正确类型的局部变量来调用方法,即使所有三个都在同一个实例上工作。或者在每个第二次方法调用中使用强制转换。
第一个解决方案意味着我获得了许多名称非常难以理解的空接口。
第二种使用一种“ad-hoc”打字。而且,当Eclipse正确的时候,Oracle的javac
有时会发现它们。
最后一个解决方案使用强制转换。努夫说。
问题:
是否有更好的解决方案来混合任意数量的接口?
有没有理由避免解决#2为我提供的临时类型(除了Oracle的javac
中的缺点)?
注意:我知道编写不能用Oracle javac
编译的代码是一种风险。我们知道我们可以应对这种风险。
[编辑] 我在这里尝试尝试的东西似乎有些混乱。我的模型实例可以具有以下特征之一:
现在我有支持代码,可以在树上运行。树木的延伸是树木修改。但我也没有树木的修改。
当我在代码中添加修订树管理器中的子代时,我知道每个实例都必须实现ITtree
和IRevisionable
,但两者都没有通用接口,因为它们完全是独立的关注。
但是在实现中,我需要在树的节点上调用方法:
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
,但它似乎紧凑而有用。还有其他选择/评论吗?
答案 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)。
最后一个疯狂的想法 - 可能可以为适当的接口创建动态代理,委托给您的实际对象。