2012-02-23 60 views
5

有没有办法使用Scala的类型系统来简洁地指定完整对象图的上下文相关子图?Scala可以约束一个对象图,以便只有那些与上下文相关的对象才可见?

DCI认为你经常有一个相当复杂的对象图,但在任何一种用例中,你通常只想使用一个子图。您有一个Foo,它有一个Bar和一个Bat,但是在使用情况1时,您只关心Bar,而在使用情况2时只关心Bat

举例来说,假设你有这样的结构,基于role1用例需要Foo->Bar->Baz->Bin和role2所用例需要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{} 

可以很容易地看到如何在一个限制访问单班使用性状:

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/ 

你不得不重复这种模式的每个对象相关的使用 - 案件。 (例如,你需要一个BazInRole1访问binBazInRole2访问biz

我的问题是,是否有一些方法来避免编写所有这些易于获得,错,命名空间拥挤特征。举例来说,我能想象这样的事情代码(不编译):

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 

好像Scala的类型系统是足够的表现做这样的事情,但我不能弄明白。

+0

我认为类似这样的事情可以通过类型来实现。只需将类型类视为对象图上的视图,并仅通过类型类来访问和操作其内容。 – ziggystar 2012-02-23 09:54:04

回答

0

如果我理解正确你的问题,你想Foo提供任何barbat取决于Foo类型参数之一(这我不知道的)。

我的第一个镜头是:

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 

人们可以通过拉动相应的声明到定义摆脱BarExtBatExtr1r2,但我觉得“难”与合作:

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

在底线,我仍然不相信,这正是你一直在问什么了,是这样吗?

+0

我不认为这正是我要找的。想想Baz(或者更糟糕的是,20个类的交叉引用图)。为了确保Baz.bin仅在Role1中可用,我不必为每个角色的每个对象定义一个特征(xExt)并为每个边写入一个转换fn?也许这是可以做到的最好的,但是我希望*能够找到一种方法将Foo的类型参数简洁地“级联”到Baz。那有意义吗? – 2012-02-23 19:31:11

+0

因此,*所有*方法应该包含在原始图中,但是当从某个角色“查看”图时,只有这些方法的子集被暴露?我假设“子图”指的是同一组节点,但具有不同的类型依赖角色? – fotNelton 2012-02-23 20:51:08

+0

是的,这正是我所希望的。 – 2012-02-23 21:12:40

0

在DCI的this artima article中,作者提出了一种在Scala中获得DCI体系结构的方法,该体系结构看起来像您所期望的。

其基本思想是定义与你的用例相关的方法,而不是你的方法,它使用自我类型注释来确保它是某个基类的对象。

所以为了让这个更容易一点:你有一个Data-class Data,它包含你的数据对象的基本组件。当你想知道某一特定用途的情况下,它喜欢考虑在一定的作用Data对象Role你可以像这样准备的角色:

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

对于用例的执行,然后创建一个通过对象具体到这个角色:

val myUseCaseObject = new Data with Role 

与此类似,对象myUseCaseObject仅限于正是其Data成分和其在给定的用例角色所需的方法。

如果它变得更加复杂,您可能需要创建类似于伪角色特征的东西,该特征定义了多个用例共同的方法。然后,用例角色的自我类型注释将指向这个伪特征,而伪特征自我类型注释指向相应的数据类。

+0

是的,这是一篇关于DCI的重要文章,我的问题从这种类型的设计开始,但问题在于您必须为每个角色中的角色明确写出一个特质。在我的例子中,你最终不得不创建一个特性FooInRole1,FooInRole2和BazInRole1,BazInRole2,甚至最终必须创建一个BarInRole1,以便将角色类型“传递”给Baz。它*可能是*,尽管你可以做到这一点,但似乎应该有一种方法来避免这么多的名称空间混乱。 – 2012-02-24 18:09:21

1

不是非常简洁,成员在那里,只是不可能使用,但也许朝这个方向是可以接受的?

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 
} 
相关问题