5

有没有办法使用 Scala 的类型系统来简洁地指定完整对象图的上下文相关子图?

DCI 认为您通常拥有一个相当复杂的对象图,但在任何一个用例中,您通常只想使用子图。你有 a Foo,它有 aBar和 a 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/ 

但是您必须为与用例相关的每个对象重复此模式。(例如,您需要 a BazInRole1to accessbin和 a BazInRole2to access 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 的类型系统足以表达这样的事情,但我无法弄清楚。

4

3 回答 3

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
}
于 2012-03-12T00:29:17.993 回答
0

这篇关于 DCI 的 artima 文章中,作者介绍了一种在 Scala 中获得 DCI 架构的方法,这看起来就像您的目标一样。

基本思想是在特征中定义与您的用例相关的方法,但不是您的方法,而是使用自类型注释来确保它是某个基类的对象。

因此,为了让这更容易理解:您有一个 Data-class Data,其中包含数据对象的基本组件。当您想实现某个用例时,它喜欢考虑Data某个角色中的对象,Role您可以像这样准备角色:

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

为了执行用例,您可以通过以下方式创建一个特定于该角色的对象:

val myUseCaseObject = new Data with Role

像这样,对象myUseCaseObject仅限于它的Data组成部分以及它在给定用例中的角色所需的方法。

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

于 2012-02-24T15:38:53.300 回答
0

如果我正确理解了您的问题(我不确定),您希望Foo根据.barbatFoo

我的第一个镜头是:

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 = RoleProvider$$anon$3$$anon$1@187b2d93    scala> Foo.create(Role1).bar

scala> Foo.create(Role1).bar
res2: Bar = Bar@7ea4b9da

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 = Bat@56352b57

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

可以通过将相应的声明拉入 and 的定义来摆脱 and ,BarExt但是我发现使用它“更难”:BatExtr1r2

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

归根结底,我仍然不相信这正是您所要求的,或者是吗?

于 2012-02-23T15:43:29.697 回答