Использование черт с фабрикой - PullRequest
6 голосов
/ 01 августа 2010

В настоящее время я обнаруживаю scala, и мне было интересно, смогу ли я использовать черты с фабрикой.

Я попробовал это:

abstract class Foo {
  ...
}
object Foo {
  def apply() = new Bar

  private class Bar extends Foo {
    ...
  }
}

Foo() with MyTrait // Not working

Я думаю, это потому, что with должно бытьпредшествует new.

Так есть ли способ сделать это?

Спасибо

Ответы [ 4 ]

4 голосов
/ 01 августа 2010

Нет, это слишком поздно, экземпляр уже создан, когда метод 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 {
  ...
}

В этом коде тип (профессия и раса) вашего аватара определяется на фабрике на основе перечислений RaceType и CharacterType.У вас есть одна фабрика для всех видов различных типов или комбинаций типов.

3 голосов
/ 03 августа 2010

Решение с неявным преобразованием

Кен предположил, что прокси может помочь нам в этом случае.Здесь мы пытаемся добавить черту к экземпляру после его создания.Это «исправление обезьян» может быть полезно, если кто-то еще написал класс (и метод 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)

Prints:

I am Xerxes, the Elf.

В этом примере добавленная черта Elf использует метод getName экземпляра, который он расширяет.

Буду признателен, если вы обнаружите какие-либо ошибки, я не очень хорошо понимаю (пока).

3 голосов
/ 01 августа 2010

Скажем, у вас есть:

class Foo
object Foo { def apply() = new Foo }
trait Baz

Тогда:

Foo() with Baz

будет аналогично:

val foo = new Foo
foo with Baz

, что подразумевает какое-то наследование на основе прототипа, которого нет у Scala. (Насколько я знаю.)

(Я предполагаю, что ошибка в мышлении заключается в интуитивном принятии знака = за «знак замещения». Т.е. поскольку Foo () означает Foo.apply () и который «равен» новому Foo, вы можете заменить Foo () с новым Foo. Что, очевидно, вы не можете.)

2 голосов
/ 03 августа 2010

Решение с прокси и неявным преобразованием

В этом примере расширяется решение olle, позволяющее пользователю указывать черту mixin при вызове метода apply() (например, 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)

Отпечатки:

Я Ксеркс, Эльф.

...