假设我们有一个基类(trait),它有两个方法(同名,不同的参数,而不是实现):
trait Base {
def compute(arg1:type1, arg2:type2): returnType
def compute(arg1:type1, arg2:type2, arg3:type3): returnType
}
我们有一些继承自Base的类。假设它们是A,B,C,A和B实现"计算"有两个args,而C实现"计算"有三个参数。
class A extends Base {
def compute(arg1:type1, arg2:type2): returnType = {
//detailed implementations
}
}
class B extends Base {
def compute(arg1:type1, arg2:type2): returnType = {
//detailed implementations
}
}
class C extends Base {
def compute(arg1:type1, arg2:type2, arg3:type3): returnType = {
//detailed implementations
}
}
现在,我有一组这些对象objs
,我想自动选择哪个版本的"计算"使用:
val name = objs.map{ x =>
val name = x.getClass.getSimpleName
name match {
case "C": x.compute(arg1, arg2, arg3)
case _: x.compute(arg1, arg2)
}
}
然而,它编译错误:
class A(B the same) needs to be abstract, since method compute in trait Base of type (three parameters) is not defined
我对此错误感到困惑。这是因为Base
中的所有方法都必须在其子类(A,B,C)中实现吗?
在没有编辑A类和B类的情况下是否有任何优雅的修复(因为C类是最近设计的,它的compute
必须再添加一个参数,所以我再设计了一个函数)?
答案 0 :(得分:1)
您需要在compute(arg1:type1, arg2:type2, arg3:type3)
和A
中定义B
并在compute(arg1:type1, arg2:type2)
中定义C
,否则您可以提供默认值,无操作在你的特质中实现
trait Base {
def compute(arg1:type1, arg2:type2) {}
def compute(arg1:type1, arg2:type2, arg3:type3) {}
}
我还建议在Base
修改的
使用案例类的完整(简化)工作示例:
trait Base {
def compute(arg1: Int, arg2: Int): Int = 0
def compute(arg1: Int, arg2: Int, arg3: Int): Int = 0
}
case class A() extends Base {
override def compute(arg1: Int, arg2: Int): Int = arg1 + arg2
}
case class B() extends Base {
override def compute(arg1: Int, arg2: Int): Int = arg1 - arg2
}
case class C() extends Base {
override def compute(arg1: Int, arg2: Int, arg3: Int): Int = arg1 + arg2 - arg3
}
case class D(arg1: Int, arg2: Int, arg3: Int, objs: Seq[Base]) {
val computed = objs map (_ match {
case x: C => x.compute(arg1, arg2, arg3)
case x: Base => x.compute(arg1, arg2)
})
}
答案 1 :(得分:1)
接口隔离原则(ISP)声明没有客户端应该 被迫依赖它不使用的方法。1 ISP分裂 接口非常大,更小,更具体 客户只需了解其中的方法 对他们感兴趣。这种缩小的界面也称为角色 接口
来源:Wikipedia
Traits在某些方面类似于名为“interfaces”的那些。
基本上,你必须分开Base
特质
Traits代表Scala中的模块,将它们保持在较小状态是一种很好的做法,这样我们就可以提高它们的组合能力并获得更大的抽象。
你最终会得到两个特征: (我只是将命名改为更清楚)
trait Computation {
def compute(arg1:Int, arg2:Int): Unit
}
trait SpecificComputation {
def compute(arg1:Int, arg2:Int, arg3:Int)
}
class A extends Computation {
def compute(arg1:Int, arg2:Int) = {
//detailed implementations
}
}
class B extends Computation {
def compute(arg1:Int, arg2:Int) = {
//detailed implementations
}
}
class C extends SpecificComputation {
def compute(arg1:Int, arg2:Int, arg3:Int) = {
//detailed implementations
}
}
如果您想要了解这两个class D
方法变体的compute
,请写下:
class D extends SpecificComputation with Computation {
def compute(arg1:Int, arg2:Int) = {
//detailed implementations
}
def compute(arg1:Int, arg2:Int, arg3:Int) = {
//detailed implementations
}
}
答案 2 :(得分:1)
同意@ Mik378。但是,如果您正在从2 args版本迁移到3 args版本,您可以:
trait Base {
// Mark this as deprecated
// No default implementation here because otherwise, A & B would need to
// be modified to add the 'override' keyword
@deprecated
def compute(arg1:type1, arg2:type2): returnType
// Provide a default implementation for old implementations e.g. A / B
def compute(arg1:type1, arg2:type2, arg3:type3): returnType =
compute(arg1, arg2)
}
// Convenience base class for new implementations e.g. C
abstract class NewBase extends Base {
override def compute(arg1: type1, arg2: type2): returnType =
throw new UnsupportedOperationException
}
class A extends Base {
def compute(arg1:type1, arg2:type2): returnType = {
//detailed implementations
}
}
class B extends Base {
def compute(arg1:type1, arg2:type2): returnType = {
//detailed implementations
}
}
// All new implementations extend 'NewBase' instead of 'Base'
class C extends NewBase {
override def compute(arg1:type1, arg2:type2, arg3:type3): returnType = {
//detailed implementations
}
}
现在,您只需使用3-args版本即可获得旧版和版本版。新的objs,
val name = objs.map(_.compute(arg1, arg2, arg3))