【大型軟體開發】淺談大型Qt軟體開發(二)面向未來開發——來自未來的技術:COM元件。我如何做到讓我們的教學模組像外掛一樣即插即用,以及為什麼這麼做。

軒先生。發表於2023-01-13

前言

最近我們專案部的核心產品正在進行重構,然後又是年底了,除了開發工作之外專案並不緊急,加上加班時間混不夠了....所以就忙裡偷閒把整個專案的開發思路聊一下,以供參考。

鑑於接下來的一年我要進行這個主框架的開發,本著精益求精的態度,加上之前維護前輩的產品程式碼確實給我這個剛畢業的社畜帶來了不小的震撼,我決定在這個模組的開發中最佳化之前的開發模式,提升整個產品的健壯性和獨立性。

開發一個大型軟體最重要的問題有三個,一是如何保證每個模組開發的獨立性 二是如何保證資料結構的一致性 三是如何保證程式的可維護性和健壯性。這幾個文章的內容我會在幾篇文章中分開聊聊我的做法,做個記錄。

本篇文章聊聊如何保證各個模組開發的獨立性——怎麼讓功能模組、教學模組的開發獨立於主框架本身。讓不同的模組之間儘量透過介面的形式進行互動,而拋棄傳統的中轉訊息碼->呼叫模組的模式,讓實際功能以介面形式暴露。

這一期簡單聊聊開發準備,下一期淺談怎麼在開發中我們專案如何保證模組開發的獨立性。

技術背景

請在閱讀以下兩篇文章之後觀看本文以獲得更佳的體驗,以免你不知道我在說什麼。

Qt開發Active控制元件:如何使用ActiveQt Server開發大型軟體的主框架

Qt開發:Windows 下程式間通訊的可行橋樑:窗體訊息

【大型軟體開發】淺談大型Qt軟體開發(一)開發前的準備——在著手開發之前,我們要做些什麼?

為什麼?

我們談到為什麼要這樣開發的之前,要先聊聊之前我們的框架。

我們的軟體之前沒有框架,就是一個大的C#程式,然後裡面想到什麼就塞什麼,然後教學模組、主框架、功能模組之間就透過一個C#事件委託向上傳遞。教學模組中沒有辦法直接引用所有的功能模組,而是透過一個函式里面傳參,再轉移到主程式中去。

image

這個模式本身是沒有任何問題的,但是也可能是開發人員當時在開發的時候沒有考慮過這個專案要維護十多年,導致整個程式在後面變成了一個幾乎不可維護的龐然大物。

這裡功能模組是幾乎嵌死在主框架內的,這個是可以的,因為功能模組一般是呼叫外部的DLL或者對外部exe的管理,一般會和這些模組之間有深度的互動。而且一般我們也很少更新新的功能模組,光是現有的功能模組就幾乎夠了,之後的更新哪怕是直接嵌入在主框架內也是沒有關係的。

當然了,功能模組也是一個個DLL,但是原先的呼叫是在使用的時候去嘗試例項化一個功能類,然後再呼叫其中的方法。這樣就涉及到呼叫、事件繫結等問題。

最嚴重的問題還是來自教學模組的資訊向上傳遞。這個是由於引用導致的:

1.我們不可能讓每個教學模組去引用各個功能模組,這樣主框架完全脫離了教學模組的掌控,那相當於是每個教學模組重新開發了。而且每個功能模組並不能直接這樣使用,需要的很多引數和訊息都必須在主框架中獲取或者初始化。比如座位資訊,我們這個產品有一套很複雜的座位資訊資料。還有一些比如MAC地址等。如果獨立獲取會極大的提升整個產品開發的維護難度和開發難度。

2.只能由框架去呼叫教學模組,這也就導致訊息沒法直接向上傳遞,只能透過類似回撥函式的事件委託將事件以類似 key-value鍵值對的形式向上傳送。這樣我的主框架就要寫一大堆if else去根據每個key去判斷教學模組發這個訊息想表達什麼了。

我這裡展示其中一部分就知道有多逆天了:
image
image

當然就像我說的,這是由於早期設計的時候完全沒有考慮程式的擴充性開發所用的妥協性開發策略。在一開始確實方便而且好用。在後面這樣一大坨if else讓整個程式碼維護起來簡直異常困難,更別提在接到訊息之後這裡還需要小小處理一下。

3.教學模組沒法獨立開發,必須依託於教師端。怎麼說呢,就是教學模組幾乎完全沒法脫離教師端進行開發,因為要教學模組也需要暴露一大堆方法和介面供主框架去互動。模組設計操作起來就非常噁心了。

甚至!我們之前有過用exe的應用程式,所有的資訊互動都是透過SendMessage進行的!你知道那對我幼小的心靈造成了多麼大的傷害嗎!這樣的教學模組幾乎是完完全全嵌在主框架內的專用exe,幾乎不存在什麼擴充套件性和維護性,因為除了問題除了開發者本人,別人根本沒法改,也不知道怎麼改。

或者我們換一種說法:教學模組這樣設計就只能讓主框架開發人員開發,且很難多個模組之間平行開發。

怎麼做?

這就不得不提到面向未來的技術:COM元件

COM元件可以讓主框架提供介面,然後各個教學模組就可以直接去呼叫主框架提供的介面。這樣就直接替換掉了我上面說的if else判定這種僵硬的開發方式。

這個是ActiveQt Server向外公佈的介面文件
image

這裡提供的Public slots 就是主框架提供的方法。這樣我們就可以透過直接呼叫介面的方式呼叫主框架內部的服務了。

也就是說框架實際上和之前的那個if else也差不多,區別就是主框架不需要在接收到訊息之後進行檢索,可以直接呼叫介面了。也就是說不需要維護那麼長一條if else 鏈,而是暴露介面,管理介面即可~

就這樣?當然不止,在這裡我還提供了一個新的教學模組開發的正規化。原來的教學模組 就像靜態呼叫的DLL,而現在我們希望這個教學模組能像插槽一樣即插即用。

該怎麼做?既然是即插即用,那麼就得請出我們的動態呼叫DLL 方法,接下來我將給出我的框架設計。

框架設計?

框架設計分為教學模組設計和主框架介面設計,這兩部分實際上都是對主框架的設計,不過涉及的內容不一樣。

教學模組框架

教學模組框架的涉及我需要給出一張圖來做表示:

image

這個部分看圖吧,我懶得解釋太多了,這個圖我覺得寫的相當清楚了。

流程如下:

1.透過讀取配置檔案確定有哪些模組是可以嘗試去呼叫的

2.在本地資料夾中檢索可以呼叫的模組是否存在

3.呼叫教學模組約定好的方法來確定當前模組是否可用。

4.讀取本地鎖控,確定指定模組是否有鎖控許可權。

注1:因為是動態載入的DLL,那麼DLL就必須提供類C介面,也就是extern "c" __del什麼的,我忘了,反正就這麼回事,自己百度查一下吧。

主框架的介面設計:

image

如圖所示,我們在Interface_Kernel中提供了整個主程式中掛載的所有功能模組的單例呼叫介面,並在Interface_Kernel的單例中進行管理,然後再透過單例的方式直接向功能模組申請功能。這樣既避免了大量重複囉嗦的訊息碼發來發去,也可以減少很多重複開發的工作量。

這樣操作,主框架就不需要提供一個單獨的類來管理教學模組和與教學模組進行互動了,而是可以只需要關心教學模組的啟動、關閉、顯示、隱藏 即可,其他的功能都由Interface_kernel單獨互動即可。

這裡我寫了一個完整的工程,但由於程式碼是公司的財產,在此我不能開源所有的程式碼,如果你是將來維護開發的人員,你應該可以根據這篇部落格瞭解到為什麼主框架程式碼中會有介面類還有一個Interface_Kernel,原因也很簡單,因為每次外部繫結主程式的時候,都會例項化一個新的介面,需要一個核心來管理所有的介面類例項。

至於說教學模組DLL的管理,也是類似的,但是這裡就不展開細聊了。

總結

COM元件為功能模組的集中管理提供了可能,同時也為教學模組的獨立開發提供了可能。

整個新框架和舊框架的核心思想區別就是:就框架把所有的教學模組都當成了主程式的一部分。

而新的框架中主程式其實只是為所有的教學模組提供各種各樣的服務,比如語音、影片等服務,教學模組只需要呼叫指定的COM介面就可以直接調到主程式中的工具,而不需要再進行模組間的互動。而教學模組本身也只需要提供開啟、關閉、顯示、隱藏、認證五個介面,不需要再像之前一樣寫一大坨東西管理教學模組了。

這樣既為後續模組化開發提供了更多的便利,也讓主程式更加簡潔,減少了很多不必要的重複開發。

如果有什麼講的不夠詳細的地方歡迎站內私信,我會盡量解答。

相關文章