能够在获得一个Map<String, *>
数据集对象,对其进行java.util.BigDecimal
“安全地”复杂数值计算的DSL提供。
DSL设计:
// get records
val records: Map<String, *> = querySql()
// do calculation
val result = calculate(records) {
(n("numerator") + n("numerator2")) / n("denominator")
}
还是存在一些语法噪音,如n(key: String)
,表示由一个key字符串,获得一个安全的BigDecimal
对象。
对比直接使用Scala插值器num"key1"
等的方式,我觉得还是差了一点。
# SafeBigDecimal
为了避免直接重载BigDecimal
的操作符,定义了一个新的Wrapper对象SafeBigDecimal
,用来包装BigDecimal
后对其进行拓展。
class SafeBigDecimal(
val value: BigDecimal? = null,
private val mathContext: MathContext = MathContext.DECIMAL64
) : Number() {
companion object {
val NULL_SAFE_BIG_DECIMAL = SafeBigDecimal()
}
// --- kotlin ext
override fun toDouble(): Double = value?.toDouble() ?: Double.MIN_VALUE
override fun toFloat(): Float = value?.toFloat() ?: Float.MIN_VALUE
override fun toLong(): Long = value?.toLong() ?: Long.MIN_VALUE
override fun toInt(): Int = value?.toInt() ?: Int.MIN_VALUE
override fun toChar(): Char = value?.toChar() ?: Char.MIN_VALUE
override fun toShort(): Short = value?.toShort() ?: Short.MIN_VALUE
override fun toByte(): Byte = value?.toByte() ?: Byte.MIN_VALUE
// --- object
override fun equals(other: Any?): Boolean =
if (value == null && other == null) true
else value?.equals(other) ?: false
override fun hashCode(): Int = value.hashCode()
override fun toString(): String = value.toString()
}
然后是运算符重载,当+
、-
为null时候取0值,当*
、/
的取null值,以及当/ 0
的除零情况,包装为value == null
的SafeBigDecimal
对象用于允许继续计算。
BigDecimal
运算符重载operator fun plus(value: BigDecimal?): SafeBigDecimal = when { value == null -> this this.value == null -> value.toSafeBigDecimal() else -> SafeBigDecimal(this.value.add(value, mathContext), mathContext) } operator fun minus(value: BigDecimal?): SafeBigDecimal = when { value == null -> this this.value == null -> value.toSafeBigDecimal() else -> SafeBigDecimal(this.value.subtract(value, mathContext), mathContext) } operator fun div(value: BigDecimal?): SafeBigDecimal = if (this.value == null || value == null || value == ZERO) NULL_SAFE_BIG_DECIMAL else SafeBigDecimal(this.value.divide(value, mathContext), mathContext) operator fun times(value: BigDecimal?): SafeBigDecimal = if (this.value == null || value == null) NULL_SAFE_BIG_DECIMAL else SafeBigDecimal(this.value.multiply(value, mathContext), mathContext)
Self
运算符重载operator fun plus(value: SafeBigDecimal): SafeBigDecimal = this + value.value operator fun minus(value: SafeBigDecimal): SafeBigDecimal = this - value.value operator fun div(value: SafeBigDecimal): SafeBigDecimal = this / value.value operator fun times(value: SafeBigDecimal): SafeBigDecimal = this * value.value
# Any?
to SafeBigDecimal
然后是为了支持从Map<String, *>
对象取值的转换,定义了Any?.toSafeBigDecimal(): SafeBigDecimal
的拓展函数。
fun Any?.toSafeBigDecimal(): SafeBigDecimal = when {
this == null -> NULL_SAFE_BIG_DECIMAL
this is SafeBigDecimal -> this
this is BigDecimal -> SafeBigDecimal(this)
this is BigInteger -> SafeBigDecimal(this.toBigDecimal())
this is String -> this.toBigDecimal().toSafeBigDecimal()
this is Double -> BigDecimal.valueOf(this).toSafeBigDecimal()
this is Float -> BigDecimal.valueOf(this.toDouble()).toSafeBigDecimal()
this is Long -> BigDecimal.valueOf(this).toSafeBigDecimal()
this is Int -> BigDecimal.valueOf(this.toLong()).toSafeBigDecimal()
this is Char -> BigDecimal.valueOf(this.code.toLong()).toSafeBigDecimal()
this is Short -> BigDecimal.valueOf(this.toLong()).toSafeBigDecimal()
this is Byte -> BigDecimal.valueOf(this.toLong()).toSafeBigDecimal()
this is Byte -> BigDecimal.valueOf(this.toLong()).toSafeBigDecimal()
else -> NULL_SAFE_BIG_DECIMAL
}
这样就可以通过任何类型map[key]: Any?
安全地获得一个可运算的SafeBigDecimal
对象
# Calculate DSL
最后是最一开始的DSL设计的实现。
首先实现n
取值函数,用来对字符串key
调用map::get
和toSafeBigDecimal
取值。
fun Map<String, *>.n(key: String): SafeBigDecimal = this[key]?.toSafeBigDecimal() ?: NULL_SAFE_BIG_DECIMAL
然后是DSL的calculate
方法实现。暂时忽略MathContext
,先做一个简单版。
现在知道我们需要实现一个入参为map: Map<String, *>, fomula: Map<String, *>.() -> R
的方法,具体实现非常简单,仅仅对map
运行传入的第二个formula
lambda函数,返回formula
的运算结果。
inline fun <reified T> calculate(map: Map<String, *>, formula: Map<String, *>.() -> T): T =
map.run(formula)
# Jackson Serialization
为了保证序列化和反序列化,分别在Jackson编写自定义的StdSerializer
和StdDeserializer
。
# SafeBigDecimalJsonSerializer
class SafeBigDecimalJsonSerializer : StdSerializer<SafeBigDecimal>(SafeBigDecimal::class.java) {
override fun serialize(value: SafeBigDecimal, gen: JsonGenerator, serializers: SerializerProvider) =
gen.writeNumber(value.value)
}
# SafeBigDecimalJsonDeserializer
class SafeBigDecimalJsonDeserializer : StdDeserializer<SafeBigDecimal>(SafeBigDecimal::class.java) {
override fun deserialize(p: JsonParser, ctxt: DeserializationContext): SafeBigDecimal =
p.readValueAs(BigDecimal::class.java).toSafeBigDecimal()
}
# 测试
编写测试代码
fun main() {
val map = mapOf(
"key1" to "1",
"key2" to 0,
"key3" to 0
)
calculate(map) {
(n("key1") + n("key2")) / n("key3")
}.also { println("(key1 + key2) / key3 = $it") }
calculate(map) {
(n("key1") + n("key2")) / n("key1")
}.also { println("(key1 + key2) / key1 = $it") }
}
结果如下:
(key1 + key2) / key3 = null
(key1 + key2) / key1 = 1
Process finished with exit code 0