2013-05-13 13 views
6

Attualmente sto utilizzando il modello di torta per implementare alcuni algoritmi di ottimizzazione. Colpisco spesso i problemi di collisione del nome. Per esempio:Evitare la collisione del nome con Cake Pattern

trait Add[T] { this: Foo[T] => 
    def constant: T 
    def plus(t1: T, t2: T): T 
    def add(t: T) = plus(t, constant) 
} 

trait Mul[T] { this: Bar[T] => 
    def constant: T 
    def times(t1: T, t2: T): T 
    def mul(t: T) = times(t, constant) 
} 

trait Operations[T] { this: Add[T] with Mul[T] => 
    def neg(t: T): T 
} 

Qui, constant è definito in entrambe le Add e Mul tratti, ma i loro valori potrebbero essere diversi. Potrei prefisso il nome con il nome del tratto ma lo trovo brutto e fragile (def mulConstant: T). C'è un modo migliore per farlo?

risposta

7

A mia conoscenza, il modello di torta tradizionale di solito comporta 1 strato di nidificazione dei tratti, per raggruppare le operazioni. Quindi, il livello esterno dichiara il "servizio" effettivo (qui: Aggiungi, Mul, Operazioni) senza definirlo.

trait AddComponent[T] { this: FooComponent[T] => 
    def addition: Add 

    trait Add { 
    def constant: T 
    def plus(t1: T, t2: T): T 
    def add(t: T) = plus(t, constant) 
    } 
} 

trait MulComponent[T] { this: BarComponent[T] => 
    def multiplication: Mul 

    trait Mul { 
    def constant: T 
    def times(t1: T, t2: T): T 
    def mul(t: T) = times(t, constant) 
    } 
} 

trait OperationsComponent[T] { this: Add[T] with Mul[T] => 
    def operations: Operations 

    trait Operations { 
    def neg(t: T): T 
    } 
} 

Poi, quando si mescolano i tratti "... Componente" insieme, le dipendenze sono cablati:

trait IntOperations extends Operation[Int] { 
    class IntAdd extends Add { ... } 
    class IntMul extends Mul { ... } 
} 

class MyFooBar extends FooComponent[Int] with BarComponent[Int] with IntOperations { 
    lazy val addition = new IntAdd 
    lazy val multiplication = new IntMul 
    lazy val foo = ... 
    lazy val bar = ... 
} 

Questo risolve il problema particolare namespacing ma si scontra nome (le definizioni di 'servizio') rimangono un problema del modello di torta tradizionale. C'è uno blog post di Daniel Spiewak che dimostra come può essere risolto in generale, ma la soluzione viene fornita con un proprio insieme di (enormi) compromessi (vedere this talk).

Spero che questo abbia aiutato un po '.

P.S. invece di digitare i parametri potrebbe essere meglio usare i tipi astratti qui

+0

Grazie per la risposta, risolve il mio problema. Per quanto riguarda il tuo PS, avrò anche conflitti di tipo nome se definisco il tipo astratto "T" in ogni tratto (dal momento che dovrebbe essere definito a livello "componente"). Ho sbagliato ? – paradigmatic

+0

In questo caso particolare, "T" significa la stessa "cosa" in tutti i tratti, quindi non si scontreranno. Anche se ogni tratto contribuisce a una T con un tipo diverso legato, non si scontreranno, i loro limiti di tipo sono piuttosto raccolti insieme dai diversi tratti quando sono mescolati insieme. Finché i limiti non si scontrano, le cose vanno bene (per il compilatore). Questa è una cosa che Daniel Spiewak mostra in quel post del blog. Tuttavia, darei personalmente un tipo astratto sul livello "componente" un nome più descrittivo di "T", poiché 2 'T's non significa necessariamente la stessa cosa in 2 componenti diversi. –

+0

OK. Ma qui ho bisogno di essere sicuro che i tipi siano unificati ('tipo T = Int 'ovunque), quindi ho bisogno di esprimere la dipendenza che tutti i tratti misti hanno lo stesso' T'. Non vedo come ottenerlo se non con tipi generici. – paradigmatic

0

Questo può essere un po 'fuori moda da dire, e non è una risposta diretta alla tua domanda, ma non è più semplice fare semplicemente l'iniezione di dipendenza in costruttori? Ogni collaboratore ha il proprio spazio dei nomi, quindi non c'è mai uno scontro. E non ci sono problemi né con l'api pubblica della classe. Tuttavia, rimane difficile mescolare i pattern DI e cake.

Problemi correlati