情境二:當 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 卡死的現象。