504 Часть II. Типовые решения
денежными объектами. Кроме того, все содержимое кошелька может быть переведено в
какую-либо одну валюту.
Операции умножения и деления еще более сложны, потому что их выполнение неми-
нуемо приводит к проблемам округления. При умножении денежных величин можно
воспользоваться обычными числами: например, чтобы подсчитать величину пятипро-
центного налога на сумму заказа, ее следует умножить на 0,05. Таким образом, выполне-
ние операций умножения над денежными объектами подразумевает умножение объекта
на скалярную величину, что не так сложно.
Настоящие проблемы округления возникают в ситуациях, когда заданную сумму де-
нег необходимо распределить между несколькими различными местами. В связи с этим
попробуйте решить простенькую задачку, предложенную Мэттью Фоммелем (Matthew
Foemmel). Предположим, у меня есть бизнес-правило, в соответствии с которым я дол-
жен положить имеющуюся сумму денег на два банковских счета: 70% суммы на первый
счет и 30% — на второй. Пусть эта сумма составляет 5 центов. В результате вычислений
я получаю, что на первый счет нужно положить 3,5 цента, а на второй — 1,5 цента. Если я
выполню стандартное округление до ближайшего целого числа, полтора цента превра-
тятся в два, а три с половиной — в четыре. Как видите, в сумме у меня появился лишний
цент. Если же я округлю полученные величины путем отбрасывания дробной части, то
получу 4 цента, т.е. потеряю один цент. Таким образом, я не могу применить к обеим ве-
личинам одну и ту же схему округления, потому что все универсальные схемы приводят к
потере или приобретению "лишних" центов.
Мне встречалось несколько вариантов решения данной проблемы.
• Наиболее распространенное решение — не обращать на это внимания (в конце
концов, речь идет о каком-то там центе). Тем не менее большинство банковских
клиентов были бы крайне возмущены подобной небрежностью по отношению к
своим деньгам.
• Вычислять сумму, которую нужно положить на последний счет, путем вычитания
из общей суммы денег тех сумм, которые были положены на все предыдущие сче
та. Это позволит избежать потери центов, однако в конце концов может привести
к накоплению на последнем счете довольно большого количества центов, которые
ему не принадлежат.
• Предоставить пользователям класса Money возможность самим выбирать схему
округления при вызове нужного метода. В этом случае программист может ука
зать, что 70% суммы следует округлить до следующего целого числа, а 30%
суммы— до предыдущего. Разумеется, подобный метод весьма неудобен, если
деньги нужно распределить не между двумя, а, скажем, между десятью счетами.
Кроме того, об указании схемы округления легко забыть. Иногда во избежание по
добных проблем метод умножения класса Money принудительно снабжают пара
метром округления. Это не только обяжет программиста выбрать схему округле
ния, но и напомнит ему о необходимости написания тестов. К сожалению, данный
подход весьма непрактичен при выполнении множества вычислений (например,
при подсчете величин налогов), использующих одну и ту же схему округления.
• Идеальное, на мой взгляд, решение — создать специальную функцию-распреде
литель. В качестве параметра этой функции будет передаваться список чисел, оп
ределяющих пропорцию, согласно которой необходимо распределить деньги