Mixins vs scala中的成分

San*_*ozi 77 scala composition mixins

在java世界中(更确切地说,如果你没有多重继承/ mixins),经验法则很简单:"赞成对象组合而不是类继承".

如果你还考虑mixins,我想知道它是否/如何改变,特别是在scala中?
mixins被认为是多重继承的方式,还是更多的类组合?
是否还有一个"赞成对象组成超类组成"(或其他方式)指南?

当人们使用(或滥用)mixins时,我已经看到了一些例子,当对象组合也可以完成这项工作时,我并不总是确定哪一个更好.在我看来,你可以用它们实现非常相似的东西,但也有一些差异,一些例子:

  • 可见性 - 使用mixins,一切都成为公共API的一部分,而不是组合的情况.
  • 冗长 - 在大多数情况下,mixins不那么冗长,也更容易使用,但情况并非总是如此(例如,如果你也在复杂的层次结构中使用自我类型)

我知道简短的回答是"它取决于",但可能有一些典型的情况,当这个或那个更好.

到目前为止,我可以提出一些指导原则(假设我有两个特征A和B,A想要使用B中的一些方法):

  • 如果你想用B中的方法扩展A的API,那么就是mixins,否则就是组合.但是,如果我创建的类/实例不是公共API的一部分,它就无济于事.
  • 如果你想使用一些需要mixins的模式(例如Stackable Trait Pattern),那么这是一个简单的决定.
  • 如果你有循环依赖,那么mixins与self类型可以提供帮助.(我尽量避免这种情况,但这并不容易)
  • 如果你想要一些动态的,运行时决定如何组合然后对象组合.

在很多情况下,mixins似乎更容易(和/或更简洁),但我很确定它们也有一些陷阱,比如"神级"和其他两篇artima文章中描述的其他:第1 部分,第2部分(BTW it在我看来,大多数其他问题与scala不相关/不那么严重.

你有更多这样的提示吗?

Dav*_*ith 40

如果你只将抽象特征混合到你的类定义中,然后在对象实例化时混合相应的具体特征,那么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 
Run Code Online (Sandbox Code Playgroud)

这个结构有几个值得推荐的东西.首先,由于需要不同的特质功能组合,它可以防止类别爆炸.其次,它允许简单的测试,因为可以创建和混合"无所事事"的具体特征,类似于模拟对象.最后,我们完全隐藏了所使用的锁定特性,甚至是锁定特性,来自我们服务的消费者.

由于我们已经超越了大多数声称混合的缺点,我们仍然需要在混合和组合之间进行权衡.对于我自己,我通常根据假设的委托对象是否完全由包含对象封装,或者它是否可能被共享并具有自己的生命周期来做出决定.锁定提供了完全封装的委托的一个很好的例子.如果您的类使用锁定对象来管理对其内部状态的并发访问,则该锁定完全由包含对象控制,并且它及其操作都不会作为类的公共接口的一部分进行通告.对于像这样的完全封装的功能,我使用mix-ins.对于共享的内容,如数据源,请使用合成.

  • 这是一个自我类型的注释,我总是忘记它的语法.编辑.在这种情况下,它意味着任何扩展MyService的对象也必须扩展锁定 (3认同)

Von*_*onC 10

你没有提到的其他差异:

  • 特质课没有任何独立存在:

(Scala编程)

如果您发现特定性状是最常用的其他类的父,让子类表现为母特质,然后再考虑定义特征的一类,而不是,使这个逻辑关系更加清晰.
(我们说表现为,而不是一个,因为前者是继承更精确的定义的基础上,里氏替换原则-见[Martin2003],例如).

[Martin2003]:罗伯特C.马丁,敏捷软件开发:原则,模式与实践,普伦蒂斯霍尔,2003

  • mixins(trait)没有构造函数参数.

因此,仍然来自Programming Scala建议:

避免使用无法初始化为合适默认值的特征中的具体字段.
改为使用抽象字段或使用构造函数将特征转换为类.
当然,无状态特征在初始化时没有任何问题.

良好的面向对象设计的一般原则是,实例应始终处于已知的有效状态,从构造过程完成的那一刻开始.

最后一部分,关于对象的初始状态,经常帮助决定给定概念的类(和类组合)和特征(和混合).