在编写scala代码时,我很困惑地选择trait
或class
。
首先,我有一个with
几个特征的控制器:
class MyController extends Controller
with TransactionSupport
with JsonConverterSupport
with LoggerSupport
在这些特征中,我定义了一些可以直接在MyController
中使用的方法和字段。
但是我的朋友说:当你extends
或with
这个特性时,它应该be a
这个特性。
查看MyController
,它是Controller
,但它不是TransactionSupport
,而不是JsonConverterSupport
,而不是LoggerSupport
,所以不应该with
他们。
所以代码变成:
class MyController(tranSupport: TransactionSupport,
jsonConverter: JsonConverterSupport,
loggerSupport: LoggerSupport) extends Controller
但是我对这段代码感觉不太好,这看起来很奇怪。
我看到trait
在scala代码中大量使用,我什么时候应该使用它或使用类来注入?
答案 0 :(得分:6)
我会推荐你Interfaces should be Adjectives。虽然一些特征可能扮演一个阶级的角色(因此,是名词并尊重“是 - ”关系),但当用作混合时,它们往往会扮演界面的角色。
作为一个“形容词”,该特征将为他们所扩展的任何内容添加一个合格的属性。例如,它们可能是Comparable
或Serializable
。
找到一个适合的形容词可能有点难度 - 你会用LoggerSupport
这个形容词? - 所以不要觉得过分受到限制。请注意,特质是完全错误的,因为它必然是一种“是一种”的关系。
但我会尽量避免使用特征来代替“has-a”关系。
答案 1 :(得分:1)
我的意见是它没有to be
它。混合是一种与继承不同的概念。尽管在语法上它是相同的,但它并不意味着相同。混合的典型用例就像你写的那样记录。如果您的服务类混合Logging
特征it is
记录器,这并不意味着。它只是另一种如何将功能组合成工作对象的方式。
Odersky建议,如果您不确定并且可以,请使用trait
,因为它们更灵活。如果需要,您可以在将来将trait
更改为class
。
答案 2 :(得分:1)
有时当我觉得混合特性看起来不太好时,我会使用这样的模块模式:
trait JsonConverterModule {
protected def jsonConverter: JsonConverter
protected trait JsonConverter {
def convert(in: Json): Json
}
}
class MyController extends Controller with JsonConverterModule {
private doSmth = jsonConverter.convert(...)
}
在这种情况下,MyController看起来更像是一个Controller,所有与Json相关的东西都隐藏在MyController'client'中
答案 3 :(得分:0)
你的第一个特征是"蛋糕模式"你的第二个例子是"构造函数注入"。两者都是在Scala中进行依赖注入的完全有效的方法。蛋糕模式功能强大,你可以注入类型成员,不同的特性可以很容易地相互交谈(我们不必创建单独的对象并将它们传递给彼此的对象,通常需要setter注入而不是简单的构造函数注入但是,类型必须在编译时实现,并且必须为每个特征组合实现单独的类。构造函数注入允许您在运行时构建对象,并且可以针对大量组合进行更好的扩展。