都說軟件架構(gòu)要分層、分模塊,具體應(yīng)該怎么做之二
- 一、前言
- 二、需求調(diào)研和需求分析
- 1. 用例圖
- 2. 用例描述
- 三、概要設(shè)計(jì)
- 1. 針對(duì)關(guān)鍵用例,畫出魯棒圖
- 2. 對(duì)魯棒圖中的模塊進(jìn)行歸類,劃分出子系統(tǒng)
- 四、詳細(xì)設(shè)計(jì)
- 1. 邏輯架構(gòu)
- 2. 運(yùn)行架構(gòu)
- 3. 開發(fā)架構(gòu)
- 五、架構(gòu)驗(yàn)證
- 1. 系統(tǒng)框架
- 2. 技術(shù)瓶頸
- 六、總結(jié)
一、前言
在上一篇文章中,我們主要聊了:在嵌入式系統(tǒng)的應(yīng)用程序架構(gòu)設(shè)計(jì)中,應(yīng)該從哪些方面來進(jìn)行需求整理和分析,文章鏈接:都說軟件架構(gòu)要分層、分模塊,具體應(yīng)該怎么做之一。
這篇文章,我們繼續(xù)聊一下在概要設(shè)計(jì)、詳細(xì)設(shè)計(jì)階段,我們應(yīng)該做什么工作?用什么工具或手段來做?輸出結(jié)果是什么?
按照慣例,為了內(nèi)容描述的方便,我會(huì)用一個(gè)物聯(lián)網(wǎng)網(wǎng)關(guān)的設(shè)計(jì)過程,把所有的內(nèi)容串接在一起。如果小伙伴對(duì)于網(wǎng)關(guān)不太了解,請(qǐng)滑到文章底部的推薦閱讀列表,其中有幾篇文章是關(guān)于網(wǎng)關(guān)功能介紹的。
二、需求調(diào)研和需求分析
1. 用例圖
上篇文章說到,在進(jìn)行需求調(diào)研和需求分析的時(shí)候,用例圖是非常非常好用的一個(gè)工具。通過用例圖,我們可以把一個(gè)系統(tǒng)中需要完成的所有功能,從粗粒度上一目了然的呈現(xiàn)出來。
下面這張圖,是網(wǎng)關(guān)的用例圖(這里畫的用例還不完全):
2. 用例描述
用例圖僅僅是描述了系統(tǒng)具有的功能,但是并沒有描述每一個(gè)用例的行為,也就是執(zhí)行過程。
在上一篇文章中說到,我們不需要對(duì)每一個(gè)用例進(jìn)行分析,而是需要在這些用例中,找出那些關(guān)鍵用例,然后對(duì)這些關(guān)鍵用例寫出用例描述,因?yàn)殛P(guān)鍵用例才是系統(tǒng)架構(gòu)的決定因素。
那么又出現(xiàn)一個(gè)問題了:如果把所有的用例,按照重要程度進(jìn)行優(yōu)先級(jí)排序,那么從上到下應(yīng)該選取多少個(gè)、或者說百分之多少的關(guān)鍵用例呢?這個(gè)就要看整個(gè)系統(tǒng)的復(fù)雜度了,30%不嫌少,50%不嫌多,根據(jù)你的時(shí)間自由把握。
以上圖網(wǎng)關(guān)中的用例圖來說,我認(rèn)為:添加設(shè)備、刪除設(shè)備、控制設(shè)備、規(guī)則配置、規(guī)則觸發(fā)這幾個(gè)用例比較關(guān)鍵,因此,我就針對(duì)這幾個(gè)用例寫用例描述。
(1)添加設(shè)備用例描述
其中有 2 點(diǎn)注意的地方:
在事件流中,我們是把網(wǎng)關(guān)作為一個(gè)黑盒進(jìn)行描述的,因?yàn)槲覀兪窃谶M(jìn)行需求分析,而不是在進(jìn)行設(shè)計(jì),因此,不需要考慮網(wǎng)關(guān)內(nèi)部的執(zhí)行流程;
紅色部分都是一個(gè)執(zhí)行主體,這個(gè)主體可以是一個(gè)人、一個(gè)界面、一個(gè)設(shè)備、一個(gè)系統(tǒng)等等;
事件流可以用文字來描述(就像圖中這樣),也可以畫一個(gè)序列圖來展現(xiàn)這個(gè)過程,就像下面這樣(這里沒有詳細(xì)描述出更細(xì)的執(zhí)行過程,主要以示意性為主):
(2) 刪除設(shè)備用例描述
(3) 控制設(shè)備用例描述
(4) 規(guī)則配置用例描述
(5) 規(guī)則觸發(fā)用例描述
三、概要設(shè)計(jì)
可以把概要設(shè)計(jì)理解成一個(gè)粗略、抽象的架構(gòu)圖,用來體現(xiàn)高層組件,以及它們之間的聯(lián)系。那么應(yīng)該怎么做,才能得到這樣的一張架構(gòu)圖呢?
我們現(xiàn)在的掌握的材料就是:用例圖和(關(guān)鍵用例的)用例描述,而且在用例描述的基本事件流中,把要設(shè)計(jì)的系統(tǒng)當(dāng)做一個(gè)黑盒子進(jìn)行描述。
現(xiàn)在我們需要做的事情,就是打開這個(gè)黑盒子,進(jìn)入其中內(nèi)部,從執(zhí)行過程上來分析:需要哪些模塊完成什么動(dòng)作。
注意,這是我們的目的。要達(dá)成這個(gè)目的,使用魯棒圖這個(gè)工具。
也就是說,我們現(xiàn)在需要通過魯棒圖這個(gè)工具,去拆解用例描述中的事件流,把系統(tǒng)內(nèi)部的、為了完成這個(gè)用例所需要的參與元素,全部都找出來,并標(biāo)注它們之間的關(guān)系。
1. 對(duì)每個(gè)關(guān)鍵用例的用例描述,畫出魯棒圖
先說一下容易混淆的概念:魯棒性,也稱作健壯性,是指程序在運(yùn)行過程中,即使出現(xiàn)了一些錯(cuò)誤的狀況,也已讓能夠順利的執(zhí)行下去。它描述的是程序的容錯(cuò)性。
魯棒圖是指:用圖形建模的方式,來描述一個(gè)用例描述是否正確、是否完善。
主要通過 3 種元素:邊界對(duì)象,控制對(duì)象和實(shí)體對(duì)象,來畫出一個(gè)用例描述中,待設(shè)計(jì)的系統(tǒng)內(nèi)部各功能模塊之間的交互關(guān)系。
邊界對(duì)象:在系統(tǒng)內(nèi)部,需要與外界進(jìn)行交互的元素。它負(fù)責(zé)接收外部的輸入、向外部輸出內(nèi)部的處理結(jié)果;
控制對(duì)象:描述動(dòng)態(tài)的控制行為,強(qiáng)調(diào)從一個(gè)執(zhí)行環(huán)節(jié)進(jìn)入另一個(gè)執(zhí)行環(huán)節(jié);
實(shí)體對(duì)象:對(duì)一個(gè)信息內(nèi)容進(jìn)行描述,比如:網(wǎng)關(guān)中的一個(gè)設(shè)備描述信息、一條規(guī)則配置信息等;
關(guān)于邊界對(duì)象,在 Web 類項(xiàng)目中,可能比較好理解,就是與用戶、外部系統(tǒng)所交互的界面。但是在嵌入式系統(tǒng)中,大部分情況下是沒有界面的,但是我們只要抓住一個(gè)根本的東西:接收外部的輸入、向外部輸出數(shù)據(jù)。
我們這里就簡(jiǎn)單畫一下添加設(shè)備、控制設(shè)備和規(guī)則觸發(fā),這 3 個(gè)用例描述對(duì)應(yīng)的魯棒圖(先忽略這幾張圖中的顏色):
添加設(shè)備:
控制設(shè)備:
規(guī)則觸發(fā):
關(guān)于添加規(guī)則的執(zhí)行過程中,大部分工作是在手機(jī) APP 上完成的(選擇源設(shè)備--觸發(fā)條件--目標(biāo)設(shè)備),網(wǎng)關(guān)中只是把配置好的這條規(guī)則存儲(chǔ)一下而已,沒有其他過多的操作。
規(guī)則中更重要的部分是規(guī)則觸發(fā)的處理,例如:當(dāng)紅外設(shè)備(源設(shè)備)檢測(cè)到人體時(shí),如果當(dāng)前處于布防狀態(tài)(觸發(fā)條件),就啟動(dòng)聲光個(gè)報(bào)警器(目標(biāo)設(shè)備),因此下面這張圖是描述執(zhí)行一條規(guī)則的執(zhí)行過程,這個(gè)過程的執(zhí)行鏈條比較長(zhǎng),能把很多的模塊串接起來。
2. 對(duì)魯棒圖中的模塊進(jìn)行歸類,歸納出子系統(tǒng)
假設(shè)我們現(xiàn)在把所有關(guān)鍵用例的魯棒圖都畫出來了,下一步的動(dòng)作就是對(duì)這些模塊進(jìn)行分類。上面幾張圖中,有些模塊被標(biāo)記了不同的顏色,相同的顏色表示它們是屬于一類的。
黃色部分的模塊都是與無線通訊相關(guān)的,那么這些模塊就可以歸類為無線通信管理子系統(tǒng);
綠色部分的模塊都是與設(shè)備相關(guān)的,那么它們就歸類為設(shè)備管理子系統(tǒng);
藍(lán)色部分的模塊都是與規(guī)則相關(guān)的,那么它們就歸類為規(guī)則管理子系統(tǒng);
繼續(xù)找出其他的子系統(tǒng)。。。
最終,我們把這些子系統(tǒng)(或者稱之為功能組)畫到一張圖中如下:
這張圖就從上層組件的視角,把整個(gè)系統(tǒng)劃分為幾個(gè)子系統(tǒng),每一個(gè)子系統(tǒng)都是一個(gè)獨(dú)立的、可以交付的實(shí)體模塊。
這張圖的作用還是挺大的,可以用于向領(lǐng)導(dǎo)進(jìn)行匯報(bào)(領(lǐng)導(dǎo)才沒有時(shí)間看詳細(xì)的設(shè)計(jì)),也可以用于產(chǎn)品說明書中的技術(shù)架構(gòu)描述部分,還可以用于團(tuán)隊(duì)成員分工,因?yàn)槊恳徊糠侄际且粋€(gè)獨(dú)立的單位,與其他子系統(tǒng)之間的耦合性,從靜態(tài)和動(dòng)態(tài)兩方面都隔離開來了(待會(huì)在后面的開發(fā)架構(gòu)設(shè)計(jì)中進(jìn)行說明)。
這些子系統(tǒng)之間是需要通信的,因此,在畫出這個(gè)設(shè)計(jì)圖之后,我們還需要做出下面的幾個(gè)決策:
使用的技術(shù)棧:開發(fā)語言 C,進(jìn)程之間的通信方式:消息總線;
并發(fā):每個(gè)子系統(tǒng)以進(jìn)程為執(zhí)行單位運(yùn)行在系統(tǒng)中,通過 MQTT 消息總線的C語言實(shí)現(xiàn) mosquitto 庫,來接入到總線系統(tǒng)上;
系統(tǒng)不支持二次開發(fā);
四、詳細(xì)設(shè)計(jì)
在上面的概要設(shè)計(jì)圖中,已經(jīng)把所有的功能模塊劃分到不同的子系統(tǒng)中,也可以稱之為功能組。下一步的工作,就是把每一個(gè)功能組中的內(nèi)部對(duì)象、需要完成的功能、交互流程找出來,具體來說,就是要分析出系統(tǒng)的邏輯架構(gòu)、運(yùn)行架構(gòu)和開發(fā)架構(gòu)。
1. 邏輯架構(gòu)
邏輯架構(gòu)就是把每一個(gè)子系統(tǒng)再分為粒度更細(xì)的功能塊,如果想粒度更細(xì)的話,也可以拆解到類這個(gè)級(jí)別。此外,還需要定義好各模塊之間的交互接口。
根據(jù)上面的描述,我們已經(jīng)決定把各子系統(tǒng)設(shè)計(jì)為一個(gè)獨(dú)立的進(jìn)程,各進(jìn)程之間通過消息總線進(jìn)行數(shù)據(jù)交互,而這個(gè)消息總線,是基于 topic 主題來進(jìn)行消息路由的,因此,下面就要設(shè)計(jì)好每一個(gè)進(jìn)程需要處理哪些數(shù)據(jù)交互:
- 入口:對(duì)其他哪些模塊的請(qǐng)求進(jìn)行響應(yīng);
- 出口:為了完成自己的工作,需要依賴其他哪些模塊提供服務(wù);
一句話總結(jié):就是找出每一個(gè)模塊,為了完成自己的工作,需要與其他哪些單元模塊之間進(jìn)行交互?交互的接口(函數(shù)、方法或者協(xié)議)是什么?
那么怎么來找到這些對(duì)象和接口呢?用序列圖或者類圖來完成。下面是控制設(shè)備的一個(gè)簡(jiǎn)單序列圖:
圖中的每一個(gè)箭頭,都代表一個(gè)接口,對(duì)于這個(gè)網(wǎng)關(guān)來說,就代表處理的一個(gè) topic 主題。
如果用類圖來分析,對(duì)于面向?qū)ο蟮拈_發(fā)語言來說,可能會(huì)更容易理解,比如:可以明確的定義出每一個(gè)對(duì)象的屬性,私有函數(shù),共有函數(shù),并且能夠清晰的構(gòu)建出對(duì)象之間的關(guān)系。
2. 運(yùn)行架構(gòu)
運(yùn)行架構(gòu)描述的是每一個(gè)執(zhí)行單元的動(dòng)態(tài)狀態(tài)、執(zhí)行時(shí)的控制流程,需要考慮的重點(diǎn)是:系統(tǒng)是否安全?性能是否滿足質(zhì)量要求?可擴(kuò)展性如何?
具體到網(wǎng)關(guān)來說,每一個(gè)子系統(tǒng)是以進(jìn)程為執(zhí)行單位的,每個(gè)進(jìn)程通過一個(gè)第三方的附件(也就是動(dòng)態(tài)庫),掛接到消息總線上,如下圖所示:
系統(tǒng)的并發(fā)性,是通過多進(jìn)程來實(shí)現(xiàn);系統(tǒng)的安全性,主要通過消息總線的安全機(jī)制來管理。
比如在開發(fā)階段,消息總線允許系統(tǒng)外的其他客戶端接入,這樣就可以在 PC 機(jī)上寫一個(gè)調(diào)試程序,接入到總線中,可以監(jiān)聽所有的數(shù)據(jù),此時(shí)數(shù)據(jù)可以不加密,全部是 human readable 的;但是在項(xiàng)目 release 階段,那么就關(guān)閉這個(gè)權(quán)限,PC 機(jī)上的客戶端就不能接入總線,并且總線中所有數(shù)據(jù)的需要加密、壓縮,進(jìn)一步提高系統(tǒng)的安全性。
3. 開發(fā)架構(gòu)
作為以擼代碼為主力的我們來說,開發(fā)架構(gòu)就容易理解了,無非就是定義好項(xiàng)目結(jié)構(gòu)、編譯流程、測(cè)試步驟等等。
具體來說,我們可以從下面幾方面來做出規(guī)定:
- 并行開發(fā):每個(gè)子系統(tǒng)是一個(gè)獨(dú)立的進(jìn)程,因此可以劃分為一個(gè)獨(dú)立的項(xiàng)目,提高開發(fā)效率;
- 第三方庫:作為基礎(chǔ)的公共模塊來使用(SSL加密、消息總線接入、通信協(xié)議解析);
- 代碼安全:每位開發(fā)人員只能有權(quán)限拿到自己負(fù)責(zé)的代碼,只有管理員有權(quán)限獲取所有代碼;
- 代碼管控:使用 git、svn 等工具進(jìn)行代碼版本的管理;
- 集成編譯:使用 Jenkins + git module 功能,自動(dòng)拉取所有的子系統(tǒng)代碼,自動(dòng)編譯。如果需要自動(dòng)部署的話,也可以使用腳本來實(shí)現(xiàn)。
五、架構(gòu)驗(yàn)證
終于來到最后一個(gè)環(huán)節(jié)了,其實(shí)項(xiàng)目經(jīng)歷多了,以上設(shè)計(jì)出來的架構(gòu),是否能滿足需求中提出的功能和質(zhì)量要求,我們?cè)谛闹幸呀?jīng)大概知道答案了。
為了保險(xiǎn)起見,我們還是需要對(duì)其中的某些關(guān)鍵部分進(jìn)行驗(yàn)證。這個(gè)驗(yàn)證過程是有價(jià)值的,或者說可以把這個(gè)驗(yàn)證過程所得到的成果,作為正式的代碼進(jìn)行提交。
驗(yàn)證的大方向有 2 點(diǎn):系統(tǒng)的框架是否合理、穩(wěn)定;一些技術(shù)瓶頸是否可以搞定。如果這兩部分都沒問題,那后面就可以大膽的往前走了。
六、總結(jié)
經(jīng)過 2 篇文章的介紹,我基本上把自己在平常工作中,對(duì)應(yīng)用程序架構(gòu)設(shè)計(jì)的這個(gè)思考過程描述了一遍。
佛經(jīng)里說了:渡人就像幫助一個(gè)人過河,過了河上了岸,就應(yīng)該把乘坐的木筏丟掉,心中不要再想著木筏。
這篇文章介紹的設(shè)計(jì)流程,也是一個(gè)套路而已。這個(gè)套路在面對(duì)一個(gè)新領(lǐng)域、新項(xiàng)目時(shí),就像一個(gè)腳手架一樣,告訴我們這一步該做什么,下一步該做什么,應(yīng)該使用什么樣的工具。
在僵化的運(yùn)用這個(gè)套路之后,你可以繼續(xù)改造、優(yōu)化,然后丟掉這個(gè)套路,從而形成適合你自己的套路,從此走向思考致富的道路!
祝你好運(yùn)!
本文轉(zhuǎn)載自微信公眾號(hào)「IOT物聯(lián)網(wǎng)小鎮(zhèn)」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系IOT物聯(lián)網(wǎng)小鎮(zhèn)公眾號(hào)。