шаблон отправки дизайн? - PullRequest
       3

шаблон отправки дизайн?

5 голосов
/ 09 сентября 2010

Предположим, у меня есть иерархия классов в Java:

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

, и у меня есть другой класс в том же пакете:

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

, где я хочу сделать что-то другое для каждого элементатипа, но я не хочу определять это действие в различных Item классах (MusicBox, TypeWriter, SoccerBall).

Один из способов справиться с этим:

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 ...
        }
    }
}

Это работает, но кажется неуклюжим. Есть ли лучший способ сделать это, когда я знаю об особых случаях? (очевидно, если Item содержит метод doSomethingSpecial, тогда я могу просто вызвать метод этого элемента, не заботясь о его типе, ноесли я не хочу, чтобы это различие происходило внутри самого предмета, как мне с ним бороться?)

Ответы [ 4 ]

7 голосов
/ 09 сентября 2010

В Java вы можете сделать многократную рассылку с шаблоном, похожим на посетителя. Реализации Item не должны содержать логику обработки, им просто нужен метод типа accept().

public interface Item {
/** stuff **/

void processMe(ItemProcessor processor);

}

public interface ItemProcessor {

void process(MusicBox box);

void process(SoccerBall ball);

//etc

}

public class MusicBox implements Item {

  @Override
  public void processMe(ItemProcessor processor) {
    processor.process(this);
  }

}

public class ItemAddingProcessor implements ItemProcessor {

  public void add(Item item) {
    item.processMe(this);
  }

  @Override
  public void process(MusicBox box) {
    //code for handling MusicBoxes
    //what would have been inside if (item instanceof MusicBox) {}
  }

//etc
}
2 голосов
/ 23 сентября 2010

Я думаю, что собираюсь использовать идею инверсии управления и шаблон посетителя :

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);
      }
   }
}

, а затем сделать следующее, что по крайней мере уменьшает instanceof один чек за add() звонок:

 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 */
    }
 }
0 голосов
/ 01 августа 2014

Почему бы не определить некоторую функцию обратного вызова для интерфейса Item?

public Interface Item {
  void onCallBack();
}

Затем в каждом классе, который реализует Item, например MusicBox, он должен реализовать функцию обратного вызова.

public class MusicBox {
  @override
  public void onCallBack() {
    // business logic
    ...
    ...  
  }
}

Тогда вы можете создать диспетчер, которого вы называете "SpecialItemProcessor".

public SpecialItemProcessor {
  private final Item _item;

  public SpecialItemProcessor(Item item) {
    _item = item;
  }

  public dispatch() {
    _item.onCallBack()
  }
}

И тогда в классе Client, который содержит SpecialItemProcessor, можно просто вызвать метод, например:

public void XXXX() {
  ....
  SpecialItemProcessor specialItemProcessor = new SpecialItemProcessor(new MusicBox());
  specialItemProcessor.dispatch();
  ....
}

На самом деле, в C ++ это динамическое связывание. И именно поэтому существует чистый абстрактный класс ...

0 голосов
/ 09 сентября 2010

Вы можете создать шаблон моста для Item, в котором другая сторона была связана с процессами, вызываемыми при вызове add ().Вы также можете добавить фабричный метод к миксу.

class SpecialItemProcessor {
  public void add(Item item)
  {
     Process p = Item.createCorrespondingProcessor( p );
     p.doWhenAddin();
  }
}

Надеюсь, это поможет.

...