Money, SmallMoney and IMoney – Configuring the IMoney Implementation at Run Time

Added by Roger Dunn about 6 years ago

Many applications developed using <xweld/> will settle on and use a specific implementation of IMoney to represent amounts of currency. Other applications will be designed to support changing or "plugging in" an alternative implementation at runtime without changing their application code. <xweld/> Tools extensively support this pattern and the following discussion outlines some considerations if you decide to implement this pattern for IMoney in your applications.

<xweld/> IMoney is designed to be configurable and use that configuration at runtime through its MoneyFactory, or using dependency injection. This configurability permits an application to decide at run time whether it wants to use Money versus SmallMoney versus some other IMoney class of its own.

This configurability is important for applications that need the ability to assess the tradeoffs of Money versus SmallMoney. Money is backed by BigDecimal, which means that it has guaranteed precision and supports very large numbers, but it is potentially slower because it can't take advantage of an FPU and it has a relatively hefty memory footprint. SmallMoney is backed by a double, which gives is less precision and a lower max value, but supports arithmetic operations optimized on an FPU and it is about one order of magnitude smaller than Money.

Here are some tips/do's and don'ts when developing <xweld/> IMoney:

DO use MoneyFactory.create(String, CurrencyCode) to instantiate IMoney instances. This factory method will create a Money or SmallMoney, depending on the value specified in the System property "", and pass the String (which represents the amount) to the appropriate constructor. Since the amount is based on a String, it is safe (and correct!) for the current IMoney classes and any other implementations added down the road.

DO NOT instantiate Money or SmallMoney directly. Why? Good question! Money and SmallMoney are not compatible with each other. So your choices become really simple: use the MoneyFactory or @Inject to ensure that all IMoney instances share the same backing representation, or you have to convert your IMoney operands to ensure that they are compatible. The former approach is dead easy, and the latter approach is highly prone to error, especially if many different developers are working on the same code.

BE EXCEEDINGLY CAREFUL if you use MoneyFactory.create(Number, CurrencyCode). This is only safe to use if you can guarantee that the class of Number that you're passing in exactly matches the kind of IMoney object that is being constructed. In other words, if you pass a BigDecimal to this factory method, it'll be okay if it creates a Money instance, but it will fail if it tries to create a SmallMoney instance. In other words, if you use this overloaded factory method, you defeat the configurability of xweld IMoney.

There are two other things that you need to be scrupulous about, so you will want to review your code:

(1) If you want to achieve the best performance, leave the arithmetic up to the IMoney object. The anti-pattern for this is (a) extracting the amount from the IMoney object as a Number, (b) putting that in a known representation (e.g. a BigDecimal), (c) doing BigDecimal-specific math, and finally, (d) putting things back as an IMoney via the MoneyFactory.create(String, CurrencyCode) method. If you let IMoney do the work, you avoid the extra BigDecimal instantiation (wastes time and space) and you also avoid having to stringize that BigMoney when you call MoneyFactory.create (wastes even more time and space).

(2) Look at any application code and see if you're using BigDecimal directly. If you are, maybe you could use Number instead? Or let IMoney do the work? If you are using BigDecimal and a CurrencyCode, then you’re duplicating the work that IMoney was designed to do for us, so we need to revisit it and see if we can use IMoney instead.