【问题标题】:Encoding complex hierarchy rules in Scala types在 Scala 类型中编码复杂的层次结构规则
【发布时间】:2015-12-10 19:39:52
【问题描述】:

假设我在树结构中有一个复杂的节点层次结构,其中某些类型的节点只能具有某些(可能很多,甚至可能包括它自己的类型)类型的子节点。

假设我们有一棵员工树,并且想要编码哪些类型的员工可以成为老板,哪些其他类型。

一种方法是定义我们的Employee 类型PresidentCTOManager,以及它们对应的“下属”类型PresidentSubordinateCTOSubordinateManagerSubordinate。然后,您可以将*Subordinate 扩展到他们可以成为老板的任何Employee。然后你可以这样做:

sealed trait Employee

sealed trait PresidentSubordinate extends Employee
sealed trait VicePresidentSubordinate extends Employee
sealed trait CTOSubordinate extends Employee
sealed trait ManagerSubordinate extends Employee
sealed trait DeveloperSubordinate extends Employee

case class President(subordinates: Seq[PresidentSubordinate])

case class VicePresident(subordinates: Seq[VicePresidentSubordinates])
extends PresidentSubordinate

case class CTO(subordinates: Seq[CTOSubordinate]) 
extends PresidentSubordinate 
with VicePresidentSubordinate

case class Manager(subordinates: Seq[ManagerSubordinate]) 
extends VicePresidentSubordinate

case class Developer(subordinates: Seq[DeveloperSubordinate]) 
extends CTOSubordinate 
with ManagerSubordinate 
with VicePresidentSubordinate
with DeveloperSubordinate // Devs can be bosses of other devs

// Note, not all employees have subordinates, no need for corresponding type
case class DeveloperIntern() 
extends ManagerSubordinate 
with DeveloperSubordinate 

这种方法对我的六种左右的树节点类型效果很好,但我不知道这是否是最好的方法,因为类型的数量增加到 10 或 50 种类型。也许有一个更简单的解决方案,可能适合使用here 所示的模式。类似的东西

class VicePresidentSubordinate[T <: Employee]
object VicePresidentSubordinate {
  implicit object CTOWitness extends VicePresidentSubordinate[CTO]
  implicit object ManagerWitness extends VicePresidentSubordinate[Manager]
  implicit object DeveloperWitness extends VicePresidentSubordinate[Developer]
}

但是我不确定生成的案例类会是什么样子,因为这显然无法编译:

case class VicePresident(subordinates: Seq[VicePresidentSubordinate]) extends Employee

感谢您的帮助!

【问题讨论】:

  • 我遇到过这种情况。它从类型系统的 POV 中运行良好,但是是的,您会获得许多人为的超级特征。也许这在 Dotty 中使用联合类型变得更简单......
  • 是的!我等不及要联合类型了 :)

标签: scala hierarchy hierarchical-data


【解决方案1】:

当您说“它有效,但我可以做其他事情吗?”时,我不完全确定您在寻找什么属性。

过去我做过一些类似的事情,在类型系统中不做所有这些事情可能会变得有用。例如,如果您的角色动态变化,您可能希望拥有一个 Employee 类(带有“title”属性和“subordinates”)属性,并在运行时验证标题和依赖关系,例如,基于配置文件。

如果您的结构不是动态的,那么您可以将其保存在代码中。我会使用抽象类型来减少特征并表示您想要的关系。例如:

  // An employee has a name
  trait Employee {
    val name: String
  }

  // A subordinate has a manager
  trait Subordinate[M <: Manager[M]] {
    val manager: M
    manager.subordinates += this
  }

  // A manager has subordinates
  trait Manager[M <: Manager[M]] {
    val subordinates = mutable.Set[Subordinate[M]]()
  }

  // A middle level can both have a manager and manage others.
  trait Middle[M <: Manager[M], S <: Manager[S]] extends Manager[M] with Subordinate[S]

  // President does not have a manager, it manages only.
  case class President(name: String) extends Manager[President]

  // Some traits that report to the president.
  case class VicePresident(name: String, manager: President) extends Middle[VicePresident, President]
  case class CTO(name: String, manager: President) extends Middle[CTO, President]

  // An intern can't manage
  case class Intern(name: String, manager: CTO) extends Subordinate[CTO]

  // A simple test
  def main(args: Array[String]): Unit = {
    val bob = President("Bob")
    val alice = VicePresident("Alice", bob)
    val lucas = CTO("Lucas", bob)
    val sam = Intern("Sam", lucas)

    println(alice.manager)        // President(Bob)
    println(bob.subordinates)     // Set(CTO(Lucas,President(Bob)), VicePresident(Alice,President(Bob)))
    sam.subordinates              // Compiler error
  }

注意保持下属和经理的指针是如何完全由基本特征完成的。您添加的任何新类的对象都会立即链接!

【讨论】:

  • 谢谢,然后让类成为多种类型的从属,只是扩展多个从属特征? case class Intern( /*... */) extends Subordinate[CTO] with Subordinate[VicePresident]
  • 顺便说一句,如果这回答了您,请尝试将您的问题标记为已回答以保持网站清洁。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-01
  • 2013-01-22
  • 2017-01-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多