组合子类型(在Scala中)

Mag*_*sen 15 scala

我正在寻找一种干净的面向对象的方式来模拟以下(在Scala中):

一个人可以是:

  • 某公司的经理
  • 一位数学家
  • 世界级的网球运动员
  • 一个业余爱好者程序员
  • 当地学校的志愿者
  • 一个有创意的画家

这表明我们引入了Person超类和子类:

  • Manager
  • Mathematician
  • TennisPlayer
  • HobbyistProgrammer
  • Volunteer
  • Painter

Manager类有方法,如:getSalary(),workLongHours(),findNewJob()TennisPlayer类有方法,如:getWorldRanking(),playGame(),strainAnkle(),等等等等.另外在课堂上也有Person类似的方法becomeSick().一个生病的经理失去了工作,网球运动员在赛季中停止了比赛.

此外,这些课程是不可改变的.也就是说,例如,strainAnkle()返回一个TennisPlayer具有紧张脚踝的新的,但所有其他属性保持不变.

现在的问题是:我们如何模拟一个人可以既是a Manager又是a TennisPlayer

解决方案保留不变性和类型安全性非常重要.

我们可以实现以下类:

  • ManagerAndMathematician
  • ManagerAndTennisPlayerAndPainter
  • ManagerAndPainter

但这导致了类的组合爆炸.

我们也可以使用traits(with state),但是我们如何实现诸如的方法findNewJob(),这些方法需要返回一个具有相同特征的新人,但是具有新的Manager特征状态.同样,我们如何实现诸如becomeSick()

问题:如何在Scala中以干净的OO方式实现这一点?请记住:不可变性和类型安全是必须的.

Rex*_*err 6

这对我而言并不像是继承的理想情况.也许你试图强迫事物进入继承模式,因为使用不可变值来处理组合似乎很尴尬.这是实现它的几种方法之一.

object Example {
  abstract class Person(val name: String) {
    def occupation: Occupation
    implicit val self = this
    abstract class Occupation(implicit val practitioner: Person) {
       def title: String
       def advanceCareer: Person
    }
    class Programmer extends Occupation {
      def title = "Code Monkey"
      def advanceCareer = practitioner
    }
    class Student extends Occupation {
      def title = "Undecided"
      def advanceCareer = new Person(practitioner.name) {
        def occupation = new Programmer
      }
    }
  }

  def main(args: Array[String]) {
    val p = new Person("John Doe") { def occupation = new Student }
    val q = p.occupation.advanceCareer
    val r = q.occupation.advanceCareer
    println(p.name + " is a " + p.occupation.title)
    println(q.name + " is a " + q.occupation.title)
    println(r.name + " is a " + r.occupation.title)
    println("I am myself: " + (r eq r.occupation.practitioner))
  }
}
Run Code Online (Sandbox Code Playgroud)

我们来试试吧:

scala> Example.main(Array())
John Doe is a Undecided
John Doe is a Code Monkey
John Doe is a Code Monkey
I am myself: true
Run Code Online (Sandbox Code Playgroud)

所以这有点有用.

这里的诀窍是,每当一个职业(这是一个内部阶级)决定改变时,你就会创建你的人的匿名子类.它的工作是创造一个新角色完整的新人; 这是由implicit val self = this隐式构造函数帮助完成的,Occupation它有助于自动加载正确的人员实例.

您可能需要一个职业列表,因此可能需要辅助方法来重新生成职业列表.就像是

object Example {
  abstract class Person(val name: String) {
    def occupations: List[Occupation]
    implicit val self = this
    def withOccupations(others: List[Person#Occupation]) = new Person(self.name) {
      def occupations = others.collect {
        case p: Person#Programmer => new Programmer
        case s: Person#Pirate => new Pirate
      }
    }
    abstract class Occupation(implicit val practitioner: Person) {
       def title: String
       def addCareer: Person
       override def toString = title
    }
    class Programmer extends Occupation {
      def title = "Code Monkey"
      def addCareer: Person = withOccupations( this :: self.occupations )
    }
    class Pirate extends Occupation {
      def title = "Sea Monkey"
      def addCareer: Person = withOccupations( this :: self.occupations )
    }
  }

  def main(args: Array[String]) {
    val p = new Person("John Doe") { def occupations = Nil }
    val q = (new p.Programmer).addCareer
    val r = (new q.Pirate).addCareer
    println(p.name + " has jobs " + p.occupations)
    println(q.name + " has jobs " + q.occupations)
    println(r.name + " has jobs " + r.occupations)
    println("I am myself: " + (r eq r.occupations.head.practitioner))
  }
}
Run Code Online (Sandbox Code Playgroud)