Vamos imaginar este cenário: seu sistema de faturamento possui uma InvoiceValue
classe que possui n entradas, uma para cada produto adquirido.
Seu InvoiceValue::Entry
, expõe um método subtotal
que é produto de preço e quantidade.
def subtotal
price * quantity
end
Assim, para calcular o InvoiceValue
total que você usaria:
def total
@entries.inject(0) {|result, e| result += e.subtotal }
end
O que é um pouco estranho: se você imaginar InvoiceValue::Entry
como um valor puro, deve ser fácil de ser usado diretamente nas expressões matemáticas, como +=
.
A primeira coisa que você é tentado a fazer é sobrescrever Numeric#+
, a fim de lidar com esse caso especial, mas isso polui muito o comportamento desse método.
Felizmente, Ruby é uma linguagem tão bonita, que permite coerção implícita entre os tipos. Tudo que você precisa fazer é implementar um método:
protected
def coerce(other)
[other, subtotal]
end
Existem algumas coisas que precisam de explicação. Em primeiro lugar, #coerce
requer o retorno de um array com dois elementos: o objeto recebido como argumento e o valor coagido do mesmo tipo.
O other
here representa o outro objeto contra o qual estamos tentando lançar (neste caso, a Fixnum
) e subtotal
é o valor que queremos usar nas expressões aritméticas.
Isso simplifica muito a nossa implementação:
def total
@entries.inject(0, :+)
end