有条件的多态 - 一个更复杂的情况

时间:2015-09-23 12:11:13

标签: design-patterns polymorphism refactoring

说我知道这个example

   double getSpeed() {
       switch (_type) {
           case EUROPEAN:
              return getBaseSpeed();
           case AFRICAN:
              return getBaseSpeed() - getLoadFactor() * _numberOfCoconuts;
           case NORWEGIAN_BLUE:
              return (_isNailed) ? 0 : getBaseSpeed(_voltage);
       }
       throw new RuntimeException ("Should be unreachable");
   }

显然,我会重构为子类,一切都将在世界上再次正确。但是,如果我有:

   double getSpeed() {
       switch (_type) {
           case EUROPEAN:
              inform_gary(_count);  
              return getBaseSpeed();
           case AFRICAN:
              increment_package_counter();
              transmit_coordinates(_coordinates);
              return getBaseSpeed() - getLoadFactor() * _numberOfCoconuts;
           case NORWEGIAN_BLUE:              
              return (_isNailed) ? 0 : getBaseSpeed(_voltage);
       }
       throw new RuntimeException ("Should be unreachable");
   }

现在它对子类没用,因为那时我必须将子类紧密地耦合到他们不应该知道的代码上。这个问题有解决方案吗?

1 个答案:

答案 0 :(得分:1)

问题的评论稍微改进了一下这个问题;我将回答更新的版本。

我会为每种可能的消息类型创建一个类。这些将继承具有execute函数的消息接口。

通常,消息需要执行一些需要引用执行环境的操作。由于消息对象不可变时生命更容易,我会将这些引用作为参数传递给execute函数。

界面:

interface Message
{
    void execute(Context context)
}

示例消息:

class RebroadcastMessage : Message
{
    private final String content;
    private final int timeToLive;

    public RebroadcastMessage(String content, int timeToLive) {

        this.content = content;
        this.timeToLive = timeToLive;
    }

    void execute(Context context)
    {
        if (timeToLive > 0)
        {
            foreach (Peer p in context.Peers)
            {
                context.send(p, new RebroadcastMessage(content, timeToLive - 1));
            }
        }
    }
}

这使得处理消息变得容易:

messageQueue.take().execute(this.context); 

根据下面的讨论,我将解释另一种技术。

有时您希望在消息之间处理状态,但不希望将此逻辑放在消息接收类中。在这些情况下,我建议采用事件驱动设计。

A Session接收消息(例如来自网络连接)并更新收到的每条消息的所有侦听器。

interface MessageListener 
{
    void handle(Context context, Message message);
}

interface Session
{
    void addListener(MessageListener listener);
}

然后,您可以为所需的每项功能单独实施MessageListener

class FruitTracker implements MessageListener 
{
    private Set<Fruit> stock;

    public FruitTracker()
    {
        stock = new HashSet<>();
    }

    void handle(Context context, Message message)
    {
        if (message instanceof StockQueryMessage)
        {
            context.send(context.getSender(), new StockQuoteMessage(stock));
        } 
        else if (message instanceof StockUpdateMessage)
        {
            stock = ((StockUpdate)message).getStock();
        }
    }
}

设置:

val fruitTracker = new FruitTracker();

session.addListener(fruitTracker);