倾斜或抛弃多态 - 什么是较小的邪恶?

时间:2017-09-08 17:17:31

标签: java polymorphism downcast

假设我们有一个基类消息:

public abstract Class Message {

    Object content;

    public Message(Object content) {
        this.content = content;
    }
}

各种实施:

public Class Packet extends Message {

    public Packet(Long largeNumber) {
        super(largeNumber);
    }

    public Long unpack() {
        return (Long) content;
    }
}

public Class Letter extends Message {

    public Letter(Short smallNumber) {
        super(smallNumber);
    }

    public Short unpack() {
        return (Short) content;
    }
}

现在假设我们有一个发送者类,它会在某处发送消息。

public Class Sender {

    public send(Message msg) {
        // send it somewhere
    }
}

接收器类,接收消息:

public Class Receiver {

    receive(Message msg) {
       // do something with the msg 
   }
}

然而,接收器类只获取超级类消息并且事先不知道它将接收哪个子类。那么我现在如何解开"消息?

如果我们假设我确切知道哪些消息会落在哪里,我可以像这样使用向下转发:

Packet packet = (Packet) msg;

但不知何故,这种感觉是错误的,因为它首先解释了多态性的观点。发送绝对子消息会更好吗? 或者是否有解决方案我不会看到这样的问题(例如在某些变体中使用泛型 - 我对它们不太熟悉)?

4 个答案:

答案 0 :(得分:3)

可以使用visitor pattern

通过邮件本身解压缩邮件
public abstract Class Message {
    void send(Receiver r) {
        r.receive(this); // Catch-all
    }
}

public Class Packet extends Message {
    void send(Receiver r) {
        r.receive(this); // Overload for packets
    }
}

public Class Letter extends Message {
    void send(Receiver r) {
        r.receive(this); // Overload for letters
    }
}

public Class Receiver {
    // There is an overload for each subclass
    receive(Packet packet) {
    }
    receive(Letter letter) {
    }
    // This is the catch-all implementation
    receive(Message msg) {
    }
}

这种方法允许接收器在静态类型的上下文中单独处理字母和数据包。 Catch-all实现通常用于错误报告。

答案 1 :(得分:1)

可行的方法是泛型:

public abstract class Message<T> {

    private T content;

    public T unpack() {
        return content;
    }

}

答案 2 :(得分:0)

如果要使用polimorfism属性,则必须在父类Message中声明抽象方法unpack,然后在派生类中覆盖它:

    public abstract class Message {
     public abstract String unpack();
     // other code
}
public class Letter extends Message {
    public String unpack() {
        // your unpacked code
    }
}
public class Receiver{
    receive(Message msg){
        String s = msg.unpack(); 
        /* you can invoke unpack method because earlier declared it as abstract in parent class */
    }
}

答案 3 :(得分:0)

我建议为每种类型的Message创建一个处理程序映射。 消息的内容不应该丢失其类型。消息和信件的例子:

abstract class Message<T> {
    private T content;
    protected Message(T content) {
        this.content = content;
    }
    public T getContent() {
        return content;
    }
}

class Letter extends Message<Short> {
    public Letter(Short number) {
        super(number);
    }
}

然后为处理程序创建一个接口:

interface MessageHandler<T extends Message> {
    void handle(T message);
}

然后创建一些“提供者”来存储所有处理程序:

class MessageHandlersProvider {
    Map<Class<? extends Message>, MessageHandler<? extends Message>> handlers = new HashMap<>();
    <T extends Message> void addHandler(Class<T> messageType, MessageHandler<T> handler) {
        handlers.put(messageType, handler);
    }

    MessageHandler<?> getHandler(Class<? extends Message> messageType) {
        return handlers.get(messageType);
    }
}

处理程序的示例:

class LetterHandler implements MessageHandler<Letter> {
    @Override
    public void handle(Letter letter) {
        //Short number = letter.getContent();
        //do anything
    }
}

现在您可以创建一个提供程序并由处理程序以这种方式填充它:

MessageHandlersProvider handlersProvider = new MessageHandlersProvider();
handlersProvider.addHandler(Letter.class, new LetterHandler());

然后获取处理程序并处理任何类型的消息:

MessageHandler<?> handler = handlersProvider.getHandler(message.getClass());
handler.handle(message);

通过通用方法addHandler填充地图非常重要。它确保您没有为错误类型的Message添加处理程序。

我知道它在最后一行打破了泛型,但它似乎非常安全,因为我们保证不会放置和使用错误的处理程序。