情境二:當 Queue 中存在不同類型的資料,要如何將其分派到對應的 Consumer。
關於這個情境,可以分為兩個的處理:
Dispatch 如何識別異質性資料,並正確分派到對應的 Consumer。
首先,異質性資料如何儲存在一個 Queue 之中,己經在前一章的通用型的 Router 提到相關的概念。
來回憶一下前面在實作 Router 時,針對 C# 的 Value Type 資料的迭代過程嗎?
Type 來辨識不同類型的資料,分別放到專屬的 Queue;object 的 Boxing/Unboxing 的機制,建立通用的 Queue。Label來進行資料類型的分類,以 byte[] 來紀錄資料本身的內容。所以,將 Queue 的資料類型,由 byte[] 變更為 QueueItem ,就直接運用 QueueItem ,達到同一個 Queue 儲存不同 value Type 或不同 Reference Type 的資料。
但筆者覺得可以額外提一下,對於有繼承關係的 Reference Type ,可以利用 里氐替換原則 將其儲存在同一個 Queue 之中。
而 Reference type 的資料的識別方式,可分兩種;本身存在識別碼 或 Reference Type 可被識別。
internal class CircleShape : ShapeBase
{
public override string Type => "circle";
}
internal class RectShape : ShapeBase
{
public override string Type => "Rect";
}
internal abstract class ShapeBase
{
// 識別碼
public abstract string Type { get; }
}
static void Main(string[] args)
{
var queue = new ConcurrentQueue<ShapeBase>();
queue.Enqueue(new RectShape());
queue.Enqueue(new CircleShape());
while (!queue.IsEmpty)
{
queue.TryDequeue(out var shape);
// 方式一: Reference Type 可被識別
Console.WriteLine($"Dequeue GetType(): {shape.GetType()}");
// 方式二: 利用本身的識別碼
Console.WriteLine($"Dequeue Type: {shape.Type}");
}
}
當傳入 Queue 的資料具有共同的 interface/abstruct 的情境,可以使用上面的寫法來處理。
但異質性資料使用的情況,相當高機率是用在不同類別資料,所以依然採用 QueueItem 的方式,來儲存異質性資料。
接著,在實作異質性資料,經由 Dispatch 分派到正確的 consumer,會遇到兩個問題。
Dispatch 請求資料,Dispatch 如何得知 Consumer 要取什麼類型的資料。Dispatch 主動通知 Consumer 時,Dispatch 要如何 Consumer 接受何種類型的資料。要特別注意,不管是那一種分派方式,當 Queue 存有異質性資料,而只有其中一種 consumer 來取得資料時,因為 FIFO 的特性,會發生 Queue 卡死的現象。