2012-02-23 18 views
5

Có cách nào để sử dụng hệ thống kiểu Scala để chỉ định chính xác biểu đồ con có liên quan đến ngữ cảnh của một đồ thị đối tượng hoàn chỉnh không?Scala có thể hạn chế đồ thị đối tượng để chỉ những đối tượng có liên quan đến ngữ cảnh có thể nhìn thấy được không?

DCI cho rằng bạn thường có biểu đồ đối tượng khá phức tạp nhưng trong bất kỳ trường hợp sử dụng nào, bạn thường chỉ muốn làm việc với biểu đồ con. Bạn có FooBarBat, nhưng khi bạn đang dùng trong trường hợp 1, bạn chỉ quan tâm đến số Bar và khi trong trường hợp sử dụng 2, chỉ về Bat.

Ví dụ, giả sử rằng bạn có cấu trúc này, và việc sử dụng hợp cụ thể Role1 đòi hỏi Foo->Bar->Baz->Bin và Role2 sử dụng hợp cụ thể đòi hỏi 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{} 

Thật dễ dàng để xem làm thế nào bạn có thể hạn chế truy cập trong một lớp đơn bằng cách sử dụng những đặc điểm:

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/ 

Nhưng bạn phải lặp lại mô hình này cho tất cả các đối tượng liên quan đến use- trường hợp. (Ví dụ, bạn cần một BazInRole1 để truy cập binBazInRole2 để truy cập biz)

Câu hỏi của tôi là liệu có một số cách để tránh phải viết tất cả những đặc điểm dễ get-sai, namespace-crowding. Ví dụ, tôi có thể tưởng tượng một cái gì đó giống như mã này (mà không biên dịch):

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 

Nó có vẻ như kiểu hệ thống Scala là đủ biểu cảm để làm điều gì đó như thế này, nhưng tôi không thể hình dung nó ra.

+0

Tôi nghĩ rằng một cái gì đó như thế này có thể đạt được với kiểu lớp. Chỉ cần làm cho loại lớp xem trên đồ thị đối tượng và truy cập và thao tác nội dung của nó chỉ thông qua loại lớp. – ziggystar

Trả lời

0

Nếu tôi hiểu câu hỏi của bạn một cách chính xác (bạn không chắc chắn) bạn muốn Foo để cung cấp một trong số bar hoặc bat tùy thuộc vào thông số loại Foo.

bắn đầu tiên của tôi sẽ là:

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() 
} 

để

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 

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 

Người ta có thể thoát khỏi BarExtBatExt bằng cách kéo tờ khai tương ứng vào các định nghĩa của r1r2, tuy nhiên tôi thấy nó "khó" để làm việc với điều đó:

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

Tại dòng dưới cùng, tôi vẫn không bị thuyết phục rằng đây là chính xác những gì bạn đã được yêu cầu, hoặc là nó?

+0

Tôi không nghĩ đó là chính xác những gì tôi đang tìm kiếm. Hãy suy nghĩ về Baz (hoặc tệ hơn, một đồ thị gồm 20 lớp với các tham chiếu chéo). Để đảm bảo rằng Baz.bin chỉ có trong Role1, tôi không phải định nghĩa một đặc điểm (xExt) cho mỗi đối tượng trên mỗi vai trò và viết một fn chuyển đổi cho mỗi cạnh? Có lẽ đó là điều tốt nhất có thể được thực hiện, nhưng tôi đã * hy vọng * để tìm một cách để "thác" các loại tham số từ Foo Baz một cách súc tích. Điều đó có ý nghĩa? –

+0

Vì vậy, * tất cả * các phương thức được cho là nằm trong biểu đồ gốc nhưng khi "xem" biểu đồ từ một vai trò nhất định, chỉ có một tập hợp con của những phương thức đó được hiển thị? Tôi giả định rằng bởi "subgraph" bạn đang đề cập đến cùng một tập hợp các nút nhưng với các vai trò phụ thuộc vào loại khác nhau? – fotNelton

+0

Vâng, đó là chính xác những gì tôi đang hy vọng. –

0

Trong this artima article trên DCI, tác giả trình bày một cách để có kiến ​​trúc DCI trong Scala, trông giống như những gì bạn đang hướng đến.

Ý tưởng cơ bản là xác định các phương pháp có liên quan đến trường hợp sử dụng của bạn trong một đặc điểm, nhưng thay vì cách tiếp cận của bạn, nó sử dụng chú thích tự nhập để đảm bảo nó là đối tượng của một lớp cơ sở nhất định.

Vì vậy, để làm cho điều này dễ tiếp cận hơn một chút: bạn có một lớp dữ liệu Data, chứa các thành phần cơ bản của các đối tượng dữ liệu của bạn. Khi bạn muốn nhận ra một tình huống nhất định, mà thích để xem xét một đối tượng Data trong một vai trò nhất định Role bạn có thể chuẩn bị vai trò như thế này:

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

Đối với thực hiện các use-case, bạn sau đó tạo ra một đối tượng cụ thể để vai trò này qua:

val myUseCaseObject = new Data with Role 

như thế này, đối tượng myUseCaseObject được giới hạn chính xác Data thành phần của nó và các phương pháp cần thiết cho vai trò của nó trong việc sử dụng hợp cụ thể nhất định.

Nếu nó trở nên phức tạp hơn, bạn có thể phải tạo một cái gì đó giống như một đặc điểm vai trò giả, xác định các phương thức phổ biến cho nhiều trường hợp sử dụng. Chú giải kiểu tự của vai trò sử dụng sau đó sẽ trỏ trở lại đặc điểm giả này, trong khi các đặc tính giả tự nhập các điểm chú thích vào lớp dữ liệu tương ứng.

+0

Vâng, đó là một bài viết quan trọng về DCI, và câu hỏi của tôi bắt đầu từ kiểu thiết kế này, nhưng vấn đề là bạn phải viết một đặc điểm rõ ràng cho mỗi lớp trong vai trò. Trong ví dụ của tôi, bạn sẽ phải tạo ra một đặc điểm FooInRole1, FooInRole2 và BazInRole1, BazInRole2 và thậm chí bạn sẽ phải tạo ra một BarInRole1 để "chuyển qua" kiểu Role thành Baz. Nó * có thể * rằng điều này là tốt như bạn có thể làm, nhưng nó có vẻ như có phải là một cách để tránh rất nhiều không gian tên lộn xộn. –

1

Không thật ngắn gọn, và các thành viên ở đó, không thể sử dụng, nhưng có thể đi theo hướng này sẽ được chấp nhận?

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 
} 
Các vấn đề liên quan