在Java世界中(更确切地说,如果您没有多个继承/混合),经验法则非常简单:“在类继承上是最喜欢的对象组成”。
我想知道是否/如果您也考虑混入,特别是在scala中,如何更改混入?
是否将mixin视为一种多重继承或更多类组合的方式?
还有“从对象的构图到类的构图”(或相反)的指导原则吗?
当人们使用(或滥用)mixins进行对象组合也可以完成工作时,我已经看到了很多示例,但我并不总是确定哪一个更好。在我看来,您可以通过它们实现非常相似的事情,但是也存在一些差异,例如:
我知道简短的答案是“取决于情况”,但是当这种情况更好时,可能会有一些典型情况。
到目前为止,我可以提出一些准则示例(假设我具有A和B的两个特征,A希望使用B的某些方法):
在许多情况下,mixin似乎更容易(和/或不太冗长),但是我敢肯定,它们也有一些陷阱,例如“上帝类”以及其他两篇artima文章中所述的其他问题:part 1,part 2(顺便说一句我认为其他大多数问题与scala不相关/不太严重)。
您还有更多类似的提示吗?
最佳答案
如果只将抽象特征混入类定义中,然后在对象实例化时混入相应的具体特征,则在Scala中可以避免人们所遇到的许多混合问题。例如
trait Locking{
// abstract locking trait, many possible definitions
protected def lock(body: =>A):A
}
class MyService{
this:Locking =>
}
//For this time, we'll use a java.util.concurrent lock
val myService:MyService = new MyService with JDK15Locking
此构造有几件事值得推荐。首先,由于需要特征功能的不同组合,因此可以防止类爆炸。其次,它可以轻松进行测试,因为它可以创建并混入“不做任何事”的具体特征,类似于模拟对象。最后,我们已经从服务的使用者中完全隐藏了所使用的锁定特性,甚至锁定正在进行中。
由于我们已经克服了混搭所声称的大多数缺点,因此我们仍然需要权衡取舍
在混合和组成之间。对于我自己,我通常根据一个假设的委托(delegate)对象是否被包含的对象完全封装,或者是否有可能被共享并拥有自己的生命周期来做出决定。锁定为完全封装的委托(delegate)提供了一个很好的例子。如果您的类使用锁对象来管理对其内部状态的并发访问,则该锁将完全由包含对象控制,并且该锁及其操作均不会作为该类的公共(public)接口(interface)的一部分进行宣传。对于这样的完全封装的功能,我使用了混入。对于共享的东西,例如数据源,请使用组合。