A implementação é relativamente simples. Fazemos MoneyBag
herdar Money
e alteramos as operações aritméticas de soma e subtração. Teremos que adicionar um método simplify()
para que possamos reduzir um MoneyBag a um Money simples, quando isso for possível, ou seja, quando MoneyBag contiver o valor de apenas uma moeda.
A implementação a seguir de Money
mostra apenas as alterações que seriam necessárias à classe Money
exemplificada no padrão Money. Note-se que escolhemos modificar a forma como as operações são feitas de forma que haja integração entre Money
e MoneyBag
public class Money {
protected Money (){
}
public Money plus ( Money other ){
// usamos o mecanismo de moneyBag porque é genérico. Isso evita usar ifs
return new MoneyBag().plus(this).plus(other).simplify() ;
}
public Money subtract ( Money other ){
return new MoneyBag().plus(this).subtract(other).simplify() ;
}
public Money negate (){
return new Money ( -amount, currency ) ;
}
// nível de pacote
Money getAmount ( Currency currency ){
if ( currency.equals ( this.currency )){
return this;
}
return new Money(0, currency) ;
}
public Money simplify (){
return this ;
}
}
public class MoneyBag extends Money {
private Map< Currency, Money > bag = new HashMap< Currency, Money >();
private MoneyBag copy(MoneyBag other){
MoneyBag m = new MoneyBag();
m.bag.putAll(other.bag);
return m;
}
public Money plus (Money other){
Money bagAmount = this.getAmount (other.getCurrency());
Money otherAmount = other.getAmount (other.getCurrency());
Money total = bagAmount.plus(otherAmount);
MoneyBag result = MoneyBag.copy(this);
result.bag.put(other.getCurrency() , total);
}
public Money subtract(Money other){
return this.plus(other.negate());
}
Money getAmount (Currency c){
Money amount = bag.get(other.getCurrency());
if ( amount == null ){
return new Money(0, c) ;
}
return amount;
}
public Currency getCurrency(){
if ( bag.size () > 1 ){
return new MultipleCurrency(bag.keySet());
} else {
return bag.keySet().iterator().next();
}
}
public Money simplify (){
if ( bag.size () == 1 ){
Map.Entry entry = ( Map.Entry ) bag.entrySet().iterator.next();
return new Money( entry.getValue() , entry.getKey());
} else {
return this ;
}
}
}
Esta é uma das muitas implementações possiveis. Escolhemos fazer moneyBag um objeto imutável o que significa que a cada alteração temos que criar um objeto novo. Assim mantemos a coerência com a imutabilidade da implementação padrão de Money
.O ponto importante aqui é que MoneyBag contenha um conjunto de pares moeda-valor.
O objeto MoneyBag
é na realidade um objeto Money
também. Isto implica que MoneyBag
tem uma moeda associada. Mas na realidade ele tem várias. Por isso, ele retorna uma instancia de MultipleCurrency
sendo que só retorna uma única moeda quando apenas uma existe.
Vejamos como seria a implementação desta classe:
public class MultipleCurrency extends Currency {
private Set<Currency> currencies;
private int hashCode;
public MultipleCurrency(Set<Currency< currencies){
this.currencies = currencies;
this.hashCode = Hash.hash(currencies);
}
public boolean equals(Object other){
return other instanceof MultipleCurrency
&& equalsMultipleCurrency((MultipleCurrency)other);
}
public boolean equals(MultipleCurrency other){
return CollectionUtils.isContentEqual(this.currencies, other.currencies);
}
public int hashCode(){
return hashCode;
}
}
Usamos os objetos auxiliares Hash
que calcula o hash de uma coleção e CollectionUtils
que verifica se os objetos dentro do Set
são iguais independentemente da ordem. O hashCode
é calculado apenas uma vez, já que a coleção de moedas não será modificada.
Repare que nunca uma MultipleCurrency
será igual a uma outra Currency