Ho faticato con questo problema me stesso . Sembra che l'unica soluzione ragionevole sia utilizzare il pattern Visitor. Questo evita instanceof
, e anche la necessità di rendere l'istanziazione la responsabilità del tipo di origine A
. Tuttavia, la gerarchia dei tipi di origine dovrà essere aggiornata per supportare l'infrastruttura dei visitatori.
Innanzitutto, un'interfaccia Visitor:
interface AVisitor<Result> {
Result visitB(B object);
Result visitC(C object);
}
Successivamente, dobbiamo aggiornare la gerarchia A per supportare il visitatore:
interface A {
<Result> Result accept(AVisitor<Result> visitor);
...
}
class B implements A {
@Override
public <Result> Result acccept(AVisitor<Result> visitor) {
return visitor.visitB(this);
}
...
}
class C implements A {
@Override
public <Result> Result acccept(AVisitor<Result> visitor) {
return visitor.visitC(this);
}
...
}
Ora possiamo implementare un visitatore che gestisce la creazione di oggetti dalla gerarchia F:
class CreateFFromA implements AVisitor<F> {
@Override
public F visitB(B b) { return new D(B); }
@Override
public F visitC(C c) { return new E(c); }
}
Infine, possiamo legarlo insieme in una funzione di supporto statico:
static F createObjectA(A a) {
return a.accept(new CreateFFromA());
}
Questo è un sacco di codice e complessità per risolvere questo problema. Un instanceof
è molto più conveniente. Quindi una soluzione basata sui visitatori è particolarmente applicabile quando
- le due gerarchie di classi devono essere disaccoppiate a tutti i costi,
- o se esistono più gerarchie di tipi di output, che si desidera selezionare in fase di runtime,
- o se vuoi essere in grado di controllare staticamente che la funzione gestisca tutte le sottoclassi di A conosciute.
Esiste anche una restrizione sostanziale: non è possibile aggiungere nuove classi alla gerarchia A con la soluzione visitor o instanceof-based. Dovresti aggiungere il nuovo tipo all'interfaccia del visitatore e quindi aggiornare tutte le implementazioni dei visitatori per gestire tale caso. Se hai bisogno di questo tipo di flessibilità, potresti dover accettare alcuni accoppiamenti e utilizzare un normale metodo di istanza A.create()
che le sottoclassi possono eseguire l'override.