80

在 Java 世界中(更准确地说,如果您没有多重继承/混合),经验法则非常简单:“优先考虑对象组合而不是类继承”。

我想知道如果您还考虑mixins,它是否/如何改变,尤其是在scala中?
mixins 是否被认为是一种多重继承或更多类组合的方式?
是否还有“优先对象组合优于类组合”(或相反)指南?

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

  • 可见性 - 使用 mixins,一切都成为公共 api 的一部分,而组合则不是这样。
  • 冗长 - 在大多数情况下,mixin 不那么冗长并且更易于使用,但并非总是如此(例如,如果您还在复杂的层次结构中使用 self 类型)

我知道简短的回答是“视情况而定”,但可能有一些典型的情况是这样或那样更好。

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

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

在许多情况下,mixin 似乎更容易(和/或不那么冗长),但我很确定它们也有一些陷阱,比如“上帝类”和两篇 artima 文章中描述的其他内容:第 1部分,第 2 部分(顺便说一句在我看来,大多数其他问题与 scala 无关/不那么严重)。

你有更多这样的提示吗?

4

2 回答 2

41

如果你只在类定义中混入抽象特征,然后在对象实例化时混入相应的具体特征,那么在 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 

这个结构有几件事值得推荐。首先,它可以防止类爆炸,因为需要不同的特征功能组合。其次,它允许轻松测试,因为可以创建和混合“无所事事”的具体特征,类似于模拟对象。最后,我们已经完全隐藏了使用的锁定特性,即使是锁定仍在继续,对我们服务的消费者来说也是如此。

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

于 2010-08-06T19:30:20.807 回答
11

您未提及的其他差异:

  • 特征类没有任何独立存在:

编程斯卡拉

如果您发现某个特定 trait 最常被用作其他类的父级,从而使子类表现为父级 trait,那么请考虑将 trait 定义为一个类,以使这种逻辑关系更加清晰。
(我们说表现为,而不是a,因为前者是基于 Liskov 替换原则的更精确的继承定义——例如,参见 [Martin2003]。)

[Martin2003]:Robert C. Martin,敏捷软件开发:原则、模式和实践,Prentice-Hall,2003

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

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

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

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

最后一部分,关于对象的初始状态,通常有助于在给定概念的类(和类组合)和特征(和混合)之间做出决定。

于 2010-08-06T10:17:51.330 回答