虚拟调用与类型检查的另一个示例
本文关键字:另一个 检查 调用 类型 虚拟 | 更新日期: 2023-09-27 18:35:27
>问题
我发誓,每次我脑子里都说我应该使用虚拟呼叫而不是类型检查(例如:
if (obj is Foo)
...
else if (obj is Bar)
...
。我想出了另一个我不知道如何实现前者的例子。
我正在通过串行端口实现数据包化协议。一些伪代码可以最好地解释这一点:
OnDataReceived:
RcvPacket p = RcvPacket.ReadPacket(comport); // Call factory method
if (p is RcvPacketFoo)
OnFoo();
if (p is RcvPacketBar)
OnBar();
OnFoo:
raise Foo event
OnBar:
raise Bar event
基本上,ReadPacket 是基类中的一个工厂方法,它确定接收的数据包的类型,并将缓冲区传递给正确的派生类型构造函数。在此之后,我需要引发一个事件,具体取决于数据包的类型。如何在不使用is
运算符的情况下执行此操作?我的方法合理/理智吗?
溶液
当然是访客模式!谢谢巴勃罗·罗密欧。
在本例中,我使控制器(即调用工厂方法)是访问者。我的结果:
public interface IPacketHandler {
void Handle(FooPacket p);
void Handle(BarPacket p);
}
public class Controller : IPacketHandler {
OnDataReceived() {
RcvPacket p = RcvPacket.ReadPacket(comport); // Call factory method
p.Handle(this); // *** Virtual Call: The first dispatch ***
}
// Note the use of explicit interface implementation here.
IPacketHandler.Handle(FooPacket p) {
OnFoo();
}
IPacketHandler.Handle(BarPacket p) {
OnBar();
}
}
public abstract class RcvPacket {
public static RcvPacket ReadPacket(...) { ... } // Factory method
public void Handle(IPacketHandler handler);
}
public class FooPacket : RcvPacket {
public override void Handle(IPacketHandler handler) {
handler.Handle(this); // *** Overloaded call: The second dispatch ***
}
}
public class BarPacket : RcvPacket {
public override void Handle(IPacketHandler handler) {
handler.Handle(this); // *** Overloaded call: The second dispatch ***
}
}
有趣的是,通过显式实现访问者接口,Handle
调用基本上是隐藏的。 从 MSDN:
实现接口的类可以显式实现该接口的成员。显式实现成员时,不能通过类实例访问它,而只能通过接口的实例访问它。
我能想到的唯一方法是将OnFoo
和OnBar
的实现移动到RcvPacketFoo
和RcvPacketBar
类。
public class RcvPacket{
public abstract void On(RcvPacketHandler eh);
}
public class RcvPacketFoo : RcvPacket
{
public override void On(RcvPacketHandler eh){eh.OnFoo();} //OnFoo implemenation
}
public class RcvPacketBar : RcvPacket
{
public override void On(RcvPacketHandler eh){eh.OnBar();} //OnBar implemenation
}
//Update following your comment:
public class RcvPacketHandler
{
public void OnFoo(){}
public void OnBar(){}
//....
OnDataReceived:
RcvPacket p = RcvPacket.ReadPacket(comport); // Call factory method
p.On(this);
好吧,考虑到您可能不希望每个数据包中包含太多额外的逻辑,您可以通过双重调度来完成它。
在这种情况下,我可能会创建一个接口,例如:
public interface IPacketEvents
{
void On(Foo foo);
void On(Bar bar);
}
我假设您有一个所有数据包的基类。我要在其中声明:
public abstract void RaiseUsing(IPacketEvents events);
包的每个子类将实现以下内容:
public override void RaiseUsing(IPacketEvents events)
{
events.On(this);
}
您可以让一个新类实现IPacketEvents
,或者您调用工厂的同一类可以实现它。在第二种情况下,您的呼叫最终将变为:
OnDataReceived:
RcvPacket p = RcvPacket.ReadPacket(comport); // Call factory method
p.RaiseUsing(this);
使用这种类型的调度,你得到的是,每种类型都调用相应的方法,因为它"知道"要调用哪一种。我对所有方法使用相同的"On"名称可能会有点混淆,但这并不完全必要。它们可以是 OnFoo() 和 OnBar()。
这种类型的行为也用于访客模式。