Search code examples
javadesign-patternsdispatch

dispatch design pattern?


Suppose I have a class hierarchy in Java:

interface Item { ... };
class MusicBox implements Item { ... };
class TypeWriter implements Item { ... };
class SoccerBall implements Item { ... };

and I have another class in the same package:

class SpecialItemProcessor {
    public void add(Item item)
    {
        /* X */
    }
}

where I want to do something different for each item type, but I don't want to define that action in the different Item classes (MusicBox, TypeWriter, SoccerBall).

One way to handle this is:

class SpecialItemProcessor {
    public void add(Item item)
    {
        if (item instanceof MusicBox)
        {
            MusicBox musicbox = (MusicBox)item;
            ... do something ...
        }
        else if (item instanceof MusicBox)
        {
            TypeWriter typewriter = (TypeWriter)item;
            ... do something ...
        }
        else if (item instanceof SoccerBall)
        {
            SoccerBall soccerball = (SoccerBall)item;
            ... do something ...
        }
        else
        {
            ... do something by default ...
        }
    }
}

This works but it seems really clunky. Is there a better way to do this, when I know of special cases? (obviously if Item contains a method doSomethingSpecial then I can just call that item's method without caring what type it is, but if I don't want that differentiation to occur within the item itself how do I deal with it?)


Solution

  • I think I'm going to use the idea of inversion of control and the visitor pattern:

    interface Item { 
       public void accept(Visitor visitor);
       ... 
    
       public interface Visitor {
          public void visit(Item item);
       }
    }
    
    
    class MusicBox implements Item { 
       public interface Visitor extends Item.Visitor {
          public void visitMusicBox(MusicBox item);
       }
       ... 
       @Override public accept(Item.Visitor visitor)
       {
          if (visitor instanceof MusicBox.Visitor)
          {
              ((MusicBox.Visitor)visitor).visitMusicBox(this);
          }
       }
    }
    
    class TypeWriter implements Item { 
       public interface Visitor extends Item.Visitor {
          public void visitTypeWriter(TypeWriter item);
       }
       ... 
       @Override public accept(Item.Visitor visitor)
       {
          if (visitor instanceof TypeWriter.Visitor)
          {
              ((TypeWriter.Visitor)visitor).visitTypeWriter(this);
          }
       }
    }
    
    class SoccerBall implements Item { 
       public interface Visitor extends Item.Visitorr {
          public void visitSoccerBall(SoccerBall item);
       }
       ... 
       @Override public accept(Item.Visitor visitor)
       {
          if (visitor instanceof SoccerBall.Visitor)
          {
              ((SoccerBall.Visitor)visitor).visitSoccerBall(this);
          }
       }
    }
    

    and then do the following, which at least reduces the instanceof to one check per add() call:

     class SpecialItemProcessor 
        implements 
           MusicBox.Visitor, 
           TypeWriter.Visitor, 
           SoccerBall.Visitor, 
           Item.Visitor
     {
        public void add(Item item)
        {
            item.accept(this);
        }
        @Override public void visitMusicBox(MusicBox item)
        {
            ...
        }
        @Override public void visitTypeWriter(TypeWriter item)
        {
            ...
        }
        @Override public void visitSoccerBall(SoccerBall item)
        {
            ...
        }
        @Override public void visit(Item item)
        {
            /* not sure what if anything I should do here */
        }
     }