Come risolvere le dipendenze cicliche in uno schema di visitatore

4

Quando programmiamo sul posto di lavoro, ora affrontiamo un problema con i visitatori e le dipendenze tra moduli e progetti.

Supponiamo di avere una classe A in un modulo X. E ci sono sottoclassi B e C nel modulo Y. Ciò significa che il modulo Y dipende dal modulo X. Se vogliamo implementare un modello di visitatore nella gerarchia di classi, quindi introducendo un'interfaccia con l'handle Operazioni e un metodo di accettazione astratto in A, otteniamo una dipendenza dal modulo Y al modulo X, che non possiamo consentire per ragioni architettoniche.

Quello che facciamo è usare un confronto diretto tra i tipi (cioè instanceof , dato che programmiamo in Java), il che non è soddisfacente.

Le mie domande dovrebbero essere: incontri questo tipo di problema nel tuo lavoro quotidiano (o facciamo scelte architettoniche scadenti) e, in caso affermativo, qual è il tuo approccio per risolvere questo problema?

Ecco un esempio minimale in Java per illustrare il mio punto. Il pacchetto a ha ClassA e l'interfaccia Visitor sulla gerarchia ClassA:

package pkg.a;  
public abstract ClassA extends ClassA {  
    public abstract void accept(ClassAVisitor visitor);  
   /* other methods ... */   
} 

package pkg.a;  
import pkg.b.ClassB;
import pkg.b.ClassC;
public interface ClassAVisitor {  
    public abstract void handle(ClassB visitee);
    public abstract void handle(ClassC visitee);  
} 

Il pacchetto b ha le classi concrete che si estendono da ClassA:

package pkg.b;  
import pkg.a.ClassAVisitor;
public ClassB extends ClassA {  
    public void accept(ClassAVisitor visitor) {  
        visitor.handle(this);  
    }   
} 

package pkg.b;
import pkg.a.ClassAVisitor;  
public ClassC {  
    public void accept(ClassAVisitor visitor) {  
        visitor.handle(this);  
    }   
}

I pacchetti aeb hanno una dipendenza ciclica.

    
posta Benni 31.10.2012 - 10:09
fonte

4 risposte

1

Che ne dici di cambiare l'interfaccia del visitatore per rimuovere la dipendenza da a a b ?

package pkg.a;  
public abstract ClassA extends ClassA {  
    public abstract void accept(ClassAVisitor visitor);  
   /* other methods ... */   
} 

package pkg.a;  
public interface ClassAVisitor {  
    public abstract void handleB(ClassA visitee);
    public abstract void handleC(ClassA visitee);  
} 

e poi

package pkg.b;
import pkg.a.ClassAVisitor;
public ClassB extends ClassA {
    public void accept(ClassAVisitor visitor) {
        visitor.handleB(this);
    }
}

package pkg.b;

import pkg.a.ClassAVisitor;
public ClassC {
    public void accept(ClassAVisitor visitor) {
        visitor.handleC(this);
    }
}
    
risposta data 15.11.2012 - 15:53
fonte
4

Devi essere consapevole del fatto che, implementando il visitatore, stai limitando severamente te stesso in come e dove puoi creare una sottoclasse di root della ricerca. I tuoi problemi sono i risultati di tali limitazioni.

Forse potresti creare classi astratte BBase e CBase nel modulo X e derivare classi B e C da quelle. Quindi l'intera ricerca dei visitatori può essere nel modulo X e il modulo Y avrà solo un'implementazione concreta delle classi B e C. Ma questa soluzione è anche limitata, perché il tuo visitatore non può lavorare con nessun tipo, a cui si fa riferimento in Y ma non in X.

    
risposta data 31.10.2012 - 11:50
fonte
0

Questo mi sembra piuttosto semplice. Hai tre modi per evitare la dipendenza circolare. È possibile unire A e B in un unico pacchetto. Puoi spostare il visitatore nel pacchetto B. Puoi spostare il visitatore in un altro pacchetto V che dipende da A e B.

La mia scelta sarebbe quella di unire i pacchetti, o non usare il modello di visitatore. Altrimenti ogni volta che qualcuno crea una nuova sottoclasse di A, dovrà aggiornare il visitatore in qualche altro file sorgente molto, molto lontano. È fin troppo probabile che qualcuno si dimenticherà di farlo.

    
risposta data 31.10.2012 - 18:44
fonte
0

Puoi usare qualcosa come:

public static class X {
    public void accept(Visitor v){
        if (v instanceof XVisitor){
            ((XVisitor)v).visitX(this);
        }
    }
}
public static interface Acceptor{
    public void accept(Visitor v);        
}
public static interface Visitor{
}
public static interface XVisitor extends Visitor {
    public void visitX(X x);
}

In questo modo si elimina la dipendenza ciclica, ma si introduce usando instanceof. Ma non hai catena se ifs usa instanceof e hai l'istanza di "brutto" contenuta in poche classi, ma puoi aggiungere sia visitatori che accettori (o visite?)

PS: nel mio esempio, puoi sbarazzarti dell'interfaccia di Visitor, ma per facilitarne il pensiero l'ho messo lì. E potresti accettare e visitare i metodi restituire qualcosa, ma questo è al di fuori dell'ambito di questa idea. PS2: Questa idea non è mia, ma non ricordo dove l'ho vista.

    
risposta data 15.11.2012 - 14:40
fonte

Leggi altre domande sui tag