我正在寻找一种干净的面向对象的方式来模拟以下(在Scala中):
一个人可以是:
这表明我们引入了Person超类和子类:
ManagerMathematicianTennisPlayerHobbyistProgrammerVolunteerPainter本Manager类有方法,如:getSalary(),workLongHours(),findNewJob()等TennisPlayer类有方法,如:getWorldRanking(),playGame(),strainAnkle(),等等等等.另外在课堂上也有Person类似的方法becomeSick().一个生病的经理失去了工作,网球运动员在赛季中停止了比赛.
此外,这些课程是不可改变的.也就是说,例如,strainAnkle()返回一个TennisPlayer具有紧张脚踝的新的,但所有其他属性保持不变.
现在的问题是:我们如何模拟一个人可以既是a Manager又是a TennisPlayer?
解决方案保留不变性和类型安全性非常重要.
我们可以实现以下类:
ManagerAndMathematicianManagerAndTennisPlayerAndPainterManagerAndPainter但这导致了类的组合爆炸.
我们也可以使用traits(with state),但是我们如何实现诸如的方法findNewJob(),这些方法需要返回一个具有相同特征的新人,但是具有新的Manager特征状态.同样,我们如何实现诸如becomeSick()?
问题:如何在Scala中以干净的OO方式实现这一点?请记住:不可变性和类型安全是必须的.
这对我而言并不像是继承的理想情况.也许你试图强迫事物进入继承模式,因为使用不可变值来处理组合似乎很尴尬.这是实现它的几种方法之一.
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)