簡體   English   中英

虛擬通話與類型檢查的另一個例子

[英]Another example of virtual calls vs. type-checking

問題

我發誓,每當我進入大腦時,我都應該使用虛擬調用與類型檢查(例如:

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運算符怎么辦? 我的方法是否健全/健全?


訪客模式 ,當然! 謝謝Pablo Romeo

在這種情況下,我使控制器(即調用工廠方法)成為訪問者。 我的結果:

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

有趣的是,通過顯式實現visitor接口, Handle調用實際上是隱藏的。 MSDN

實現接口的類可以顯式實現該接口的成員。 當成員被顯式實現時,不能通過類實例進行訪問,而只能通過接口的實例進行訪問。

我能想到的唯一方法是將OnFooOnBar的實現OnFooRcvPacketFooRcvPacketBar類。

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

好吧,考慮到您可能不想在每個數據包中添加太多邏輯,可以通過Double Dispatch完成它。

在這種情況下,我可能會創建一個接口,例如:

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()。

訪客模式中也使用了這種類型的行為。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM