2010-08-01 2 views
6

Attualmente sto scoprendo scala e mi chiedevo se potevo usare i tratti con una fabbrica.Utilizzo di tratti con una fabbrica

ho provato questo:

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

    private class Bar extends Foo { 
    ... 
    } 
} 

Foo() with MyTrait // Not working 

Credo che sia perché with deve essere preceduto da new.

Quindi c'è un modo per farlo?

Grazie

+0

possibile duplicato di [Come creare un'istanza di un tratto in un metodo generico in scala?] (Http://stackoverflow.com/questions/3274279/how-do-i-create-an-instance -di-un-trait-in-un-generico-metodo-in-scala) –

risposta

4

Non è troppo tardi, l'istanza è già stato creato quando la applicano() restituisce.

Quello che puoi fare è usare i tratti all'interno del metodo di fabbrica. Il seguente codice è da un piuttosto grande esempio di codice che sto scrivendo:

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 { 
    ... 
} 

In questo codice il tipo (professione e corsa) del tuo avatar è deciso in fabbrica sulla base del RaceType e enumerazioni CharacterType. Quello che hai è uno in fabbrica per tutti i tipi di diversi tipi o combinazioni di tipi.

+1

Nel tuo caso dobbiamo sapere in anticipo i tratti differenti che potremmo usare. Ma se non lo facciamo, è possibile trasferire i tratti nel metodo factory con una definizione nell'idea di 'def apply [T]() = new Foo with T'? –

+2

@Mr_Qqn: No, non puoi farlo, nemmeno passando un manifest. Se è necessario questo comportamento, pianificare la creazione di proxy che proxy i tratti specifici dell'oggetto creato dal produttore e creare conversioni (implicite) per quei tratti che creano i proxy necessari. –

+1

Grazie, funziona perfettamente con un proxy. Ma cosa intendi con la creazione di conversioni implicite? Un 'nuovo FooProxy con MyTrait' fa il lavoro correttamente. –

3

Diciamo che avete:

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

Poi:

Foo() with Baz 

sarebbe analogo a:

val foo = new Foo 
foo with Baz 

che implicherebbe una sorta di eredità prototipo-based, che Scala doesn avere. (Per quanto ne so.)

(suppongo che l'errore nel modo di pensare stia confondendo intuitivamente il segno = per un "segno di sostituzione". Vale a dire Foo() significa Foo.apply() e quale "uguale" new Foo, è possibile substitue Foo() con il nuovo Foo. il che, ovviamente, non si può.)

3

Soluzione con conversione implicita

Ken ha suggerito che un proxy potrebbe aiutarci in questo caso. Quello che stiamo cercando di fare qui è aggiungere un tratto all'istanza dopo che è stato creato. Questa "patch di scimmia" potrebbe essere utile se qualcun altro ha scritto la classe (e il metodo apply()) e non è possibile accedere all'origine. In questo caso si può fare è aggiungere un proxy/involucro in cima alla esempio conversione implicita (nessuna conversione manuale necessario):


Utilizzando l'esempio Foo potremmo fare questo in questo modo:

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!")) 

Uscite:

I am really useful, not! 

La ragione per cui non mi piace questo esempio è:

Baz non utilizza Foo in alcun modo.È difficile vedere il motivo per cui vorremmo collegare lo usefulMethod() a Foo.


Così ho fatto una nuova esempio in cui il tratto che "patch scimmia" in l'istanza in realtà utilizza l'istanza:

// --------- 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) 

Stampe:

I am Xerxes, the Elf. 

In questo esempio il tratto Elf aggiunto usa il metodo getName dell'istanza che estende.

Sarebbe grato se vedessi degli errori, non sono bravo a impliciti (ancora).

+0

Ho esteso la tua soluzione per abilitare il codice come 'val xerxes = Avatar [Elf] (" Xerxes ")' –

2

Soluzione con proxy e conversione implicita

Questo esempio estende soluzione di olle per consentire all'utente di specificare il tratto mixin quando si chiama il metodo apply() (ad esempio 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) 

Stampe:

Sono Serse, l'Elfo.