Trabajar con cantidades de dinero nunca ha sido un asunto sencillo: redondeos, cambios de divisa… una mala gestión puede derivar en perdidas para nuestra empresa o en cobrar de más a nuestros clientes, con el consiguiente perjuicio reputacional.
El patrón Money
Martin Fowler publicó en 2002 su famoso libro Patterns of Enterprise Application Architecture, donde uno de los patrones incluidos era el llamado patron Money. Con este patrón, Fowler pretende simplificar la gestión de cantidades monetarias y reducir los posibles riesgos que antes comentábamos.
El patrón Money se basa en otro patrón conocido como Value Object, y consiste en modelar un pequeño objeto que represente una cantidad monetaria. Para ello se usan dos atributos: la cantidad (amount), y la moneda (currency), y una serie de operaciones lógicas y matemáticas:
Atributos
- amount: es el importe, representado siempre en la unidad mínima de la moneda asociada. Esta representación es perfecta para realizar comparaciones entre cantidades de una misma moneda, ya que las comparaciones entre números en coma flotante siguen siendo un asunto sin resolver en cualquier lenguaje.
Es importante saber que no todas las monedas son como el dólar o el euro, ya que no en todas la unidad mínima es el céntimo. Existen monedas donde la propia moneda es la unidad mínima, y otras donde no sólo tienen céntimos, sino que tienen unidades más pequeñas que el céntimo.
Ejemplos de estos casos son el dinar de Bahrain, con milésimas partes del dinar, o el peso de Chile, donde no hay unidades inferiores al peso. Se puede consultar una lista con todas las monedas este enlace.
- currency: es el código ISO-4217 de la moneda con la que estamos trabajando. Es un código de 3 letras que representa la moneda de forma internacional, por ejemplo EUR para Euros.
Métodos / Operaciones
- +, -, *, / : operaciones matemáticas básicas, como la suma, resta, multiplicación, división…
- allocate: una operación especial destinada a dividir una cantidad de dinero entre dos o más receptores. Luego entraremos más en detalle.
- <, >, ≤, ≥, = : operaciones lógicas básicas, como menor que, mayor que, menor igual que, mayor igual que, igual…
Ejemplos
Queremos representar la cantidad 528.75€, cuya unidad mínima es el céntimo de euro:
- amount: 52875
- currency: EUR
Queremos representar la cantidad 528.75.د (dinares de Bahrain), cuya unidad mínima es la milésima parte de un dinar.
- amount: 528750
- currency: BHD
Para representar visualmente la cantidad, solo necesitamos aplicar la sencilla fórmula: amount / (10 x unidad mínima).
Implementación
Es sencillo realizar una implementación básica de este patrón, lo realmente tedioso es tener actualizada siempre la lista de monedas, con sus unidades mínimas o sus ratios de conversión.
Veamos una implementación sencilla en PHP:+
A esta implementación básica le faltarían todos los métodos de las operaciones lógicas y matemáticas. He querido dejar de lado esta implementación para poder comentar que podemos hacerlo en dos versiones distintas:
- Orientado a objetos: añadiendo los métodos
add
,substract
,multiply
,divide
… Este sería el estilo orientado a objetos clásico. Nos permitiría tomar dos enfoques: mutable (modifica la instancia actual) o inmutable (no modifica la instancia, devuelve una nueva). De esta manera podremos ejecutar cosas como:
- Sobrecarga de operadores: muchos lenguajes orientados a objetos, como Java, PHP, Python o Ruby permiten lo que llamamos sobrecarga de operadores, que no es más que permitir cambiar el comportamiento de los operadores aritméticos y lógicos cuando estos son aplicados a objetos de cierta clase, y no a tipos primitivos como se hace habitualmente.
Esto nos permitiría sobrecargar los operadores de tal manera que podríamos ejecutar algo así como:
La implementación básica de este patrón es realmente muy sencilla, pero si queremos sacarle el máximo provecho tenemos implementaciones en casi todos los lenguajes, que además cubren otros aspectos como la representación visual de la cantidad (situar el símbolo monetario a la derecha o a la izquierda), o la conversión entre divisas.
Algunas implementaciones conocidas:
- PHP: https://github.com/moneyphp/money
- Ruby: https://github.com/RubyMoney/money
- Python: https://github.com/poswald/python-money
Método allocate
Este método, como ya comentamos antes, es un método especial usado para distribuir adecuadamente una cantidad entre dos o mas receptores cuando no puede hacerse de forma exacta (sin sobras). Por ejemplo cuando lo hacemos basándonos en porcentajes o ratios.
Veamos un caso específico:
Queremos repartir 5cts de euro entre dos cuentas al 30%-70%. Esto implica un reparto de 1,5cts y 3,5cts. El problema es que el céntimo es la unidad mínima de la divisa euro, y no podemos partir un céntimo en dos, por lo tanto el reparto exacto no puede hacerse.
La solución más habitual es repartir 1 céntimo a cada recipiente mientras no hayan llegado a la cantidad justa. Una vez haya sobrepasado esa cantidad, deja de recibir dinero.
Recipiente A | 1.5cts |
Recipiente B | 2.5cts |
# | Recipiente A | Recipiente B |
1 | +1cts. Total: 1 1 < 1.5 ✅ | +1cts Total: 1 1 < 2.5 ✅ |
2 | +1cts Total: 2 2 < 1.5 ❌ | +1cts Total: 2 2 < 2.5 ✅ |
3 | No recibe más (2 < 1.5 ❌) | +1cts Total: 3 3 < 2.5 ❌ |
Recipiente A | 2cts |
Recipiente B | 3cts |