向下转换或丢弃多态性 - 较小的邪恶是什么?
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真的很重要。它确保您没有为错误类型的消息添加处理程序。
我知道它在最后一行破坏了泛型,但它看起来很安全,因为我们保证不会放置和使用错误的处理程序。
所以假设我们有一个基地 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真的很重要。它确保您没有为错误类型的消息添加处理程序。
我知道它在最后一行破坏了泛型,但它看起来很安全,因为我们保证不会放置和使用错误的处理程序。