【问题标题】:Using traits with a factory在工厂中使用特征
【发布时间】:2011-03-23 19:31:21
【问题描述】:

我目前正在发现 scala,我想知道是否可以将特征与工厂一起使用。

我试过这个:

抽象类 Foo { ... } 对象富 { def apply() = new Bar 私有类 Bar 扩展 Foo { ... } } 带有 MyTrait 的 Foo() // 不工作

我猜是因为with 前面必须有new

那么有什么办法可以做到吗?

谢谢

【问题讨论】:

标签: scala factory traits


【解决方案1】:

使用代理和隐式转换的解决方案

此示例扩展了 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)

打印:

我是精灵薛西斯。

【讨论】:

    【解决方案2】:

    隐式转换解决方案

    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")这样的代码
    【解决方案3】:

    假设你有:

    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。显然你不能。)

    【讨论】:

      【解决方案4】:

      不,为时已晚,当 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的思想中定义?
      • @Mr_Qqn:不,你不能这样做,即使通过清单也不行。如果您需要这种行为,请计划创建代理,将特定特征代理到您的工厂创建的对象,并为那些创建必要代理的特征创建(隐式)转换。
      • 谢谢,它与代理完美配合。但是创建隐式转换是什么意思? new FooProxy with MyTrait 正确地完成了这项工作。
      • @Ken 我想看看这种代理的代码示例。您能否通过一个示例向该线程添加新的响应?
      猜你喜欢
      • 2019-04-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-05
      • 1970-01-01
      相关资源
      最近更新 更多