交易軟體中的設計模式 (1) 代理模式與 COM 元件

December 17, 2023

img


代理模式 Proxy Pattern

我們先走一遍,代理模式的結構是如何產生的

我們現在有一個 Client ,而他的 main() 會需要使用到 Xfunc()

img

由於 設計需求 (後面討論),我們希望 Client 不要直接接觸 X

例如, X 可能還有很多其他函數: unsafeFunc1 , unsafeFunc2 , unsafeFunc3

但我們不希望 Client 碰到,所以我們先定義一個介面,這個介面只有 func()

img

接著我們依照介面實作 NewX 作為 Client 互動對象,並且只有 func() 可用:

img

你還可以在 func() 前後做些準備,讓調用 func() 之前與之後做好充足準備

例如透過 beforeFunc() 檢查傳入的東西是否合法, func() 是否可以執行

或是透過 afterFunc() 把結果做些處理,再返回給 Client

你也可以把 Xfunc() code 抄過來,讓 Client 與整個 X 解耦


然而事情可能沒有那麼簡單, func() 高度依賴 X 內部的屬性與方法

那你就必須讓 NewX 持有 (has-a) X ,那這就是最經典的 組合代理模式

img

這時候 NewX 就是 XProxy ,並持有 X ,透過 self.x.func() 調用


有時候,你可能無法持有 X ,例如 X 在另一個進程、在另一個電腦

那你就需要在 XProxyX 之間建立通信,考慮 通信代理模式

這邊先最簡單的多進程共享 Queue 舉例

假設 X 在另一個進程中,初始化設置好與 XProxy 共享一個 xqueue

img

所以你可以在 XProxy 中透過往 xqueue 放入 args (a, b)

並讓 X 透過對 xqueue 的監聽,取出後執行 func() ,達成代理的功能


交易軟體中的設計需求:COM 元件的生命週期

如果券商提供的 API 是透過 COM 元件連線,有時候連線中斷後,你可能要重新連線

然而 COM 元件內部他怎麼紀錄使用狀態你不知道,就可能造成重新連線並沒有把狀態全部重置

而我們執行的 COM 元件,可能也要重啟 Event loop,然後重新 coInitialize1

由於無法確保獲信賴第三方套件,最安全的做法就法就是讓 COM 元件跑在另一個進程 2

如果券商 API 執行在另一個進程,那就像是前面的 X 一樣

我們在主進程中,就得透過 XProxy 來使用 API,並且可以監控 X 的狀況

而在 API 出問題時,可以透過 XProxy 直接把進程殺死並重啟,確保環境狀態完整重置

當然,這些都是有附加的代價,設計永遠都是各種權衡

關於代理模式的優缺點網路上有很多文章,大家需要根據自己的需求自己選擇


Footnotes

1 在 Python 中,得透過 comtypes.CoInitialize 初始化環境,包含確保線程安全、管理生命週期

2 相比之下,多數 DB 的 Client 端是開源的且封裝良好,需要使用到的設計模式是 repository pattern

Tags: architecture trading python com