Utilizzo del modello di visitatore con una gerarchia di oggetti di grandi dimensioni

10

Contesto

Ho usato con una gerarchia di oggetti (un albero di espressioni) un pattern di visitatore "pseudo" (pseudo, in quanto non usa la doppia distribuzione):

 public interface MyInterface
 {
      void Accept(SomeClass operationClass);
 }

 public class MyImpl : MyInterface 
 {
      public void Accept(SomeClass operationClass)
      {   
           operationClass.DoSomething();
           operationClass.DoSomethingElse();
           // ... and so on ...
      }
 }

Questo design è stato, tuttavia, discutibile, piuttosto constrongvole poiché il numero di implementazioni di MyInterface è significativo (circa 50 o più) e non ho bisogno di aggiungere operazioni aggiuntive.

Ogni implementazione è unica (è un'espressione o operatore diverso) e alcuni sono compositi (cioè nodi dell'operatore che conterranno altri nodi operatore / foglia).

L'attraversamento viene attualmente eseguito chiamando l'operazione Accept sul nodo radice dell'albero, che a sua volta chiama Accept su ciascuno dei suoi nodi figli, che a sua volta ... e così via ...

Ma è giunto il momento in cui ho bisogno di aggiungere una nuova operazione , ad esempio una bella stampa:

 public class MyImpl : MyInterface 
 {
      // Property does not come from MyInterface
      public string SomeProperty { get; set; }

      public void Accept(SomeClass operationClass)
      {   
           operationClass.DoSomething();
           operationClass.DoSomethingElse();
           // ... and so on ...
      }

      public void Accept(SomePrettyPrinter printer)
      {
           printer.PrettyPrint(this.SomeProperty);
      }
 }    

In pratica vedo due opzioni:

  • Mantieni lo stesso design, aggiungendo un nuovo metodo per la mia operazione a ogni classe derivata, a scapito della manutenibilità (non un'opzione, IMHO)
  • Utilizza il pattern "true" Visitor, a scapito dell'estensibilità (non un'opzione, poiché mi aspetto di avere più implementazioni lungo la strada ...), con circa 50+ overload del metodo Visit, ognuno corrispondente un'implementazione specifica?

Domanda

Consiglieresti di utilizzare il pattern Visitor? C'è qualche altro modello che potrebbe aiutare a risolvere questo problema?

    
posta T. Fabre 06.06.2012 - 13:11
fonte

1 risposta

11

Ho utilizzato il modello di visitatore per rappresentare alberi di espressione nel corso di oltre 10 anni su sei progetti su larga scala in tre linguaggi di programmazione e sono molto soddisfatto del risultato. Ho trovato un paio di cose che hanno reso molto più semplice l'applicazione del pattern:

Non utilizzare sovraccarichi nell'interfaccia del visitatore

Inserisci il tipo nel nome del metodo, quindi usa

IExpressionVisitor {
    void VisitPrimitive(IPrimitiveExpression expr);
    void VisitComposite(ICompositeExpression expr);
}

anziché

IExpressionVisitor {
    void Visit(IPrimitiveExpression expr);
    void Visit(ICompositeExpression expr);
}

Aggiungi un metodo "catch unknown" all'interfaccia del visitatore.

Renderebbe possibile agli utenti che non possono modificare il tuo codice:

IExpressionVisitor {
    void VisitPrimitive(IPrimitiveExpression expr);
    void VisitComposite(ICompositeExpression expr);
    void VisitExpression(IExpression expr);
};

Ciò consentirebbe loro di costruire le proprie implementazioni di IExpression e IVisitor che "capiscono" le loro espressioni utilizzando le informazioni sul tipo di esecuzione nell'implementazione del loro metodo catch-all VisitExpression .

Fornisci un'implementazione predefinita nulla-fare dell'interfaccia IVisitor

Ciò consentirebbe agli utenti che hanno bisogno di trattare un sottoinsieme di tipi di espressioni di costruire i propri visitatori più velocemente e rendere il loro codice immune a te aggiungendo più metodi a IVisitor . Ad esempio, scrivere un visitatore che raccolga tutti i nomi di variabili dalle tue espressioni diventa un compito facile e il codice non si interromperà nemmeno se aggiungerai un gruppo di nuovi tipi di espressioni al tuo IVisitor in seguito.

    
risposta data 06.06.2012 - 15:45
fonte

Leggi altre domande sui tag