使用Traits进行Scala客户端组合与实现抽象类的区别

33

我读到使用Scala时,一般建议使用Traits而不是抽象类来扩展基类。

以下设计模式和布局是否合适?这是Traits旨在替换抽象类的方式吗?

  • 客户端类(具有def function1)
  • Trait1类(覆盖了function1)
  • Trait2类(覆盖了function1)
  • SpecificClient1通过trait1扩展client
  • SpecificClient2通过trait2扩展client
2个回答

68
我不知道你声称在Scala中应该优先使用trait而不是抽象类的依据是什么,但有几个原因这么做:
  1. Trait会使Java兼容性变得复杂。如果你有一个带有伴生对象的Trait,在Java中调用伴生对象的方法需要使用奇怪的`MyType$.MODULE$.myMethod`语法。对于带有伴生对象的抽象类来说,情况并非如此,它们在JVM上实现为一个具有静态和实例方法的单个类。在Java中实现一个具有具体方法的Scala Trait甚至更加困难。
  2. 给Trait添加一个带有实现的方法会破坏二进制兼容性,而给类添加具体方法则不会。
  3. Trait会产生更多的字节码和一些额外开销,这与使用转发器方法有关。
  4. Trait更加强大,这是不好的——通常情况下,你应该使用最不强大的抽象化来完成任务。如果你不需要它们支持的多重继承(很多时候确实不需要),最好不要访问。

在我看来,最后一个原因是最重要的。至少其中一些问题可能会在未来的Scala版本中得到解决,但默认使用类将以(至少可以说)与良好设计一致的方式约束你的程序。如果你决定实际上确实需要Trait提供的能力,它们仍然会在那里,但这将是你做出的决策,而不是你悄悄地滑入其中。

因此,在没有其他信息的情况下,我建议使用一个抽象类(最好是一个密封的抽象类)和两个提供实现的具体类。


2
作为脚注,Scala对于线性化的处理确实是对多重继承“问题”的巧妙解决方案,而当你需要它时,它也能够工作得相当不错(除了一些字段等方面的问题)。但是,仅仅因为一个东西很巧妙就去使用它,并不明智。 - Travis Brown
1
谢谢,这回答了我的问题。我的来源是https://www.safaribooksonline.com/library/view/scala-cookbook/9781449340292/ch04s13.html。不过你的回答更有道理。 - kliew
2
使用Scala 2.12,关于 "A trait compiles directly to an interface with default methods. This improves binary compatibility and Java interoperability.",#1-3是否仍然适用?当然,我认为#4已经足够选择abstract class而不是trait的理由了。@TravisBrown - Kevin Meredith
Travis,你有关于特质更强大的任何指针吗? - laylaylom

2

另一方面,特征允许您以细粒度的方式构建和测试复杂对象的功能,并重用核心逻辑以提供不同的风味。例如,一个域对象可能部署到数据服务器,该服务器将数据持久化到数据库中,而Web服务器可能使用相同对象的只读版本,这些版本从数据服务器更新。

没有什么适合所有情况。使用正确的结构来完成手头的任务。有时,实现的现实会揭示在设计时未知的特定用例问题。使用不同的假设和结构重新实现可能会产生出人意料的结果。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接