我希望在从数字或字符串创建货币价值时,比如使用 Money.of(200.20234, "EUR")
这样的构造方式,如果输入的小数位数超过了允许的最大位数,这个构建过程能够失败。我的理解是,你希望对有问题的输入进行拒绝处理。
但这里有一个根本性的问题:如果你的API设计中使用double
来表示货币单位,那么这个设计从根本上讲是有缺陷的。简单来说,使用double
来处理货币是绝对不可行的。原因在于double
类型的本质和货币计算所需的精确性之间存在冲突。
double
类型使用64位来存储数值,这意味着它最多只能精确表示大约264个不同的值。而货币的数值理论上可以有无限多个小数点后的数字,特别是在财务计算中,即使是最微小的误差也可能导致严重的后果。
double
通过一种“近似”系统来处理这个问题,即它只能够精确表示特定的一组数值(远少于264),而对于其他非精确可表示的数值,它会默默地将其四舍五入到最接近的可表示值。这种近似处理会导致所谓的“静默错误”,例如,看似简单的运算如0.1 * 10
可能并不等于1.0
,或者给一个大数加1却没有任何变化。
因此,如果你想让构造方法拒绝不安全的输入,仅仅限制小数点后的位数远远不够。实际上,任何使用double
来进行的货币计算都可能引入无法接受的误差。
正确的做法是完全摒弃double
,转而采用整数类型来表示货币,通常是与货币最小单位对应(比如欧元中的“分”)。这样,所有的金额都以最小货币单位来存储和计算,从而确保了精确度。
举个例子,我的API应该像这样设计:
public class Money {
public static Money of(long units, Currency currency) {
// 实现逻辑,确保units是以currency的基本单位表示的
}
}
在这个设计中,当你创建一个金额时,比如12.34欧元,你会传入1234作为单位数量(因为1欧元等于100分),同时指定货币类型为欧元。这样,所有计算都在整数层面进行,避免了浮点数运算带来的误差问题。
综上所述,我需要重新考虑API的设计,确保它基于整数类型来处理货币值,以此保证计算的准确无误,并且能够优雅地处理那些不能精确分配的情况,比如在分配账户余额时遇到的非整数分配问题。这要求我在设计中充分考虑到货币的原子单位以及如何处理财务规则,确保系统的健壮性和准确性。