2012-02-23 8 views
5

Czy istnieje sposób użycia systemu typu Scala, aby zwięźle określić kontekstowy podgraph kompletnego wykresu obiektu?Czy Scala może ograniczać wykres obiektów, aby widoczne były tylko te obiekty, które są istotne dla kontekstu?

DCI twierdzi, że często masz dość złożony wykres obiektów, ale w każdym przypadku użycia często chcesz pracować tylko z pod-grafem. Masz numer Foo, który ma Bar i Bat, ale gdy używasz obudowy 1, interesuje Cię tylko wersja Bar, a w przypadku 2 używana jest tylko wersja Bat.

Na przykład, powiedzmy, że masz tę strukturę, a Role1 use-case wymaga Foo->Bar->Baz->Bin i Role2 use-case wymaga Foo->Bat->Baz->Buz:

class Foo{ 
    val bar = new Bar() //Only relevant to Role 1 
    val bat = new Bat() //Only relevant to Role 2 
} 

class Bar { 
    val baz = new Baz() 
} 

class Bat { 
    val baz = new Baz() 
} 

//Relevant to both Role 1 and 2 (via Bar or Bat) 
class Baz { 
    val bin = new Bin() //Only relevant to Role 1 
    val buz = new Buz() //Only relevant to Role 2 
} 

class Bin{} 
class Buz{} 

Łatwo jest zobaczyć, jak można ograniczyć dostęp w się pojedyncza klasa przez korzystających cech:

trait FooInRole1 { def bar : Bar } //Define accessor in trait 
s/Foo/Foo extends FooInRole1/  //Change Foo's declaration to implement trait 
val f : FooInRole1 = new Foo  //LHS is i'face, RHS is implementation 
//f.bat <--Compile error    Irrelevant field is not available. \o/ 

Ale trzeba powtórzyć ten wzór dla każdego obiektu istotne warstwa użytkowa walizka. (Na przykład, trzeba mieć dostęp do binBazInRole1 i BazInRole2 dostęp do biz)

Moje pytanie brzmi, czy istnieje jakiś sposób, aby uniknąć pisania tych wszystkich cech jest łatwy do zdobycia, tak, przestrzeni nazw wyparcia. Na przykład, można sobie wyobrazić coś takiego kodu (które nie skompilować):

class Foo[T] { 
    T match { 
    case r1 : Role1 => def bar : Bar[T] 
    case r2 : Role2 => def bat : Bat[T] 
    case _ => //Nothing 
    } 
} 

val fInRole1 = new Foo[Role1] //Provides Foo->Bar->Baz->Bin 
val fInRole2 = new Foo[Role2] //Provides Foo->Bat->Baz->Buz 

Wygląda typu systemu Scala jest na tyle wyrazisty, aby zrobić coś takiego, ale nie mogę zrozumieć.

+0

Myślę, że coś takiego można osiągnąć dzięki klasom typów. Po prostu utwórz klasę typu widok na grafie obiektu i uzyskaj dostęp do zawartości i manipuluj jej zawartością tylko poprzez klasę typów. – ziggystar

Odpowiedz

0

Jeśli dobrze zrozumiałem pytanie (które nie jestem pewien) chcesz Foo dostarczenie jednego lub obu barbat zależności od parametru typu Foo.

Mój pierwszy strzał będzie:

class Bar 
class Bat 

trait BarExt { def bar = new Bar } 
trait BatExt { def bat = new Bat } 

trait Role 
case object Role1 extends Role 
case object Role2 extends Role 

trait RoleProvider[From <: Role, To] { 
    def apply(): To 
} 

object RoleProvider { 
    implicit val r1 = new RoleProvider[Role1.type, Foo[Role1.type] with BarExt] { 
    def apply() = new Foo[Role1.type] with BarExt 
    } 

    implicit val r2 = new RoleProvider[Role2.type, Foo[Role2.type] with BatExt] { 
    def apply() = new Foo[Role2.type] with BatExt 
    } 
} 

class Foo[T <: Role] 

object Foo { 
    def create[T <: Role, To](f: T)(implicit rp: RoleProvider[T,To]): To = rp() 
} 

tak że

scala> Foo.create(Role1) 
res1: Foo[Role1.type] with BarExt = [email protected] scala> Foo.create(Role1).bar 

scala> Foo.create(Role1).bar 
res2: Bar = [email protected] 

scala> Foo.create(Role1).bat 
<console>:12: error: value bat is not a member of Foo[Role1.type] with BarExt 
       Foo.create(Role1).bat 

i

scala> Foo.create(Role2).bat 
res3: Bat = [email protected] 

scala> Foo.create(Role2).bar 
<console>:12: error: value bar is not a member of Foo[Role2.type] with BatExt 
       Foo.create(Role2).bar 

Można by pozbyć BarExt i BatExt pociągając za odpowiednie deklaracje w definicjach z r1 i r2, jednak uważam, że „twardsze”, aby pracować z tym:

implicit val r1 = new RoleProvider[Role1.type, Foo[Role1.type] { val bar: Bar }] { 
    def apply() = new Foo[Role1.type] { val bar = new Bar } 
} 

implicit val r2 = new RoleProvider[Role2.type, Foo[Role2.type] { val bat: Bat }] { 
    def apply() = new Foo[Role2.type] { val bat = new Bat } 
} 

Na dolnej linii, ja nadal nie jestem przekonany, że to jest dokładnie to, co już z prośbą o, czy jest to?

+0

Nie sądzę, że jest to dokładnie to, czego szukam. Pomyśl o Bazie (lub, co gorsza, wykresie 20 klas z odsyłaczami). Aby upewnić się, że Baz.bin jest dostępny tylko w Role1, czy nie muszę zdefiniować cechy (xExt) na obiekt na rolę i napisać konwersję fn na krawędź? Być może jest to najlepsze, co można zrobić, ale miałem nadzieję, że * uda mi się znaleźć sposób na "kaskadę" zwięzłego parametru typu od Foo do Baz. Czy to ma sens? –

+0

Więc * wszystkie * metody powinny być zawarte na oryginalnym wykresie, ale kiedy "oglądasz" wykres z określonej roli, ujawnia się tylko podzbiór tych metod? Zakładam, że przez "subgraph" odnosisz się do tego samego zestawu węzłów, ale z różnymi rolami zależnymi od typu? – fotNelton

+0

Tak, właśnie tego oczekuję. –

0

W artykule this artima article na DCI autor przedstawia sposób uzyskania architektury DCI w Scala, która wygląda tak, jak chcesz.

Podstawową ideą jest zdefiniowanie metod, które są istotne dla twojego przypadku użycia w funkcji, ale zamiast Twojego podejścia używa on adnotacji typu self, aby upewnić się, że jest ona obiektem określonej klasy bazowej.

Aby było to łatwiej dostępne: masz klasę Data Data, która zawiera elementarne komponenty twoich obiektów danych. Gdy chcesz zrealizować pewną przypadków użycia, która lubi rozważyć Data obiekt w pewnej roli Role można przygotować rolę tak:

trait Role { self : Data => 
    def methodForOnlyThisUseCase = {...} 
} 

Dla realizacji przypadków użycia, następnie utworzyć object specyficzne dla tej roli poprzez:

val myUseCaseObject = new Data with Role 

takie jak ta, obiekt myUseCaseObject ogranicza się dokładnie jego Data składników i metod niezbędnych do jej roli w danym przypadku użytkowej.

Jeśli stanie się bardziej skomplikowany, być może trzeba będzie stworzyć coś w rodzaju cechy pseudo-roli, która definiuje metody wspólne dla wielu przypadków użycia. Samoprzylepna adnotacja roli użycia sprawy wskazywałaby na tę pseudo cechę, podczas gdy pseudo-cechy samopodpisywania adnotacji wskazuje na odpowiednią klasę danych.

+0

Tak, to jest ważny artykuł na temat DCI, a moje pytanie zaczyna się od tego typu projektu, ale problem polega na tym, że musisz jawnie napisać cechę dla każdej klasy w roli. W moim przykładzie musisz utworzyć cechę FooInRole1, FooInRole2 i BazInRole1, BazInRole2, a nawet będziesz musiał utworzyć BarInRole1, aby "przejść przez" typ Roli do Baz. To * może być * tak dobre, jak to tylko możliwe, ale wydaje się, że powinien istnieć sposób uniknięcia bałaganu w przestrzeni nazw. –

1

Niezbyt zwięzłe, a członkowie są tam, po prostu niemożliwe do użycia, ale może pójście w tym kierunku byłoby do przyjęcia?

class Foo[R] { 
    def bar(implicit ev: R <:< Role1) = new Bar[R] //Only relevant to Role 1 
    def bat(implicit ev: R <:< Role2) = new Bat[R] //Only relevant to Role 2 
} 
Powiązane problemy