Mixins vs scala中的组合
在Java世界中(更确切地说,如果你没有多重inheritance/混合),经验法则很简单:“赞成inheritance类的对象组合”。
我想知道是否/如何改变,如果你也考虑mixin,特别是在斯卡拉?
mixin被认为是多重inheritance的一种方式,或者是更多的类构成?
是否还有一个“赞成对象构成上课组成”(或反过来)的指导方针?
当人们使用(或滥用)mixins的时候,我也看到了一些例子,当对象组合也可以完成这个工作时,我并不总能确定哪一个更好。 在我看来,你可以和他们取得相似的东西,但也有一些差异,例如:
- 知名度 – 与mixin一切都成为公共API的一部分,这是不是组成的情况。
- 详细程度 – 在大多数情况下,mixin不那么冗长而且易于使用,但并非总是如此(例如,如果您还在复杂层次结构中使用自我types)
我知道简短的答案是“这取决于”,但是当这个或那个更好时,可能有一些典型的情况。
目前我可以提出一些指导方针的例子(假设我有两个性状A和B,A想用B方法):
- 如果你想用B的方法扩展A的API,那么混合,否则组合。 但是,如果我创build的类/实例不是公共API的一部分,则无济于事。
- 如果你想使用一些需要mixin的模式(例如Stackable Trait Pattern ),那么这是一个简单的决定。
- 如果你有循环依赖,那么mixins与自我types可以帮助。 (我尽量避免这种情况,但并不总是那么容易)
- 如果你想要一些dynamic的,运行时决定如何做组合然后对象组成。
在很多情况下,mixin似乎更容易(和/或不那么冗长),但是我确定他们也有一些陷阱,比如两个artima文章中描述的“上帝阶级”和其他一些陷阱: 第1 部分 , 第2部分在我看来,大多数其他问题是不相关的/不太严重的斯卡拉)。
你有更多的提示吗?
如果只将抽象特征混入到类定义中,那么人们在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
这个构造有几个东西来推荐它。 首先,由于需要特征function的不同组合,所以它防止了类的爆炸。 其次,它允许简单的testing,因为人们可以创build和混合“无所事事”的具体特征,类似于模拟对象。 最后,我们完全隐藏了使用的locking特性,甚至locking正在进行,从我们的服务消费者。
由于我们已经过去了大部分声称的混音缺陷,我们仍然在混音和合成之间进行权衡。 对于我自己,我通常会根据假设的委托对象是否完全被包含对象封装,或者是否可能被共享并具有它自己的生命周期来作出决定。 locking提供了完全封装委托的一个很好的例子。 如果您的类使用锁对象来pipe理对其内部状态的并发访问,则该锁完全由包含对象控制,并且它的操作和操作都不作为类public interface的一部分进行公布。 对于像这样的完全封装的function,我使用混入。 对于共享的东西,如数据源,使用组合。
你还没有提到的其他区别:
- 特质类没有任何独立的存在:
( 编程斯卡拉 )
如果你发现一个特定的特征经常被用作其他类的父类,那么这个子类作为父特征,那么考虑将特征定义为一个类,以使这个逻辑关系更加清晰。
(我们说的行为是 ,而不是一个 ,因为前者是inheritance的更精确的定义,基于Liskov替代原则 – 例如参见[Martin2003])。
[Martin2003]:Robert C. Martin,敏捷软件开发:原则,模式和实践,Prentice-Hall,2003
- mixins(
trait
)没有构造函数参数。
因此, build议,仍然从编程斯卡拉 :
避免具有无法初始化为合适的默认值的特征字段。
改为使用抽象字段, 或使用构造函数将特征转换为类 。
当然,无状态特性在初始化时没有任何问题。这是一个良好的面向对象devise的一般原则,一个实例应该始终处于已知的有效状态,从施工过程结束的那一刻开始 。
关于对象的初始状态 ,最后一部分经常帮助确定一个给定的概念的类(和类的构成)和特性(和混合)。