Isso parece que o padrão de visitantes pode ser útil. Ele não se livra do código de despacho, mas é uma maneira bastante elegante e padrão de implementar o despacho. Em OOP no estilo Java:
// an interface for anything calculatable
interface FinancialInstrument {
// acceptVisitor makes the pattern obvious,
// but you might want to pick a more domain-specific name.
<T> T acceptVisitor(FinancialInstrumentVisitor<T> v);
}
class Option implements FinancialInstrument {
<T> T acceptVisitor(FinancialInstrumentVisitor<T> v) {
return v.visit(this);
}
}
class Equity implements FinancialInstrument {
<T> T acceptVisitor(FinancialInstrumentVisitor<T> v) {
return v.visit(this);
}
}
// an interface for all calculators
// basically, this consists of "visit" overloads that handle each type
interface FinancialInstrumentVisitor<T> {
T of(FinancialInstrument fi); // convenience wrapper
T visit(Option o);
T visit(Equity e);
}
class PriceCalculator implements FinancialInstrumentVisitor<Price> {
Price of(FinancialInstrument fi) {
return fi.acceptVisitor(this);
}
Price visit(Option o) { ... }
Price visit(Equity e) { ... }
}
class RiskCalculator implements FinancialInstrumentVisitor<Risk> {
Price of(FinancialInstrument fi) {
return fi.acceptVisitor(this);
}
Risk visit(Option o) { ... }
Risk visit(Equity e) { ... }
}
Exemplo de uso:
PriceCalculator price = new PriceCalculator();
for (FinancialInstrument fi : portfolio) {
doSomethingWith(price.of(fi));
...
}
O padrão de visitante é ótimo sempre que você deseja ser livre para adicionar facilmente novas operações, mas o conjunto de objetos em que essas operações trabalham é bastante fixo. Observe que adicionar um tipo de instrumento financeiro requer que uma sobrecarga seja adicionada à interface FinancialInstrumentCalculator
, que não é compatível com versões anteriores - todos os consumidores dessa interface precisarão ser atualizados para suportar esse método. Este não é o caso se o novo instrumento financeiro for um subtipo de uma classe existente, como o manipulador existente pode ser usado.
Se novos instrumentos financeiros aparecem com mais frequência do que novas operações, usar o padrão de visitantes é provavelmente uma má ideia. Observe que qualquer estratégia usada para corresponder n
operações a m
instrumentos financeiros, você sempre (subtipagem ignorada por um momento) acabará com n·m
métodos para codificar.
Se o seu idioma permitir isso, recomendo usar traços ou construções semelhantes para fornecer padrões na interface FinancialInstrumentCalculator
. Por exemplo. quando eu tenho três tipos
class A implements FinancialInstrument
class B extends A
class C extends A
, os métodos de visitante para B
e C
podem ser implementados pelo método de visitante para A
por padrão:
interface FinancialInstrumentVisitor<T> {
default T of(FinancialInstrument fi) { return fi.acceptVisitor(this); }
T visit(A a);
default T visit(B b) { return visit((A) b); }
default T visit(C c) { return visit((A) c); }
}