在 Scala 中对 mixin 成员的封装
encapsulation for mixin's members in Scala
Scala 中的特征可以用作混入和接口。它会导致一些不一致——如果我想关闭 trait 中的某些方法,我就是做不到:
object Library {
protected trait A { def a: Int = 5 }
trait B extends A { private override def a: Int = super.a }
//I want to close `a` memeber for all traits extending B; it's still possible to open it in some another trait `C extends A`, or even `Z extends B with C`
}
// Exiting paste mode, now interpreting.
<console>:10: error: overriding method a in trait A of type => Int;
method a has weaker access privileges; it should not be private
trait B extends A { private override def a: Int = super.a }
^
从 LSP 的角度来看,这样的错误完全没问题,因为我(或编译器)可能想将其转换为超类型 A
。但是,如果我只是将它用作混音,我实际上永远不需要这样做,例如在某些 Cake-pattern 变体中。我会做类似的事情:
import Library._
object O extends B with K with L with App
就是这样。我什至不能在这里访问 trait A
。我知道,类型推断可能会上升到超类型,但它只是一个 "line of types",因此编译器可以跳过 A
并继续(当然这是非常理论化的)。另一个例子 - here 我不得不为方法提供默认实现,我并不真正需要它。
我目前使用的解决方案是 OOP 组合,但它不太灵活(因为线性化在这里不起作用)并且与混合概念不太兼容。我见过的一些项目,他们实际上做混合并且有 "over9000" 冗余和可见成员。几年前有一个想法 "mark" 通过指定 with
关键字而不是 extends
这样的 mixins 组合,但现在甚至找不到那个线程。
那么,ad-hoc成员封装有什么更好的做法吗?
这不是通用的解决方案,但可以在一个模块内关闭来自外部世界的方法:
object Library {
protected trait A {
private[Library] def a: Int = 5
private[Library] def b: Int = 7
}
trait B extends A {
def b = super.b
}
}
import Library._
object C extends B
scala> C.a
<console>:179: error: method a in trait B cannot be accessed in object C
C.a
^
scala> C.b
res131: Int = 7
所以我们只是在这里反转封装。如果 A
也应该开放扩展:
object Library {
protected trait _A {
private[Library] def a: Int = 5
private[Library] def b: Int = 7
}
trait B extends A { /*...*/ }
trait A extends _A {
override def a = super.a
override def b = super.b
}
}
所以,也许太样板了,但至少它有效。
P.S。这个想法的部分灵感来自另一个无效的已删除答案:)
Scala 中的特征可以用作混入和接口。它会导致一些不一致——如果我想关闭 trait 中的某些方法,我就是做不到:
object Library {
protected trait A { def a: Int = 5 }
trait B extends A { private override def a: Int = super.a }
//I want to close `a` memeber for all traits extending B; it's still possible to open it in some another trait `C extends A`, or even `Z extends B with C`
}
// Exiting paste mode, now interpreting.
<console>:10: error: overriding method a in trait A of type => Int;
method a has weaker access privileges; it should not be private
trait B extends A { private override def a: Int = super.a }
^
从 LSP 的角度来看,这样的错误完全没问题,因为我(或编译器)可能想将其转换为超类型 A
。但是,如果我只是将它用作混音,我实际上永远不需要这样做,例如在某些 Cake-pattern 变体中。我会做类似的事情:
import Library._
object O extends B with K with L with App
就是这样。我什至不能在这里访问 trait A
。我知道,类型推断可能会上升到超类型,但它只是一个 "line of types",因此编译器可以跳过 A
并继续(当然这是非常理论化的)。另一个例子 - here 我不得不为方法提供默认实现,我并不真正需要它。
我目前使用的解决方案是 OOP 组合,但它不太灵活(因为线性化在这里不起作用)并且与混合概念不太兼容。我见过的一些项目,他们实际上做混合并且有 "over9000" 冗余和可见成员。几年前有一个想法 "mark" 通过指定 with
关键字而不是 extends
这样的 mixins 组合,但现在甚至找不到那个线程。
那么,ad-hoc成员封装有什么更好的做法吗?
这不是通用的解决方案,但可以在一个模块内关闭来自外部世界的方法:
object Library {
protected trait A {
private[Library] def a: Int = 5
private[Library] def b: Int = 7
}
trait B extends A {
def b = super.b
}
}
import Library._
object C extends B
scala> C.a
<console>:179: error: method a in trait B cannot be accessed in object C
C.a
^
scala> C.b
res131: Int = 7
所以我们只是在这里反转封装。如果 A
也应该开放扩展:
object Library {
protected trait _A {
private[Library] def a: Int = 5
private[Library] def b: Int = 7
}
trait B extends A { /*...*/ }
trait A extends _A {
override def a = super.a
override def b = super.b
}
}
所以,也许太样板了,但至少它有效。
P.S。这个想法的部分灵感来自另一个无效的已删除答案:)