【发布时间】:2011-03-23 19:31:21
【问题描述】:
我目前正在发现 scala,我想知道是否可以将特征与工厂一起使用。
我试过这个:
抽象类 Foo { ... } 对象富 { def apply() = new Bar 私有类 Bar 扩展 Foo { ... } } 带有 MyTrait 的 Foo() // 不工作我猜是因为with 前面必须有new。
那么有什么办法可以做到吗?
谢谢
【问题讨论】:
我目前正在发现 scala,我想知道是否可以将特征与工厂一起使用。
我试过这个:
抽象类 Foo { ... } 对象富 { def apply() = new Bar 私有类 Bar 扩展 Foo { ... } } 带有 MyTrait 的 Foo() // 不工作我猜是因为with 前面必须有new。
那么有什么办法可以做到吗?
谢谢
【问题讨论】:
使用代理和隐式转换的解决方案
此示例扩展了 olle 的解决方案,允许用户在调用 apply() 方法时指定 mixin trait(例如 val xerxes = Avatar[Elf]("Xerxes"))。
// ----- Predefined types -----
trait Race {
def whoAmI: String
}
class Avatar[R <: Race](val name: String)
object Avatar {
def apply[R <: Race](name: String) = new Avatar[R](name)
}
// ----- Generic proxy -----
class AvatarProxy[R <: Race](val avatar: Avatar[R])
implicit def proxy2Avatar[R <: Race](proxy: AvatarProxy[R]): Avatar[R] =
proxy.avatar
// ----- A new trait -----
trait Elf extends Race {
self: AvatarProxy[Elf] =>
def whoAmI = "I am " + self.name + ", the Elf."
}
implicit def avatar2Elf(avatar: Avatar[Elf]): AvatarProxy[Elf] with Elf =
new AvatarProxy[Elf](avatar) with Elf
// --- Test code -----
val xerxes = Avatar[Elf]("Xerxes")
println(xerxes.whoAmI)
打印:
我是精灵薛西斯。
【讨论】:
隐式转换解决方案
Ken 建议在这种情况下代理可以帮助我们。我们在这里尝试做的是在创建实例后为其添加特征。如果其他人编写了该类(以及apply() 方法)并且您无法访问源代码,则此“猴子补丁”可能很有用。在这种情况下,您可以通过隐式转换在实例顶部添加代理/包装器(无需手动转换):
使用Foo 示例,我们可以这样做:
class Foo
object Foo { def apply() = new Foo }
trait Baz { def usefulMethod(s: String) = "I am really useful, "+ s }
// ---- Proxy/Wrapper ----
class FooWithBazProxy extends Foo with Baz
// --- Implicit conversion ---
implicit def foo2FooWithBazProxy(foo: Foo): FooWithBazProxy = new FooWithBazProxy
// --- Dummy testcode ---
val foo = Foo()
println(foo.usefulMethod("not!"))
输出:
I am really useful, not!
我不喜欢这个例子的原因是:
Baz 不会以任何方式使用Foo。很难看出我们为什么要将usefulMethod() 附加到Foo。
所以我做了一个新的例子,我们“猴子补丁”到实例中的特征实际上使用了实例:
// --------- Predefined types -----------
trait Race {
def getName: String
}
class Avatar(val name: String) extends Race{
override def getName = name
}
object Avatar{
def apply() = new Avatar("Xerxes")
}
// ---------- Your new trait -----------
trait Elf extends Race {
def whoAmI = "I am "+ getName + ", the Elf. "
}
// ---- Proxy/Wrapper ----
class AvatarElfProxy(override val name: String) extends Avatar(name) with Elf
// ---- Implicit conversion ----
implicit def avatar2AvatarElfProxy(Avatar: Avatar): AvatarElfProxy = new AvatarElfProxy(Avatar.name)
// --- Dummy testcode ---
val xerxes= Avatar()
println(xerxes.whoAmI)
打印:
I am Xerxes, the Elf.
在此示例中,添加的 Elf 特征使用其扩展实例的 getName 方法。
如果您看到任何错误,将不胜感激,我不擅长隐式(目前)。
【讨论】:
val xerxes = Avatar[Elf]("Xerxes")这样的代码
假设你有:
class Foo
object Foo { def apply() = new Foo }
trait Baz
然后:
Foo() with Baz
类似于:
val foo = new Foo
foo with Baz
这意味着某种基于原型的继承,而 Scala 没有。 (据我所知。)
(我猜想的错误是直觉上将 = 符号误认为是“替换符号”。即由于 Foo() 表示 Foo.apply() 并且“等于” new Foo,因此您可以替换 Foo()用新的 Foo。显然你不能。)
【讨论】:
不,为时已晚,当 apply() 方法返回时,实例已经创建。
您可以做的是使用工厂方法中的特征。下面的代码来自我正在编写的一个相当大的代码示例:
object Avatar {
// Avatar factory method
def apply(name: String, race: RaceType.Value, character: CharacterType.Value
): Avatar = {
race match {
case RaceType.Dwarf => {
character match {
case CharacterType.Thief => new Avatar(name) with Dwarf with Thief
case CharacterType.Warrior => new Avatar(name) with Dwarf with Warrior
case CharacterType.Wizard => new Avatar(name) with Dwarf with Wizard
}
}
case RaceType.Elf => {
character match {
case CharacterType.Thief => new Avatar(name) with Elf with Thief
case CharacterType.Warrior => new Avatar(name) with Elf with Warrior
case CharacterType.Wizard => new Avatar(name) with Elf with Wizard
}
}
}
}
}
class Avatar(val name: String) extends Character {
...
}
在此代码中,您的 Avatar 的类型(职业和种族)是在工厂中根据 RaceType 和 CharacterType 枚举决定的。您拥有的是 one 工厂,适用于各种不同类型或类型组合。
【讨论】:
def apply[T]() = new Foo with T的思想中定义?
new FooProxy with MyTrait 正确地完成了这项工作。