W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗(yàn)值獎勵
Golang開發(fā)語言并沒有完整實(shí)現(xiàn)?OOP
?特性,因此我們只能采用包封裝的方式來踐行"高內(nèi)聚,低耦合"的功能設(shè)計(jì)。在進(jìn)行代碼分層管理之后,我們會發(fā)現(xiàn)包命名變得很困難。
大部分時(shí)候,我們都習(xí)慣按照業(yè)務(wù)領(lǐng)域來進(jìn)行命名。例如,在?api/dao/service
?分層下,我們可能都會同時(shí)存在一個(gè)以?user
?命名的包名(表示用戶相關(guān)的功能邏輯),由于包名相同,在使用的時(shí)候卻有極大的困擾。主要的兩個(gè)痛點(diǎn):
internal_member_privilege
?的包名import alias
? )cycle import
? )問題需要注意的是,Golang語言層面的包循環(huán)依賴檢測其實(shí)是很棒的一個(gè)特性,它以?package
?作為代碼封裝基本單位,使得程序邏輯在?package
?之間的執(zhí)行路徑都是單向調(diào)用鏈,可以幫助你梳理出清晰的?package
?依賴關(guān)系、編寫出更加健壯性的代碼。
?GoFrame
?開發(fā)框架經(jīng)過大量的項(xiàng)目工程實(shí)踐,本著從簡約、簡潔、高效、易維護(hù)的設(shè)計(jì)理念出發(fā),總結(jié)出了一些關(guān)于包設(shè)計(jì)和命名約束的最佳實(shí)踐,可供參考。
在代碼分層設(shè)計(jì)之后,我們盡量地減少封裝包的數(shù)量、降低包的復(fù)雜度,盡可能采用結(jié)構(gòu)化對象的方式來封裝代碼處理邏輯。
對于業(yè)務(wù)項(xiàng)目而言,業(yè)務(wù)的復(fù)雜度會不斷/快速增長,我們期望設(shè)計(jì)的模塊復(fù)雜度盡可能的小、職責(zé)盡可能的單一。而直接使用包封裝設(shè)計(jì)會使得每個(gè)包管理的資源比較多、單個(gè)包復(fù)雜度會比較高、并且存在過多同名包問題。因此我們需要將代碼做分層設(shè)計(jì)(劃分職責(zé))、將包內(nèi)容做進(jìn)一步拆分(細(xì)化粒度),并將代碼模塊的粒度細(xì)化為了"對象"方式進(jìn)行封裝(這里的"模塊"從?package
?細(xì)化為了?object
?)。目的是使得整體模塊設(shè)計(jì)更加的解耦,能夠快速響應(yīng)業(yè)務(wù)發(fā)展的變化。對于業(yè)務(wù)項(xiàng)目而言,我們采用對象封裝設(shè)計(jì)后,將會失去包循環(huán)依賴檢測特性帶來的好處,轉(zhuǎn)而由開發(fā)者自行維護(hù)對象之間的依賴關(guān)系。
涉及到業(yè)務(wù)對象封裝的代碼層級主要為?controller/service
?。每個(gè)業(yè)務(wù)包僅對外暴露實(shí)例化的對象用于該業(yè)務(wù)領(lǐng)域的具體功能邏輯封裝,同一層級下不同的業(yè)務(wù)領(lǐng)域邏輯通過不同文件來分別管理。包對外的公開對象采用業(yè)務(wù)模塊名稱(大駝峰)來命名,包內(nèi)部的數(shù)據(jù)結(jié)構(gòu)命名采用分層名稱(縮寫)+業(yè)務(wù)模塊名稱(大駝峰)來命名。
代碼分層 | 分層名稱縮寫 | 數(shù)據(jù)結(jié)構(gòu)命名示例 |
controller | c | cUser |
service | s | sUser |
特別需要強(qiáng)調(diào)的是,在?controller/service
?層級中的代碼,有且僅有需要導(dǎo)出的實(shí)例化對象才能公開。并且由于同一包下包含多個(gè)業(yè)務(wù)領(lǐng)域的數(shù)據(jù)結(jié)構(gòu)定義,因此在命名的時(shí)候務(wù)必遵從命名約束,否則容易出現(xiàn)命名沖突。采用單包管理以及實(shí)例化對象引用的設(shè)計(jì),整個(gè)包對外引用簡潔清晰、內(nèi)部維護(hù)緊湊簡便、規(guī)避循環(huán)引用問題。
封裝示例:
使用示例:
封裝示例:
使用示例:
?dao
?層的對象封裝是通過框架開發(fā)工具維護(hù)的,開發(fā)者無需自己定義。
使用示例:
如果各分層中的封裝對象都是以" 可變變量 "的形式對外暴露使用,因此存在被修改的安全風(fēng)險(xiǎn)。因此大家注意這些公開的對象不要以指針(也盡量不要以接口實(shí)例)方式公開這些對象、不要設(shè)置公開屬性(建議通過公開方法暴露內(nèi)部屬性)。
以下是一個(gè)錯(cuò)誤的示例:
以下是一個(gè)正確的示例:
富有實(shí)戰(zhàn)經(jīng)驗(yàn)的您一定發(fā)現(xiàn)了,我們上面推崇使用具體化的對象來封裝業(yè)務(wù)邏輯,而沒有一丁點(diǎn)的接口設(shè)計(jì)的影子。是的,本著務(wù)實(shí)以及成本收益的衡量,我們確實(shí)推崇使用具體化的對象來封裝業(yè)務(wù)邏輯,特別是針對于?controller
?層以及?dao
?層代碼。對于?service
?層的代碼,亦是如此。本章節(jié)主要側(cè)重于?service
?層代碼的接口化封裝設(shè)計(jì)介紹。
?GoFrame
?工程設(shè)計(jì)的顯著的一個(gè)特點(diǎn)是,按照技術(shù)維度進(jìn)行代碼分層、按照業(yè)務(wù)維度進(jìn)行對象封裝。這種設(shè)計(jì)思想落地的特點(diǎn)就是在同一代碼分層,業(yè)務(wù)模塊是平級的,使得各個(gè)業(yè)務(wù)模塊之間隔離性降低了,這也是純粹使用對象化封裝的痛點(diǎn)。比如以下的示例:
可以看到在?service
?層下,網(wǎng)絡(luò)模塊(?Network
?)訪問資源模塊(?Resource
?)時(shí),可以直接訪問到其所有的內(nèi)容,無論是公開還是私有方法,私有的屬性也能訪問甚至修改。同時(shí),雜亂的資源暴露也會使得調(diào)用者感覺困惑,模塊管理和協(xié)作成本比較高。我們更好地隔離資源,方便內(nèi)部模塊之間管理和協(xié)作,這種場景下我們推薦對?service
?業(yè)務(wù)模塊使用接口化設(shè)計(jì)。
將一個(gè)項(xiàng)目的業(yè)務(wù)模塊采用對象封裝其實(shí)也是有利弊的,不過針對大多數(shù)的項(xiàng)目,都不會接觸到需要挑戰(zhàn)?GoFrame
?框架工程化設(shè)計(jì)的邊界。在?GoFrame
?的整個(gè)分層設(shè)計(jì)中,?service
?層的業(yè)務(wù)邏輯沉淀是最多的,也是最復(fù)雜的一部分,在這一部分采用接口化設(shè)計(jì)是最有價(jià)值的,收益成本比很高。因?yàn)?service
?層的模塊不僅對外部開放,在?service
?層內(nèi)部,模塊與模塊之間的調(diào)用,才是整個(gè)業(yè)務(wù)項(xiàng)目中交互最頻繁、設(shè)計(jì)最復(fù)雜的。降低這一層的維護(hù)成本,簡化層級內(nèi)部模塊之間的調(diào)用復(fù)雜度,是最有價(jià)值和收益的事情。例如上面的例子,我們看看如何來接口化改進(jìn)。
接口化只需要兩步即可:
大家也應(yīng)該注意到了,對于?service
?層的對象化封裝方式,有一些不一樣。在?service
?層我們使用了方法返回對象,而不是采用對象變量形式。為什么呢?回答這個(gè)問題之前,我們先來看看將資源模塊(?Resource
?)進(jìn)行接口化改進(jìn)之后,我們在原來的網(wǎng)絡(luò)模塊(?Network
?)中是如何使用的:
是的,目標(biāo)業(yè)務(wù)模塊采用接口化改進(jìn)之后,對于調(diào)用方來講,使用方式并沒有發(fā)生變化。這便是我們?yōu)槭裁匆?service
?中使用方法化封裝對象,而不是直接使用對象變量定義的原因:
可以看到,我們增加一層接口定義,其實(shí)也增加了一層代碼的維護(hù)成本。此外,接口化的代碼對于代碼調(diào)試和定位不太友好,特別是多個(gè)層級嵌套的接口而言,想要定位到具體的實(shí)現(xiàn)成本較大。對于大部分的項(xiàng)目而言,其實(shí)往往不太需要接口化設(shè)計(jì)。架構(gòu)設(shè)計(jì)中常常出現(xiàn)的悲傷故事之一,就是為了設(shè)計(jì)而設(shè)計(jì)。
因此,始終本著務(wù)實(shí)的設(shè)計(jì)原則,在?GoFrame
?工程化設(shè)計(jì)中,我們推薦項(xiàng)目初期對業(yè)務(wù)邏輯的封裝使用對象封裝的方式,并支持項(xiàng)目根據(jù)業(yè)務(wù)發(fā)展需要從對象封裝設(shè)計(jì)到接口化設(shè)計(jì)的無縫切換。同時(shí),我們建議接口化設(shè)計(jì)中,不要使用多層接口嵌套(即?service
?接口方法直接返回實(shí)例化對象而不是另一個(gè)接口,接口又是接口,以此類推)。
service
?層采用接口化設(shè)計(jì)。
service
?目錄下的每個(gè)業(yè)務(wù)模塊不要都強(qiáng)行放一個(gè)代碼文件,可以按照多個(gè)文件拆分管理,文件隨著業(yè)務(wù)復(fù)雜度的提高可能文件會比較多,屬于正?,F(xiàn)象。
service/internal
?下使用包管理,并在?service
?下保留關(guān)聯(lián)的接口方法即可。?service/internal
?目錄存在的意義之一便是為此準(zhǔn)備的。Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: