On Github xala3pa / Money-Talk-Greach-2016
https://en.wikipedia.org/wiki/Cohesion_(computer_science)
[ 'totalPrice': CurrencyUtils.format( purchase.totalPrice, purchase.country ) ] CurrencyService currencyService BigDecimal totalPrice = currencyService.convertCurrency( purchase.totalPrice, purchase.currency, gateway.currency, round )
['totalPrice': purchase.totalPrice.format( Locale.US )] Money totalPrice = purchase.totalPrice.exchangeTo( gateway.currency )
http://www.tutorialspoint.com/cplusplus/cpp_data_encapsulation.htm
@groovy.transform.CompileStatic final class Money implements Serializable, Comparable<Money>, MoneyExchange, MoneyFormat { final BigDecimal amount final Currency currency // ... }
@groovy.transform.PackageScope trait MoneyExchange { //... Money exchangeTo(Currency to, Exchange exchange = getCurrentExchange()) { //... } }
@groovy.transform.PackageScope trait MoneyFormat { //... String format(Locale locale = Locale.default) { //... } }
http://c2.com/cgi/wiki?PrimitiveObsession
https://sourcemaking.com/refactoring/smells/primitive-obsession
https://sourcemaking.com/refactoring/smells/primitive-obsession
class Ticket implements Serializable { BigDecimal totalPrice Currency currency //... }
class Ticket implements Serializable { Money totalPrice //... }
https://sourcemaking.com/refactoring/smells/feature-envy
https://sourcemaking.com/refactoring/smells/feature-envy
https://sourcemaking.com/refactoring/smells/feature-envy
https://sourcemaking.com/refactoring/smells/feature-envy
BigDecimal totalPrice = currencyService.convertCurrency( purchase.totalPrice, purchase.currency, gateway.currency, round )
Money totalPrice = purchase.totalPrice.exchangeTo( gateway.currency )
@groovy.transform.CompileStatic final class Money implements Serializable, Comparable<Money>, MoneyExchange, MoneyFormat { final BigDecimal amount final Currency currency private final static MathContext MONETARY_CONTEXT = MathContext.DECIMAL128 final static class CurrencyMismatchException extends RuntimeException { CurrencyMismatchException(String aMessage) { super(aMessage) } } //... Money(Number amount, Currency currency) { this.amount = (BigDecimal) amount this.currency = currency } //... }
class MoneyUserType implements UserType, ParameterizedType { private final static String DEFAULT_CURRENCY_COLUMN = 'currency' private final static int[] SQL_TYPES = [Types.DECIMAL] as int[] Properties parameterValues //... Object nullSafeGet(ResultSet rs, String[] names, Object owner) { //... } void nullSafeSet(PreparedStatement st, Object value, int index) { //... } Class returnedClass() { Money.class } int[] sqlTypes() { SQL_TYPES } }
class Ticket implements Serializable { Money totalPrice //... static mapping = { totalPrice type: MoneyUserType, params: [currencyColumn: 'divisa'] } }
trait MoneyExchange { //... Money exchangeTo(Currency to, Exchange exchange = getCurrentExchange()) { //... } }
interface Exchange { BigDecimal getRate(Currency from, Currency to) }
Text
trait MoneyFormat { //... String format(Locale locale = Locale.default) { //... } //... }
class StructuredMoneyEditor extends AbstractStructuredBindingEditor<Money> { private static final String currencyPlaceholder = '¤' Money getPropertyValue(Map values) { DecimalFormat formatter = getCustomDecimalFormatter(values) BigDecimal parsedAmount = getParsedAmount(formatter, (String) values.amount) new Money(parsedAmount, (String) values.currency) } //... }
def doWithSpring = { //Custom structured property editor data binding for Money type moneyEditor com.ticketbis.money.StructuredMoneyEditor }
class GreaterThanZeroConstraint extends AbstractConstraint { private static final String DEFAULT_INVALID_MESSAGE_CODE = 'default.gtZero.invalid' static final String CONSTRAINT_NAME = 'gtZero' private boolean gtZero //... protected void processValidate(Object target, Object propertyValue, Errors errors) { if (!validate(propertyValue)) { def args = (Object[]) [constraintPropertyName, constraintOwningClass, propertyValue] rejectValue(target, errors, DEFAULT_INVALID_MESSAGE_CODE, "not.${CONSTRAINT_NAME}", args) } } //... }
def doWithSpring = { //... ConstrainedProperty.registerNewConstraint( GreaterThanZeroConstraint.CONSTRAINT_NAME, GreaterThanZeroConstraint) //... }
class Ticket implements Serializable { Money totalPrice //... static constraints = { totalPrice( nullable: false, gtZero: true ) //... }
class MoneyTagLib { static namespace = 'money' def inputField = { attrs -> def name = attrs.remove('name') def value = attrs.remove('value') //... } def format = { attrs -> Money value = new Money(attrs.value) //... } }
<money:inputField name="totalPrice" value="123.45" currency="EUR"/> <money:inputField name="totalPrice" value="${ money }"/> <money:format value="${ money }" pattern="¤ ##,##0.00"/> <money:format value="${ money }" numberFormat="${ formatter }"/>