CSC/ECE 517 Fall 2011/ch7 7a kr: Difference between revisions
Line 81: | Line 81: | ||
=== Disadvantages === | === Disadvantages === | ||
* Since the currency conversion data is set within the | |||
* Since the currency conversion data is set within the '''Currency''' object itself | |||
* There is no locking present for the '''Currency''' object and there could be race conditions when users are simultaneously setting and getting exchange rates | |||
* The current implementation can only convert to and from the default currency type | |||
* Since this implementation can take multiple types of arguments as the monetary value but does not add methods to primitives for converting them to '''Money''' objects, type checking has to be done on arguments to determine how to handle them. This is soemthing that we want to avoid as part of object oriented design. | |||
== References == | == References == |
Revision as of 22:27, 1 December 2011
CSC/ECE 517 Fall 2011/ch7 7a kr
7a. Representing money. Skrien Chapter 6 gives an example of a class that can be used to represent money. But how is it done in real programs? Investigate, and report on the advantages and disadvantages of other approaches vs. Skrien's.
Introduction
A common task in real world application programs is conducting operations on and manipulating money values. The base data type used to represent money can be one of several possible implementations. Object oriented languages have the ability to represent this information using classes. Primitive data types such as integers, floating point numbers can also be used to represent money. There are certain inherent advantages and disadvantages with each of these methods of representation. This wiki article explores these different implementations and analyzes their relative merits and demerits. We also focus on the various operations that can be performed with money values and analyze how different languages and schemes help while working with money values.
Money Gem<ref name = moneygemwebsite /> (Ruby)
Overview
The Ruby Money gem borrows quite a bit from the Skrien<ref name = skrien /> text methodologies while using the dynamic properties of Ruby to allow for more flexibility. However, it should be noted that this implementation does not support an object containing mixed currencies as mentioned in the Skrien text and some behavior implemented goes against it's principles.
The Money gem consists of a main Money class which houses several sub-classes and modules which allow for everything to be done from the main Money class directly without the users knowledge. This implementation also mostly sticks to the idea that a Money object is immutable and thus any conversions and operations result in a new Money object. The only exception to this is that the currency identifier for a Money object can be changed once it has been created.
Internally, a Money object stores money as an integer in the form of cents. It should be noted that while the variable is called cents, it can apply to all currencies which have a subunit of denomination. The Money object also stores a bank and currency as well as default bank and currency types at the class level. Similar to the way the Skrien text uses the Currency and CurrencyConverter classes, the Money class uses internal classes for currency information and currency conversion as well, called Currency and Bank respectively.
The Currency class contains information such as the ISO4217 three letter and number codes for common currencies as well as number of subunits in a denomination (ie, cents in a dollar) and delimiters for units. Through the use of the currency class, Money objects can be initialized with a Currency value and have specific output formats based on currency. The currency class also internally extends the CurrencyLoader class which allows for specifying different locations for loading currency information to allow for flexible currency management. Below is an example of the USD currency information loaded by a default JSON configuration file:
"usd": { "priority": 1, "iso_code": "USD", "name": "United States Dollar", "symbol": "$", "subunit": "Cent", "subunit_to_unit": 100, "symbol_first": true, "html_entity": "$", "decimal_mark": ".", "thousands_separator": ",", "iso_numeric": "840" },
The Bank class is also an internal class to the Money class but it resides at the class level. This means that the Bank object follows the Singleton pattern in that only one Bank object exists across all of the currency. The purpose of the Bank is to maintain information related to currency values such that currency conversion can take place. Since the Bank class inherits from an interface, this allows for extensible money conversion schemes such as being able to 'scrape' data from the internet and use it to populate conversion ratios.
The Money class also has multiple modules internally that expand the operations that can be performed on Money objects, including converting to and from numbers, strings and symbols. Through the use of Ruby's dynamic nature, to_money() methods are added to the Numeric, String and Symbol types.
Below is an example from the official website <ref name = moneygemwebsite />:
require 'money' # 10.00 USD money = Money.new(1000, "USD") money.cents #=> 1000 money.currency #=> Currency.new("USD") # Comparisons Money.new(1000, "USD") == Money.new(1000, "USD") #=> true Money.new(1000, "USD") == Money.new(100, "USD") #=> false Money.new(1000, "USD") == Money.new(1000, "EUR") #=> false Money.new(1000, "USD") != Money.new(1000, "EUR") #=> true # Arithmetic Money.new(1000, "USD") + Money.new(500, "USD") == Money.new(1500, "USD") Money.new(1000, "USD") - Money.new(200, "USD") == Money.new(800, "USD") Money.new(1000, "USD") / 5 == Money.new(200, "USD") Money.new(1000, "USD") * 5 == Money.new(5000, "USD") # Currency conversions some_code_to_setup_exchange_rates Money.new(1000, "USD").exchange_to("EUR") == Money.new(some_value, "EUR")
Advantages
- The internal CurrencyLoader class allows for easily extensible currency information sources, even allowing currency information to be dynamically loaded
- Due to the Bank implementation, additional class implementations can be loaded and changed out at runtime to allow for more complex, an potentially real-time, currency conversion information
- The fact that the Currency and Bank classes are internal to the Money class, the user doesn't have to know about their implementation
- Ruby's dynamic nature allows for classes to be internal to Money, yet defined in separate files from the Money class, helping to keep the code modular from a developers point of view
- The addition of to_money() methods to basic Ruby primitives makes it easier to create money from just about any type
- Since Ruby supports arbitrarily large integer values, there is theoretically no upper bound to the amount of money that can be stored in a Money object
Disadvantages
- Doesn't support multiple currency types in a single object
- A Money object is immutable except in one way, ability to change currency type, which is an inconsistency that could easily have been avoided
- While the addition of to_money() methods to primitive types makes some things easier, it adds the additional ability to incorrectly combine an object of type Money with one that is not
- Since the Bank object is a singleton contained within the Money class, any modifications to currency exchange rates require locking of the Bank object which could result in performance degradation if exchange rates are modified often
python-money<ref name = pymoneywebsite /> (Python)
Overview
Advantages
Disadvantages
- Since the currency conversion data is set within the Currency object itself
* There is no locking present for the Currency object and there could be race conditions when users are simultaneously setting and getting exchange rates * The current implementation can only convert to and from the default currency type
- Since this implementation can take multiple types of arguments as the monetary value but does not add methods to primitives for converting them to Money objects, type checking has to be done on arguments to determine how to handle them. This is soemthing that we want to avoid as part of object oriented design.
References
<references> <ref name = skrien> Object Oriented Design Using Java / Dale Skrien -- 1st Ed. </ref> <ref name = moneygemwebsite> http://money.rubyforge.org/ </ref> <ref name = pymoneywebsite> http://code.google.com/p/python-money/ </ref> </references>