向下转换或丢弃多态性 - 较小的邪恶是什么?

Downcasting or discard polymorphism - what's the smaller evil?

所以假设我们有一个基地 class 消息:

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

现在假设我们有一个发件人 class,它将消息发送到某处。

public Class Sender {

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

以及接收消息的接收者 class:

public Class Receiver {

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

接收者 class 然而只是收到超级 class 消息并且事先不知道它会收到哪个子class。那么我现在如何 "unpack" 消息?

如果我们假设我确切地知道什么消息会到达哪里,我可以像这样使用向下转型:

Packet packet = (Packet) msg;

但不知何故,这让人感觉不对,因为它从一开始就忽略了多态性的意义。只发送绝对子消息会更好吗? 或者有没有解决我看不到的问题的方法(例如,在某些变体中使用泛型——我不太熟悉它们)?

要走的路是泛型:

public abstract class Message<T> {

    private T content;

    public T unpack() {
        return content;
    }

}

解包邮件可以由邮件本身完成,使用 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 实现通常用于错误报告。

如果你想使用 polimorfism 属性,你必须在父 class 消息中声明抽象方法解包,然后在派生中覆盖它 class:

    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 */
    }
}

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

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

然后创建一些"provider"来存储所有处理程序:

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来填充map真的很重要。它确保您没有为错误类型的消息添加处理程序。

我知道它在最后一行破坏了泛型,但它看起来很安全,因为我们保证不会放置和使用错误的处理程序。