2013-06-23 10 views
7

Mam problemy z nauczeniem się pozostawania niezmiennymi głównymi osobami w Scali, z których jedną jest koncepcja klonowania (lub raczej wywodzenia) z przedmiotowego obiektu.Niezmienne klasy i cechy?

Jednym z tych problemów jest koncepcja mieszana w cechach - przykład

trait helper //modifies some kind behavior.. assume we want to to continue down the line 

class A (val x:int) { 

def add(y:int) = new A(x + y) 
} 

Example extends App { 

val a =new A(5) with helper 
val b = a.add(10) // mixed trait disappears 

} 

Teraz jest to po prostu bardzo prosta wersja bardziej złożony problem miałem już zbudowany dzisiaj wokół różnych klas, metod fabryki do ukrycia klasa A itd. Jeśli mamy do czynienia tylko z jedną cechą, wiem, że mógłbym po prostu przetestować i przesłać ją w razie potrzeby. Ale co do cholery robię, jeśli w górę z 3 lub więcej cech może istnieć? Musiałbym przetestować wszystkie kombinacje, które są nierealne.

W jaki sposób tworzysz (klonujesz) istniejący obiekt o różnych cechach i/lub modyfikujesz pewien jego aspekt, jednocześnie zachowując zasady projektowania funkcjonalnego?

Dziękujemy, - Tim

+0

Okazuje się, że wcale nie jest to proste! Jest to jeden z głównych powodów, dla których staram się unikać schematu tortowego - ponowne tworzenie tego samego ciasta jest naprawdę trudne, szczególnie jeśli budujesz ciasto w locie. Generalnie łatwiej jest, jeśli rzeczą, która modyfikuje zachowanie, są dane (np. 'Val' w klasie, prawdopodobnie w konstruktorze). –

+0

Zgadzam się i niestety, dlaczego wciąż unikam wzoru dekoracyjnego cechy ... wygląda świetnie w książce, ale książki akademickie nigdy nie opisują jak klonować obiekt później, jak to tutaj opisałem. – LaloInDublin

+0

Po prostu musisz stworzyć konstruktora dla obiektu, który dodaje kolejne trzy lub cztery linie tablicy podstawowej (kiedy to robię) za każdym razem, gdy chcesz mieszać w cechę. Czasami wciąż warto. Często tak nie jest. –

Odpowiedz

3

Kolekcje wykorzystuje budowniczych niejawne, które wiedzą jak produkować typ docelowy z czego zaczynając. Te dwa typy nie zawsze są tym samym.

Istnieje wiele związanych posty o typesafe budowniczych, które kontrolują to, co może być produkowany, np http://nullary.blogspot.com/2011/10/builder-pattern-revisited-in-scala.html

Powiązane pytanie, co jeśli masz zbiór mieszanych typów: Polymorphic updates in an immutable class hierarchy

na wartości oś, wartości śledzenia zamiast typów kodowania What is the Scala equivalent to a Java builder pattern?

Zaktualizowano: coś podobnego właśnie pojawiło się podczas gry. Używanie RE w wzorach jest opisane w ML i here.

package object interpat { 
    implicit class MySContext (val sc : StringContext) { 
    object mys { 
     def apply (args : Any*) : String = sc.s (args : _*) 
     def unapplySeq (s : String) : Option[Seq[String]] = { 
     val regexp = sc.parts.mkString ("(.+)").r 
     regexp.unapplySeq (s) 
     } 
    } 
    } 
    implicit class SBContext (val sc : StringContext) { 
    def sb(args: Any*): SB = new SB(sc.s (args : _*)) 
    } 
    implicit class toHasPattern(sb: SB) { 
    def /(pp: String) = new SB(sb.s) with HasPattern { 
     val p = pp 
    } 
    } 
    implicit class toHasRepl(hasp: SB with HasPattern) { 
    def /(rr: String) = new SB(hasp.s) with HasPattern with HasRepl with Updateable { 
     val p = hasp.p 
     val r = rr 
    } 
    } 
    // disallow it 
    implicit class noway(hasr: SB with HasPattern with HasRepl) { 
    def /(rr: String) = ??? 
    } 
    implicit class noway2(hasr: SB with HasPattern with HasRepl) { 
    def /(rr: String) = ??? 
    } 
} 

Z wykorzystaniem i alternatywą sterowania implikami za pomocą parametrów typu.

package interpat { 
    import scala.util.Try 
    object I { def unapply(x: String): Option[Int] = Try(x.toInt).toOption } 

    trait XX { 
    type HasIt 
    type Yes <: HasIt 
    type No <: HasIt 
    } 
    object XX extends XX { 
    implicit class XContext (val sc : StringContext) { 
     def x(args: Any*) = new X[No, No] { 
     val s = sc.s(args : _*) 
     } 
    } 
    implicit class xPat(x: X[No, No]) { 
     def /(pp: String) = new X[Yes, No] with HasPattern { 
     val s = x.s 
     val p = pp 
     } 
    } 
    implicit class xRep(x: X[Yes, No] with HasPattern) { 
     def /(rr: String) = new X[Yes, Yes] with HasPattern with HasRepl { 
     val s = x.s 
     val p = x.p 
     val r = rr 
     override def toString = s replaceAll (p, r) 
     } 
    } 
    implicit class xable(xx: X[Yes, Yes]) { 
     def x = xx.toString 
    } 
    } 
    import XX._ 

    trait X[HasP <: HasIt, HasR <: HasIt] { 
    def s: String 
    } 

    trait HasPattern { 
    def p: String 
    } 

    trait HasRepl { 
    def r: String 
    } 

    trait Updateable { this: HasPattern with HasRepl => 
    def update(p: String, r: String) 
    override def toString = { 
     update(p, r) 
     super.toString 
    } 
    } 

    class SB(val s: String) { 
    final val sb = new StringBuilder(s) 
    def update(p: String, r: String): Unit = 
     sb.replace(0, sb.length, sb.toString.replaceAll(p, r)) 
    override def toString = sb.toString 
    } 

    object Test extends App { 
    val msg = "The sky is blue" match { 
     case mys"The $thing is $colour" => mys"A $colour thing is $thing" 
     case _ => "no match" 
    } 
    println (msg) 
    val mys"The $thing is $colour" = "The sky is blue" 
    Console println s"$thing/$colour" 
    val mys"The ${I(number)} is blue" = "The 3 is blue" 
    Console println s"$number" 
    //sb"The sky is blue".update("blue","red") 
    // no way to get a value out! 
    sb"The sky is blue"("blue") = "red" 
    val ugh = sb"The sky is blue" 
    ugh("blue") = "red" 
    Console println ugh 
    val sx = sb"The sky is $colour"/"blue"/"red" 
    Console println sx 
    //sb"The sky is $colour"/"blue"/"red"/"yellow" 
    Console println sx 
    Console println x"The sky is $colour"/"blue"/"red" 
    Console println (x"The sky is $colour"/"blue"/"red").x 
    //Console println x"The sky is $colour"/"blue"/"red"/"yellow" 
    } 
}