Scala中如何避免在Object类中重复编写类型边界[T <: HandPoints[T]]
简化Scala中重复的自类型边界写法
我正在编写卡牌计分相关的Scala代码,通过HandPoints特质实现不同的计分规则(比如常规积分直接转换为得分,双倍积分则翻倍后转换)。但所有卡牌检查函数都需要重复声明[T <: HandPoints[T]]这个类型边界,显得冗余。这些函数都定义在object中,没法给类添加类型参数,希望在保留静态方法的同时简化代码。
原代码实现
HandPoints相关定义
trait HandPoints[T] { def toHandScore: HandScore def zero: T def add(that: T): T } case class RegularPoint(points: Int) extends HandPoints[RegularPoint] { override def toHandScore: HandScore = HandScore(points) override def zero: RegularPoint = RegularPoint(0) override def add(that: RegularPoint): RegularPoint = RegularPoint(points + that.points) } case class DoublingPoints(points: Int) extends HandPoints[DoublingPoints] { override def toHandScore: HandScore = HandScore(points*2) override def zero: DoublingPoints = DoublingPoints(0) override def add(that: DoublingPoints): DoublingPoints = DoublingPoints(points + that.points) } case class HandScore(score: Int)
卡牌评估函数(存在重复类型边界问题)
trait Card { def getValue: Int def getSuit: String } def scored[T <: HandPoints[T]](score: T)(boolean: Boolean): T = { if (boolean) score else score.zero } def isAllEvens[T <: HandPoints[T]](score: T)(cards: List[Card]): T = { scored(score) { cards.forall(_.getValue % 2 == 0) } } def isAllReds[T <: HandPoints[T]](score: T)(cards: List[Card]): T = { scored(score) { cards.forall(c => List("HEARTS", "DIAMONDS").contains(c.getSuit)) } } def isAllNoDuplicates[T <: HandPoints[T]](score: T)(cards: List[Card]): T = { scored(score) { cards.distinct == cards } } val regularGameCriteria: List[List[Card] => RegularPoint] = List( isAllEvens(RegularPoint(1)), isAllReds(RegularPoint(3)), isAllNoDuplicates(RegularPoint(5)) ) val beginnerGameCriteria: List[List[Card] => RegularPoint] = List( isAllEvens(RegularPoint(1)), isAllReds(RegularPoint(1)), isAllNoDuplicates(RegularPoint(1)) ) val superGameCriteria: List[List[Card] => DoublingPoints] = List( isAllEvens(DoublingPoints(1)), isAllReds(DoublingPoints(3)), isAllNoDuplicates(DoublingPoints(5)) ) def countScore[T <: HandPoints[T]](scoreList: List[List[Card] => T])(melds: List[Card]): T = { scoreList.map(f => f(melds)).reduce((a, b) => a.add(b)) } def regularGameScore(cards: List[Card]): RegularPoint = { countScore(regularGameCriteria)(cards) } def beginnerGameScore(cards: List[Card]): RegularPoint = { countScore(beginnerGameCriteria)(cards) } def superGameScore(cards: List[Card]): DoublingPoints = { countScore(superGameCriteria)(cards) }
优化方案
方案1:隐式上下文边界消除重复约束
在定义函数的object中创建一个隐式证据特质,封装重复的类型约束逻辑:
// 定义隐式证据类,封装T <: HandPoints[T]的约束 trait SelfHandPoint[T] object SelfHandPoint { implicit def ev[T <: HandPoints[T]]: SelfHandPoint[T] = new SelfHandPoint[T] {} }
之后所有函数的类型参数改用上下文边界[T: SelfHandPoint],替代重复的[T <: HandPoints[T]]:
def scored[T: SelfHandPoint](score: T)(boolean: Boolean): T = { if (boolean) score else score.zero } def isAllEvens[T: SelfHandPoint](score: T)(cards: List[Card]): T = { scored(score) { cards.forall(_.getValue % 2 == 0) } } def isAllReds[T: SelfHandPoint](score: T)(cards: List[Card]): T = { scored(score) { cards.forall(c => List("HEARTS", "DIAMONDS").contains(c.getSuit)) } } def isAllNoDuplicates[T: SelfHandPoint](score: T)(cards: List[Card]): T = { scored(score) { cards.distinct == cards } } def countScore[T: SelfHandPoint](scoreList: List[List[Card] => T])(melds: List[Card]): T = { scoreList.map(f => f(melds)).reduce((a, b) => a.add(b)) }
这种方式完全消除了重复的类型边界声明,代码简洁性大幅提升。
方案2:重构HandPoints为自类型特质
修改HandPoints的定义,使用自类型替代泛型参数,子类无需显式指定泛型类型,函数的类型约束也会简化:
trait HandPoints { self: HandPoints => def toHandScore: HandScore def zero: this.type def add(that: this.type): this.type } case class RegularPoint(points: Int) extends HandPoints { override def toHandScore: HandScore = HandScore(points) override def zero: RegularPoint = RegularPoint(0) override def add(that: RegularPoint): RegularPoint = RegularPoint(points + that.points) } case class DoublingPoints(points: Int) extends HandPoints { override def toHandScore: HandScore = HandScore(points * 2) override def zero: DoublingPoints = DoublingPoints(0) override def add(that: DoublingPoints): DoublingPoints = DoublingPoints(points + that.points) }
此时函数的类型约束简化为[T <: HandPoints],仅需在scored方法中做一次类型转换:
def scored[T <: HandPoints](score: T)(boolean: Boolean): T = { if (boolean) score else score.zero.asInstanceOf[T] } def isAllEvens[T <: HandPoints](score: T)(cards: List[Card]): T = { scored(score) { cards.forall(_.getValue % 2 == 0) } } // 其余函数同理修改
该方案彻底移除了泛型参数的重复声明,唯一的小瑕疵是需要一次类型转换,适合追求极简代码结构的场景。
方案3:按类型分组封装函数
将计分函数封装到带类型参数的特质中,一次性声明类型约束,再为每种HandPoints类型创建对应的实现对象:
trait CardScorer[T <: HandPoints[T]] { def scored(score: T)(boolean: Boolean): T = { if (boolean) score else score.zero } def isAllEvens(score: T)(cards: List[Card]): T = { scored(score) { cards.forall(_.getValue % 2 == 0) } } def isAllReds(score: T)(cards: List[Card]): T = { scored(score) { cards.forall(c => List("HEARTS", "DIAMONDS").contains(c.getSuit)) } } def isAllNoDuplicates(score: T)(cards: List[Card]): T = { scored(score) { cards.distinct == cards } } def countScore(scoreList: List[List[Card] => T])(melds: List[Card]): T = { scoreList.map(f => f(melds)).reduce((a, b) => a.add(b)) } } // 为每种积分类型创建对应的计分器对象 object RegularCardScorer extends CardScorer[RegularPoint] object DoublingCardScorer extends CardScorer[DoublingPoints]
使用时直接调用对应对象的方法:
val regularGameCriteria: List[List[Card] => RegularPoint] = List( RegularCardScorer.isAllEvens(RegularPoint(1)), RegularCardScorer.isAllReds(RegularPoint(3)), RegularCardScorer.isAllNoDuplicates(RegularPoint(5)) ) def regularGameScore(cards: List[Card]): RegularPoint = { RegularCardScorer.countScore(regularGameCriteria)(cards) }
这种方案完全消除了重复的类型约束,同时按类型分组管理计分逻辑,适合规则复杂、需要扩展更多计分类型的场景。
内容的提问来源于stack exchange,提问作者eancu
相关产品推荐
相关产品推荐

