Я строю простой DSL для операций с длиной. Я хочу, чтобы операции над доменом были расширяемыми, поэтому я использую их как mixins
вместе с implicit
преобразованиями для компонентов моего домена.
1. Ниже мое приложение.
package com.shasank.funWithLengths
object LengthAdditionApp extends App{
val length1 = 11 inches
val length2 = 15 inches
val length3 = 2 feet
println(length1)
println(length2)
println(length3)
println(length1 + length2) // all ok
println(length1 + length3) // all ok
println(length3 - length1) // all ok
println(length1 + length2 + length2) // this breaks since object returned from first operation doesn't have adder
}
Ниже мой базовый класс. Мне бы хотелось, чтобы это было абстрактно, но так как я не мог найти способ создать экземпляр подкласса
Inches
, я пометил конструктор как защищенный, так что только подклассы могут расширять его, и ничто другое не может создать экземпляр.
package com.shasank.funWithLengths
class Length protected(val measure: Int, val unit: String) {
private def convertToInches(length: Length)= length.unit match {
case "feet" => length.measure * 12
case "inches" => length.measure
}
protected def operateOnMeasures(other: Length, op: (Int,Int) => Int): Length ={
val thisInches = convertToInches(this)
val otherInches = convertToInches(other)
val operatedMeasure = op(thisInches,otherInches)
new Length(operatedMeasure, "inches") // object created does not have adder & subtracter capabilities
}
override def toString = {
val measureInInches = convertToInches(this)
val (feetMeasure, inchesMeasure) = BigInt(measureInInches) /% 12
val feetMeasureString = s"$feetMeasure feet and"
val inchesMeasureString = s"$inchesMeasure inches"
s"$feetMeasureString $inchesMeasureString"
}
}
Ниже приведены компоненты моего домена.
package com.shasank
package object funWithLengths {
implicit class Inches(measure: Int) extends Length(measure, "inches") with Adder with Subtracter {
def inches = this
}
implicit class Feet(measure: Int) extends Length(measure, "feet") with Adder with Subtracter {
def feet = this
}
}
Ниже приведены операторы моего домена.
package com.shasank.funWithLengths
trait Adder extends Length {
def +(other: Length) = super.operateOnMeasures(other, _+_)
}
package com.shasank.funWithLengths
trait Subtracter extends Length {
def -(other: Length) = super.operateOnMeasures(other, _-_)
}
Вопрос: Есть ли способ создать экземпляр Inches
(чтобы я мог получить все вкусности этого) пока возвращаюсь с метода operateOnMeasures
в моем базовом классе Length
?