Может ли Scala ограничить граф объектов, чтобы были видны только те объекты, которые имеют отношение к контексту? - PullRequest
5 голосов
/ 23 февраля 2012

Есть ли способ использовать систему типов 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 для доступа к bin и BazInRole2 для доступа к 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 достаточно выразительна, чтобы делать что-то подобное, но я не могу понять это.

Ответы [ 3 ]

1 голос
/ 12 марта 2012

Не очень лаконично, и члены есть, просто невозможно использовать, но, возможно, движение в этом направлении будет приемлемым?

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
}
0 голосов
/ 24 февраля 2012

В этой статье Artima о DCI, автор представляет способ получить архитектуру DCI в Scala, которая выглядит так, как вы нацелены.

Основная идея - определитьметоды, относящиеся к вашему сценарию использования в признаке, но вместо вашего подхода он использует аннотацию самостоятельного типа, чтобы гарантировать, что это объект определенного базового класса.

Итак, чтобы сделать это немногоболее доступный: у вас есть класс данных Data, который содержит элементарные компоненты ваших объектов данных.Когда вы хотите реализовать определенный вариант использования, который любит рассматривать Data объект в определенной роли Role, вы можете подготовить роль следующим образом:

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

Для выполнения использования -В этом случае вы создаете объект, специфичный для этой роли, с помощью:

val myUseCaseObject = new Data with Role

Таким образом, объект myUseCaseObject ограничен ровно его составляющими Data и методами, необходимыми для его роли в данном использовании.-case.

Если это усложняется, вам, возможно, придется создать что-то вроде псевдоролевой черты, которая определяет методы, общие для нескольких вариантов использования.Самостоятельная аннотация роли варианта использования будет затем указывать на эту псевдотракт, в то время как псевдотрактовая аннотация собственного типа указывает на соответствующий класс данных.

0 голосов
/ 23 февраля 2012

Если я правильно понял ваш вопрос (в чем я не уверен), вы хотите, чтобы Foo предоставил один из bar или bat в зависимости от параметра типа 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 = 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

Можно было бы избавиться от BarExt и BatExt с помощьювтягивая соответствующие декларации в определения r1 и r2, однако мне труднее с этим работать:

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

В итоге я все еще не уверен, что этоэто именно то, о чем вы просили, или это так?

...