2012-12-02 14 views
7

Chcę utworzyć instancję obiektu, którego rodzaj zależy od danych środowiska wykonawczego:scala dynamicznie określana typ

trait Business 
case class Business1() extends Business 
case class Business2() extends Business 

object Business { 
    def fromData(data:Array[Byte]): Business = data(0) match { 
    case 1 => new Business1 
    case 2 => new Business2 
    case _ => throw new RuntimeException("data error") 
    } 
} 

Powyższy kod może wykonywać swoje zadania, ale ma wątpliwości, że jest on zamknięty. Ilekroć implementuję nową podklasę Business, będę musiał zmodyfikować kod Business.fromData, np.

case 3 => new Business3 

Jak mogę określić Business.fromData raz i można później dodać Business3, Business4 bez rejestracji do niego?

Edit

końcu zdałam sobie sprawę, że jest to idealny przypadek zastosowania Multimethod, czyli dysponowanie na podstawie funkcji jakiegoś argumentu. Więc bardziej ogólne pytanie powinno brzmieć "Jak wykonać multimethod w scala"? Uważam, że wzorce projektowe istnieją tylko z powodu niemożności posługiwania się językiem, dlatego jestem niechętny akceptacji odpowiedzi opartej na fabrykacji.

+1

Czy spalę w piekle za sugestię odbicia java? :) –

Odpowiedz

3

to nie rozwiązuje problemu, ale jeśli zrobisz Business jest „zamknięte” cecha następnie kompilator na połów każdy niepełny mecz aż Zaktualizowaliśmy fromData:

sealed trait Business 
case class Business1() extends Business 
case class Business2() extends Business 

biz match { 
    case Business1 => println("Business1") 
} 

... będzie w wyniku ...

warning: match is not exhaustive! 
missing combination   Business2 
3

Możesz również to zrobić. Chociaż nie jestem pewien, czy to jest lepsze niż przypadek dopasowania. Zależy od tego, co próbujesz zrobić.

class Business { 
    override def toString = "Business" 
} 

val factories: Map[Int,() => Business] = Map(
    1 -> (() => new Business { 
    override def toString = "Business1" 
    }), 
    2 -> (() => new Business { 
    override def toString = "Business2" 
    }), 
    3 -> (() => new Business { 
    override def toString = "Business3" 
    }) 
) 

object Business { 
    def fromData(data: Array[Byte]): Business = factories(data(0))() 
} 

val b = Business.fromData(Array(1,1,2)) 
println(b) 
+0

'fabryki' jest nadal zamknięte – xiefei

1

Klasyczną odpowiedzią jest użycie fabryki z rejestracją, a.k.a. fabryki abstrakcyjnej.

Tak więc biorąc pod uwagę powyższą hierarchię, utworzysz mapę "fabryk", podobnie jak ta przedstawiona tutaj w innej odpowiedzi, ale możesz także utworzyć równoległą hierarchię twórców obiektów i zarejestrować je podczas uruchamiania, np. tak:

trait BusinessCreator { 
    def createBusiness() : Business 
} 
object BusinessCreator1() extends BusinessCreator { 
    override def createBusiness() : Business = new Business1() 

    factories += "1" -> this 
} 
//etc. 

Innym bardziej „Scalaish” sposób to zrobić byłoby pominąć hierarchię równoległego i po prostu przypisać funkcję twórca w fabrykach obiekt z obiektu towarzyszem każdej klasy, ale idea jest taka sama.

+0

To nie zadziała, ponieważ instancja "BusinessCreator" jest leniwie tworzona - nie będzie instancji, dopóki nie zostanie odwołana. Musisz więc odwołać się do wszystkich swoich twórców, co jest ponownie procesem zamkniętym. –

0

Co z tym jednym?

val factories = collection.mutable.Map(
    1 -> new Function0[Business] { 
    private[this] lazy val single = new Business { 
     override def toString = "Business1" 
    } 
    override def apply() = single 
    } 
    ,2 -> new Function0[Business] { 
    private[this] lazy val single = new Business { 
     override def toString = "Business2" 
    } 
    override def apply() = single 
    } 
    ,3 -> new Function0[Business] { 
    private[this] lazy val single = new Business { 
     override def toString = "Business3" 
    } 
    override def apply() = single 
    } 
)