数量

用数量和单位来表示有量纲的值

2004年5月10日

这是我在2000年代中期进行的企业应用架构进一步发展写作的一部分。不幸的是,太多其他事情吸引了我的注意力,所以我没有时间进一步研究它们,而且在可预见的未来我也没有时间。因此,这些材料还处于草稿阶段,在我能够找到时间再次处理它们之前,我不会进行任何更正或更新。

在许多情况下,我们希望计算机能够表示有量纲的量:例如六英尺或三十公斤。通常这些量被表示为裸数字,主要是因为这是在语言提供的有限类型系统中我们能做的最好的事情。

但是,使用对象让我们可以在每次它们增加价值时添加新的基本类型,而为有量纲的量创建特定类型则会增加相当大的价值。当有量纲的值是货币时,这一点尤其重要。货币是Quantity的一种特殊情况,但也可能是最广泛使用的,因为很多人愿意花钱来观看货币。

工作原理

Quantity的基本思想是一个将数量与单位结合在一起的类。这样说,数量是一种简单的表示。然而,Quantity真正有趣的地方在于你可以应用于它的行为。

第一种行为是算术。你应该能够像添加两个数字一样轻松地将两个数量加在一起。此外,数量应该对加法和减法有智能:至少防止你将 6 英尺加到 180 磅。

当你想将类似的单位加在一起时,例如将 500 米加到 3 公里时,加法会出现更复杂的问题。最简单的方法是拒绝这种加法,迫使客户端进行转换。更复杂的方法是看看是否可以将一个参数转换为另一个参数,如果可以,就可以执行加法。你能够做到这一点的程度取决于你的转换器的复杂程度。

乘法在复杂程度上也有类似的变化。最简单的方法是只允许乘以和除以标量数字。更复杂的方法是允许乘以其他数量,但随后你必须计算出结果的单位。因此,如果你将 150 英里除以 2 小时,你将得到 75 英里/小时。

需要比较运算,这样你就可以知道六英尺是否大于五英尺。这里的问题与加法的问题非常相似——你必须选择进行多少转换。

提供一个简单的接口来允许直接对数量进行转换非常有用,尽管转换工作通常最好留给单独的转换器对象。但是,如果你只有几个单位,那么将它们直接嵌入到数量类中会更容易。

你可以赋予数量的最有用的行为之一是提供打印和解析方法,这些方法允许你轻松地生成字符串并从字符串生成数量。这种简单的模式可以极大地简化许多输入和输出行为,无论是对文件还是在 GUI 接口中。

对于简单的打印,你可以有一个默认值,例如先打印数量,然后打印单位。当在某些情况下你想先打印单位,然后打印数字,而在其他情况下则相反时,这种方法就会失效。通常,这种变化将取决于单位,因此在这些情况下,你可以将打印和解析行为放在单位上,并委托给它。

货币

我在几乎所有我工作的系统中都使用Quantity,但我很少用它来表示物理单位。大多数情况下,我用它来表示货币。物理单位的许多注释都是一样的,但货币在使用时确实有自己的特点需要注意。

最大的变化围绕着转换的整个领域。虽然物理单位的转换形式不会随着时间的推移而改变,但货币的汇率却一直在变化。显然,这会影响转换操作,但这种影响也会波及加法、减法和比较操作。

当你转换货币时,至少你需要提供某种时间参考,其粒度将取决于应用程序。但在许多情况下,你可能在不同的上下文中拥有不同的转换。

所有这一切的结果是,你需要对货币的算术或比较操作中的自动转换更加小心。因此,你经常会发现它们是不允许的。

除了Quantity的功能外,货币的一个特别有用的功能是处理舍入。货币通常用小数部分表示,但你永远不应该使用实数来处理货币。原因是实数的舍入行为几乎永远不会对应于货币所需的舍入行为,而忽略这一事实很容易导致间歇性错误,这些错误的金额很小,但令人沮丧。

然而,货币对象可以编码自己的舍入规则,这意味着在大多数情况下,你不需要在使用货币时意识到舍入规则。

与之相关的是除法的棘手问题。如果你将 100 美元除以 3,你会得到什么?通常答案并不像 33.33 美元那样简单。问题是,如果你将 33.33 美元相乘,你将得到 99.99 美元——这意味着一分钱不见了。会计师不喜欢一分钱不见了。因此,你必须找出适用于你所处情况的政策。通常的规则是,应该有人得到额外的几分钱,尽管这无关紧要。因此,你可以在货币对象中添加一个方法,以返回你需要从除法中分配的货币集合。

关系数据库

关于Quantity的一个常见问题是如何在没有创建新轻量级类型的情况下将其用于关系数据库和其他系统。你是否应该为每个货币值存储一个金额和一个货币代码?

这里的问题出现在有一个约束迫使所有货币在特定上下文中具有相同的货币。因此,考虑你有一个有许多条目的帐户的情况。每个条目都有一个货币属性来显示条目的金额,但帐户上的所有条目都具有相同的货币。是否合理地将货币存储在帐户上一次,而不是在条目中重复货币?

我倾向于回避这个问题,并将其留给你的数据库设计的具体情况。我仍然敦促你在代码中使用货币对象:你是否以及如何在数据库中存储它们取决于你。毕竟,数据库不会从Quantity中获得任何行为优势。你只能在代码中获得这些优势。

何时使用它

正如你所了解的,我使用Quantity,至少在货币变体中,很多。事实上,在面向对象的環境中,几乎没有理由不使用它。我经常注意到,人们害怕使用像这样的小型对象,主要是因为不熟悉。性能是一个经常被提及的担忧,尽管我还没有看到或听说Quantity成为性能的真正问题。

有一种说法认为,当只有一个单位时,使用Quantity不值得,因此当你只有一个货币时,你不应该使用货币。但是,货币的许多价值来自它的行为,而不是它的多单位功能,因此即使在这种情况下,我也会使用它。

进一步阅读

P of EAA中,对货币变体进行了更详细的讨论。