Visitor Pattern

Source: comp.object.moderated
Date: 03-Jun-99

Related Sites


------------------------------

o-< Problem: Sometimes it seems you just have to use switch statements and downcasts. What is an OOPer to do?


---------------

o-< Marcus Edwards presented the dilemma:

[A transaction object is provided to the client and...] the client must now select the right UI classes and pass the newly created transaction class to it. This is a relatively simple process of checking the class type information and using a switch or if statement to select the right UI class but I'm almost positive there is a better way.

The second problem arises from the fact that the base class reference must be down cast (i.e. cast to a subclass) to allow access to the subclass attributes and behaviour which seems inelegant at best.


---------------

o-< Robert C. Martin replied:

[...] You can make use dual dispatch to create the UI.
[dual or double dispatch means a function call that depends on two of its arguments. OO usually supports single dispatch - the call is dynamically dispatched based on the object's type, but not on the type of any of the arguments. -YS]

I presume that if you know the type of transation created, then you can determine the kind of UI to pass it to. You could have the derived Transaction select the UI, but I imagine you don't want the transactions knowing anything about the UI's. Rather you could use the Visitor pattern (i.e. dual dispatch)

package Transactions;

public class Transaction
{
  public abstract void accept(TransactionVisitor v);
}

interface TransactionVisitor
{
  public void visit(DepositTransaction dt);
  public void visit(WithdrawlTransaction wt);
}

class DepositTransaction extends Transaction
{
  public void accept(TransactionVisitor v)
  {v.visit(this);}
}

class WithdrawlTransaction extends Transaction
{
  public void accept(TransactionVisitor v)
  {v.visit(this);}
}

class TransactionUIFactory implements TransactionVisitor
{
  private UI itsUI;
  public UI getUI() {return itsUI;}
  public void visit(DepositTransaction dt) {
    itsUI = new DepositUI(dt);
  }
  public void visit(WithdrawlTransaction wt) {
    itsUI = new WithdrawlUI(wt);
  }
}
[...]

If you use the visitor pattern as described above, and if you pass the transaction into the UI constructor, then you won't need the downcast. The visitor will have already resolved the type of the transaction for you...

[So the client would write something like this:

TransactionUIFactory uiFactory = new TransactionUIFactory;
transaction.accept(uiFactory);
uiFactory.getUI().show();
-YS]


------------------------------

o-< More Info:

Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides,
Design Patterns -- Elements of Reusable Object-Oriented Software

Excerpt from Design Pattens, The Visitor Pattern

Vince Huston, A description of the Visitor pattern

Robert C. Martin, Acyclic Visitor

Martin E. Nordberg III, Variations on the Visitor Pattern


------------------------------