【问题标题】:dispatch design pattern?调度设计模式?
【发布时间】:2021-08-31 06:06:18
【问题描述】:

假设我在 Java 中有一个类层次结构:

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

我在同一个包中有另一个类:

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

我想为每种项目类型做不同的事情,但我不想在不同的Item 类(MusicBoxTypeWriterSoccerBall)中定义该操作。

一种处理方法是:

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 那么我可以直接调用该项目的方法而不关心它是什么类型,但是如果我不希望在项目本身中出现这种差异,我该如何处理?)

【问题讨论】:

  • 不是我知道的答案,但在 ActionScript 3 中,显然你可以从字符串实例化一个类(即;'com.djw.MusicBox' 可以实例化一个 MusicBox),这种事情可能在也许是Java?只是一个建议!
  • @dan:Class#forName()。然而,我质疑这里的价值。

标签: java design-patterns dispatch


【解决方案1】:

在Java 中,您可以使用visitor(-like) 模式进行多次调度。 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
}

【讨论】:

  • 嗯。给了我一些想法,但在你的代码中,接口 Item 现在依赖于 ItemProcessor,它依赖于 MusicBox 和 SoccerBall 类(所以不确定这是否会编译)
  • 它会编译,你可以在Java中的类之间有交叉依赖。但是,你不能在 .jars 之间有 corss-dependencies,所以如果 Item 和 ItemProcessor 在不同的 .jars 中,它将不起作用。
  • @Jason: 是的,它可以编译 :) 这只是一种翻转事物的方式,这样您就可以拥有不同的方法来试图弄清楚如何处理特定类型的 Item Items 的类型将它们自己传递给一个有它们处理的方法。编译时耦合可能是一个好处,如果你在应用程序周围的很多地方都有那个 if-else instanceof 块,那么当你创建一个新的项目类型时错过更新一个比当它们都实现一个接口时要容易得多改变了。
【解决方案2】:

我想我会使用控制反转的想法和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);
      }
   }
}

然后执行以下操作,这至少将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 */
    }
 }

【讨论】:

    【解决方案3】:

    为什么不给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()
      }
    }
    

    然后,在包含 SpecialItemProcessor 的 Client 类中可以调用该方法,例如:

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

    实际上,在 C++ 中,这是动态绑定。这就是为什么存在纯抽象类...

    【讨论】:

    • 阅读问题:“我想为每种项目类型做不同的事情,但我不想在不同的项目类中定义该操作(MusicBox,打字机,足球)。”
    【解决方案4】:

    您可以为 Item 创建一个桥接模式,其中另一端是调用 add() 时要执行的关联进程。您还可以添加工厂方法。

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

    希望这会有所帮助。

    【讨论】:

    • 那是什么语法?而且,在最初分配给它之前,你如何使用p
    • 这不是 PHP... 正确版本:p.doWhenAddin()
    • 对不起,我终于投入了一些 C++ 语法。这个源代码是Java。创建对应处理器方法是一个工厂方法,它根据处理器的类层次结构创建相应的类,链接到Items的类层次结构。
    猜你喜欢
    • 2011-09-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-24
    • 2016-10-11
    相关资源
    最近更新 更多