使用多种界面的最佳方式是什么?
我遇到的情况是,我有很多模型类(~1000),它们实现了任意数量的 5 个接口。所以我有一些类实现了一个,而其他类则实现了四到五个。
这意味着我可以对这五个接口进行任意排列。在经典模型中,我必须实现 32-5 = 27 个“元接口”,将接口“加入”到一个包中。通常,这不是问题,因为 IB
通常扩展 IA
等,但就我而言,五个接口是正交/独立的。
在我的框架代码中,我有一些方法需要实现任意数量的这些接口的实例。因此,假设我们有类 X
和接口 IA
、IB
、IC
、ID
和 IE
。 X
实现了 IA
、ID
和 IE
。
情况变得更糟,因为其中一些接口具有 正式类型参数。
我现在有两个选择:
我可以定义一个接口
IADE
(或者更确切地说IPersistable_MasterSlaveCapable_XmlIdentifierProvider
;下划线只是为了您的阅读乐趣)我可以将泛型类型定义为
这将为我提供一种方便的方法来混合和使用根据需要匹配接口。我可以使用这样的代码:
IA a = ...; ID d = (ID)a; IE e = (IE)e
然后使用具有正确类型的局部变量来调用方法,即使这三个变量都在同一个实例上工作。或者在每个第二个方法调用中使用强制转换。
第一个解决方案意味着我得到了很多名称非常不可读的空接口。
第二种使用一种“临时”打字。 Oracle 的 javac
有时会遇到这些问题,而 Eclipse 却能正确处理。
最后一个解决方案使用强制转换。纳夫说道。
问题:
是否有更好的混合任意数量接口的解决方案?
是否有任何理由避免使用解决方案 #2 提供的临时类型(除了 Oracle 的
javac
中的缺点)?
注意:我知道编写不能用 Oracle 的 javac
编译的代码是有风险的。我们知道我们可以应对这种风险。
[编辑] 我在这里尝试的似乎有些混乱。我的模型实例可以具有以下特征之一:
- 它们可以是“主从能力”(想想克隆)
- 它们可以有 XML 标识符
- 它们可能支持树操作(父/子)
- 它们可能支持修订
- 等。(是的,该模型是甚至比这更复杂)
现在我有了在树上运行的支持代码。树的扩展是带有修订的树。但我也有没有树的修订。
当我在代码中在修订树管理器中添加子级时,我知道每个实例都必须实现 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,但它看起来紧凑且有用。还有其他选项/评论吗?
I have a situation where I have have a lot of model classes (~1000) which implement any number of 5 interfaces. So I have classes which implement one and others which implement four or five.
This means I can have any permutation of those five interfaces. In the classical model, I would have to implement 32-5 = 27 "meta interfaces" which "join" the interfaces in a bundle. Often, this is not a problem because IB
usually extends IA
, etc. but in my case, the five interfaces are orthogonal/independent.
In my framework code, I have methods which need instances that have any number of these interfaces implemented. So lets assume that we have the class X
and the interfaces IA
, IB
, IC
, ID
and IE
. X
implements IA
, ID
and IE
.
The situation gets worse because some of these interfaces have formal type parameters.
I now have two options:
I could define an interface
IADE
(or ratherIPersistable_MasterSlaveCapable_XmlIdentifierProvider
; underscores just for your reading pleasure)I could define a generic type as
<T extends IPersistable & IMasterSlaveCapable & IXmlIdentifierProvider>
which would give me a handy way to mix & match interfaces as I need them.I could use code like this:
IA a = ...; ID d = (ID)a; IE e = (IE)e
and then use the local variable with the correct type to call methods even though all three work on the same instance. Or use a cast in every second method call.
The first solution means that I get a lot of empty interfaces with very unreadable names.
The second uses a kind of "ad-hoc" typing. And Oracle's javac
sometimes stumbles over them while Eclipse gets it right.
The last solution uses casts. Nuff said.
Questions:
Is there a better solution for mixing any number of interfaces?
Are there any reasons to avoid the temporary types which solution #2 offers me (except for shortcomings in Oracle's
javac
)?
Note: I'm aware that writing code which doesn't compile with Oracle's javac
is a risk. We know that we can handle this risk.
[Edit] There seems to be some confusion what I try to attempt here. My model instances can have one of these traits:
- They can be "master slave capable" (think cloning)
- They can have an XML identifier
- They might support tree operations (parent/child)
- They might support revisions
- etc. (yes, the model is even more complex than that)
Now I have support code which operates on trees. An extensions of trees are trees with revisions. But I also have revisions without trees.
When I'm in the code to add a child in the revision tree manager, I know that each instance must implement ITtree
and IRevisionable
but there is no common interface for both because these are completely independent concerns.
But in the implementation, I need to call methods on the nodes of the tree:
public void addChild( T parent, T child ) {
T newRev = parent.createNewRevision();
newRev.addChild( foo );
... possibly more method calls to other interfaces ...
}
If createNewRevision
is in the interface IRevisionable
and addChild
is in the interface ITree
, what are my options to define T
?
Note: Assume that I have several other interfaces which work in a similar way: There are many places where they are independent but some code needs to see a mix of them. IRevisionableTree
is not a solution but another problem.
I could cast the type for each call but that seems clumsy. Creating all permutations of interfaces would be boring and there seems no reasonable pattern to compress the huge interface names. Generics offer a nice way out:
public
<T extends IRevisionable & ITree>
void addChild( T parent, T child ) { ... }
This doesn't always work with Oracle's javac
but it seems compact and useful. Any other options/comments?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
松散耦合的功能可能会很有趣。 示例。
这是一种完全不同的方法;解耦事物而不是打字。
基本上接口是隐藏的,作为委托字段实现。
它适合这里,就像许多接口一样,解耦的愿望也会增加,并且您可以更轻松地组合相互依赖的接口的情况 (
if (ia != null && ib != null) ...
)。Loosely coupled capabilities might be interesting. An example here.
It is an entirely different approach; decoupling things instead of typing.
Basically interfaces are hidden, implemented as delegating field.
It fits here, as with many interfaces the wish to decouple rises, and you can more easily combine cases of interdepending interfaces (
if (ia != null && ib != null) ...
).如果您有一个方法(半代码),
那么我主要关心的是:不能更好地定制
doSomething
吗?拆分功能可能会更好吗?还是界面本身定制不当?我已经多次遇到类似的事情,每次都证明最好向后退一步并重新思考逻辑的完整划分 - 不仅由于您提到的内容,还由于其他内容的担忧。
由于您非常抽象地提出了您的问题(即没有合理的例子),我无法告诉您这对于您的情况是否也是可取的。
If you have a method (semicode)
then my main concern is: Couldn't
doSomething
be better tailored? Might it be better to split up the functionality? Or are the interfaces itself badly tailored?I have stumbled over similar things several times and each time it proved to be better to take big step backward and rethink the complete partitioning of the logic - not only due to the stuff you mentioned but also due to other concerns.
Since you formulated your question very abstractly (i.e. without a sensible example) I cannot tell you if that's advisable in your case also.
我会避免所有尝试表示组合的“人工”接口/类型。这只是糟糕的设计...如果再添加 5 个接口会发生什么?组合的数量呈爆炸式增长。
看来您想知道某个实例是否实现了某些接口。合理的选择是:
instanceof
- 没有什么可耻的,object.getClass().getInterfaces()
发现接口 - 你也许可以写一些通用的处理内容的代码object.getClass().getMethods()
发现方法,并仅调用与接口的已知方法列表匹配的方法(这种方法意味着您没有关心它实现了什么 - 听起来很简单因此听起来是个好主意)您没有向我们提供任何背景信息来说明您想知道的具体原因,因此很难说出“最佳”方法是什么。
编辑
好。自从添加了您的额外信息后,它就开始有意义了。这里最好的方法是使用回调:不要传入父对象,而是传入接受“子对象”的接口。
这是访问者模式的简单版本。您的调用代码知道它正在调用什么以及它如何处理子项,但是导航和/或决定添加子项的代码没有调用者的上下文。
您的代码看起来像这样(警告:可能无法编译;我只是输入了它):
您可能必须处理一些事情,但我希望您理解我想说的内容。
I would avoid all "artificial" interfaces/types that attempt to represent combinations. It's just bad design... what happens if you add 5 more interfaces? The number of combinations explodes.
It seems you want to know if some instance implements some interface(s). Reasonable options are:
instanceof
- there is no shameobject.getClass().getInterfaces()
- you may be able to write some general code to process stuffobject.getClass().getMethods()
and just invoke those that match a known list of methods of your interfaces (this approach means you don't have to care what it implements - sounds simple and therefore sounds like a good idea)You've given us no context as to exactly why you want to know, so it's hard to say what the "best" approach is.
Edited
OK. Since your extra info was added it's starting to make sense. The best approach here is to use the a callback: Instead of passing in a parent object, pass in an interface that accepts a "child".
It's a simplistic version of the visitor pattern. Your calling code knows what it is calling with and how it can handle a child, but the code that navigates around and/or decides to add a child doesn't have context of the caller.
Your code would look something like this (caveat: May not compile; I just typed it in):
You may have to juggle things around, but I hope you understand what I'm trying to say.
实际上解决方案1是一个很好的解决方案,但是您应该找到更好的命名。
实际上,您会将实现
IPersistable_MasterSlaveCapable_XmlIdentifierProvider
接口的类命名为什么?如果遵循良好的命名约定,它应该具有源自模型实体的有意义的名称。您可以为接口指定相同的名称,并以I
为前缀。我不认为拥有许多接口是一个缺点,因为这样您就可以编写模拟实现来进行测试。
Actually solution 1 is a good solution, but you should find a better naming.
What actually would you name a class that implements the
IPersistable_MasterSlaveCapable_XmlIdentifierProvider
interface? If you follow good naming convention, it should have a meaningful name originating from a model entity. You can give the interface the same name prefixed withI
.I don't find it a disadvantage to have many interfaces, because like that you can write mock implementations for testing purposes.
您是否能够通过传递(例如)三个对象来完全绕过该问题?因此,
您可以:
或者,您可以创建一个实现所有接口的代理,但允许您禁用某些接口(即调用“错误”接口会导致 UnsupportedOperationException)。
最后一个疯狂的想法 - 也许可以为适当的接口创建动态代理,将其委托给您的实际对象。
Are you able to bypass the problem entirely by passing (for example) three objects? So instead of:
you do:
Or, you could create a proxy that implements all interfaces, but allows you to disable certain interfaces (i.e. calling the 'wrong' interface causes an UnsupportedOperationException).
One final wild idea - it might be possible to create Dynamic Proxies for the appropriate interfaces, that delegate to your actual object.