Sembra che il pattern Visitor possa essere utile. Non si sbarazza del codice di spedizione, ma è un modo abbastanza elegante e standard per implementare il dispacciamento. In OOP in stile 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) { ... }
}
Esempio di utilizzo:
PriceCalculator price = new PriceCalculator();
for (FinancialInstrument fi : portfolio) {
doSomethingWith(price.of(fi));
...
}
Il modello di visitatore è ottimo quando vuoi essere libero di aggiungere facilmente nuove operazioni, ma l'insieme di oggetti su cui queste operazioni funzionano è abbastanza fisso. Si noti che l'aggiunta di un tipo di strumento finanziario richiede un sovraccarico da aggiungere all'interfaccia FinancialInstrumentCalculator
, che non è retrocompatibile: tutti i consumatori di tale interfaccia dovranno essere aggiornati per supportare tale metodo. Questo non è il caso se il nuovo strumento finanziario è un sottotipo di una classe esistente, in quanto può essere utilizzato il gestore esistente.
Se i nuovi strumenti finanziari si presentano più spesso delle nuove operazioni, l'utilizzo del modello di visitatori è probabilmente una cattiva idea. Nota che qualsiasi strategia tu usi per abbinare n
operazioni a m
strumenti finanziari, sarai sempre (sottotitoli ignorati per un momento) e finirai con n·m
metodi per codificare.
Se la lingua lo consente, consiglio vivamente di utilizzare tratti o costrutti simili per fornire valori predefiniti nell'interfaccia FinancialInstrumentCalculator
. Per esempio. quando ho tre tipi
class A implements FinancialInstrument
class B extends A
class C extends A
quindi i metodi visitatore per B
e C
possono essere implementati dal metodo visitor per A
per impostazione predefinita:
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); }
}