Creazione oggetto basata sul tipo dalla gerarchia parallela senza instanceof (Java)

3

Ho due gerarchie parallele:

  • B e C implementano l'interfaccia A
  • D ed E estendono la classe astratta F

Voglio essere in grado di fare: F f = createObject(A a)

dove createObject dovrebbe seguire il comportamento:

F f createObject(A a){
    if (a instanceof B)
       return new D(a)
    else if (a instanceof C)
       return new E(a)
    else
        throw some error

Cioè, la creazione di F dipende dal tipo di A.

Non riesco a rifare il codice per rimuovere questa dipendenza dalla gerarchia parallela. Ho cercato di trovare una soluzione pulita a questo problema che non coinvolge instanceof, ma non sono stato in grado di.

Ogni suggerimento è stato apprezzato.

EDIT:

B e C non dovrebbero avere conoscenza di D ed E, preferibilmente, poiché la relazione è che D ed E hanno-un A, e A esiste indipendentemente da D ed E.

    
posta andfor 18.04.2018 - 09:05
fonte

1 risposta

1

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.

    
risposta data 18.04.2018 - 10:32
fonte

Leggi altre domande sui tag