iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 11
1
Software Development

從零開始土炮MQ系列 第 11

四、Router 路由器(2) - Router key

4.3 Queue 的註冊

接下來,我們來處理 AddQueue 在支援不同類型 Queue 時,要不停增加函數多型的情況。

在這邊,我們回顧一下需求。整理一下思路,程式改動的目的是 不用修改程式,就可以指向不同類型的佇列

再加上前面 Enqueue(QueueItem item) 的實作,是利用 Label 的 欄位進行資料的識別,那我們是不是可以加入 Label 做為 registry key 。

Binding Queue 時,必須註冊 RouterKey,以便 Router 在分派輸入的物件。

初版

我們最初的想法,是直接指定 Router 的泛型類別,而且 Queue 的儲存內容,限定泛型類別或其繼承類別。

//	實作概念
public class QueueRouter<T>
{
    public void AddQueue(string label, ConcurrentQueue<T> queue){...}
    public void Enqueue(QueueItem item){...}
}

還記得先前的 QueueItemPayload 類別是 object 嗎?

在變更 Enqueue(QueueItem item) 的前提下,就會發生隱型限制,也就 T 的類型只能為 object 。但基於一次只變動特定功能的原則。

這個版本的問題,我們延後處理,先驗證目前想法是否可行。

//	變更為泛型的 QueueRouter
public class QueueRouter<T>
{
    private Dictionary<string, ConcurrentQueue<T>> _dctQueues
        = new Dictionary<string, ConcurrentQueue<T>>();

    public void AddQueue(string label, ConcurrentQueue<T> queue)
    {
        _dctQueues[label] = queue;
    }

    public void Enqueue(QueueItem item)
    {
        if (!_dctQueues.ContainsKey(item.Label))
            return;
        _dctQueues[item.Label].Enqueue((T)item.Payload);
    }
}
//	測試案例
[TestClass]
public class QueueRouterTest
{
    [TestMethod]
    public void QueueRouter_輸入不同的值_放入對應佇列()
    {
        var router = new QueueRouter<object>();
        var intQueue = new ConcurrentQueue<object>();
        var strQueue = new ConcurrentQueue<object>();

        router.AddQueue(typeof(int).ToString(), intQueue);
        router.AddQueue(typeof(string).ToString(), strQueue);

        int[] expected = {23, 16};
        string[] strExpected = {"Skype", "Google"};
        Enqueue2Router(router, expected);
        Enqueue2Router(router, strExpected);

        var actual = DequeueFromQueue(intQueue);
        var strActual = DequeueFromQueue(strQueue);
        expected.ToExpectedObject().ShouldMatch(actual.ToArray());
        strExpected.ToExpectedObject().ShouldMatch(strActual.ToArray());
    }

    private static void Enqueue2Router<T1, T2>(
        QueueRouter<T1> router, IEnumerable<T2> expected) where T2 : T1
    {
        foreach (var i in expected)
            router.Enqueue(new QueueItem {Label = i.GetType().ToString(), Payload= i});
    }

    private static List<T> DequeueFromQueue<T>(ConcurrentQueue<T> queue)
    {
        var actual = new List<T>();
        while (!queue.IsEmpty)
        {
            queue.TryDequeue(out var item);
            actual.Add(item);
        }

        return actual;
    }
}

雖然我們確定這個作法可行,但目前會遇到幾點問題。

  • QueueItem 造成的泛型類別隱性限制。
  • Boxing / Unboxing 的吃效能的問題。

所以,接下來,我們先處理泛型類別隱性限制為 object 的問題。


迭代:QueueItem 的泛型

先前 QeueueRouter 的泛型類別會被隱性限制為 object 的問題,主要是出在 QueueItem 的 Payload 類別。

所以我們將 QueueItem 變更為 QueueItem<T> ,同時對 QueueRouter 進行對應的改動

//	QueueItem 的泛型
public class QueueItem<T>
{
    public string Label { get; set; }
    public T Payload { get; set; }
}

//	實作概念
public class QueueRouter<T>
{
    public void AddQueue(string label, ConcurrentQueue<T> queue){...}
    public void Enqueue(QueueItem<T> item){...}
}

針對概念的變動,此次迭代的異動範圍有...

  • QueueRouter.Enqueue 的傳入參數,由 QueueItemQueueItem<T>
  • 測試案例對應的修改。
//	針對迭代的內容,只要修改這個函數,即可全部通過測試。
private static void Enqueue2Router<T1, T2>(Router<T1> router, IEnumerable<T2> expected) where T2 : T1
{
    foreach (var i in expected)
    {
		router.Enqueue(new QueueItem<T1> {Label = i.GetType().ToString(), Payload = i});
	}
}

從上面,我們己經解決了 QeueueRouter 的泛型類別會被隱性限制為 object 的問題 ,但還是幾點可以持續改善的。

  • Payload 的類型為 Vaule Type 時,用 object 做為泛型類別,仍有 Boxing/Unboxing 的效能問題。
  • 對 QueueRoter 要 Binding 全新的類別,必須額外再建立對應的 Router。

目前只有部份滿足需求,就是所有的資料,只要放入同一個 Router,就可以放入對應的 queue 之中。

所以接下來,我們先改善一個 Router 無法通用所有 Queue 類型的問題。


上一篇
四、Router 路由器(1)
下一篇
四、Router 路由器(3) - 通用 Router
系列文
從零開始土炮MQ30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言