首頁 > 軟體

GO中優雅編碼與降低圈複雜度詳析

2022-12-11 14:01:19

前言

本次主要是聊聊關於使用介面抽象和降低圈複雜度的方式

工作中,難免會遇到老專案老程式碼,不僅僅需要我們維護,可能還需要我們在原來的垃圾程式碼上進行新增功能或者是進行優化調整

例如

現有的老程式碼中關於使用者系統這一塊就已經經是搖搖欲墜,牽一髮而動全身,並且去弄清其中的業務細節,那可以說是很難撥開迷霧,甚至交接都是一句話的那種,更是難上加難

這種情況,相信每個公司都會存在,畢竟過去的需求,過去的標準,放到現在來看,啥也不是

若是很多程式碼都是程式導向的,各種業務邏輯,非業務的邏輯都混合在一起,主流程上插入一些亂七八糟的邏輯,上下文並沒有啥關係的東西,一個函數上千行的程式碼也是隨處可見,這種情況狗看了都搖頭

對業務函數需要做基本的封裝

首先咱們編碼前一定會去捋清楚基本的需求,設計,以及實現流程,對於需要用到的工具我們會對程式碼結構進行分層

例如一些與業務主邏輯沒有什麼關聯的功能就可以獨立封裝,便於維護和使用,例如:

  • 工具包(例如語言中的各種計算,資料處理,加解密等等)
  • 基本的 rpc 通訊
  • http 相關的各種通訊方式
  • 基本的中介軟體,攔截器,打點介面延時等等
  • 資料庫操作(獨立封裝 DAO 層提供出來)
  • 快取操作
  • 訊息佇列
  • ...等等

儘可能的將這些單獨的功能模組拆解出去,獨立出來,單獨維護

對於那種沒有必要同步的功能,完全可以通過非同步化來進行處理,非同步的話相信你會很容易想到訊息佇列來進行實現

自然實際專案中你能夠看到最開始可能也會這樣去做,但是隨著業務越來越複雜,這些獨立的模組被各種包進行使用,甚至有的開始慢慢的弄成客製化化的方式

例如

func OpenTenant(){
    // 校驗基本租戶資訊
    // 檢查租戶是否特權,完成許可權分配
    // 檢查實際開戶的線路,分配各種租戶下的必備賬號
    // 完成各種系統的對接互動
    // 進行資料庫操作
    // 返回結果
}

對於一個基本的開戶流程,我們或許可以在程式碼中看到第一步做什麼,第二步又做什麼,第三步... ,然而每一個大步驟下面還有各種小步驟,每一個小步驟也會有自己的複雜邏輯

雖然有了基本的封裝,但是使用的時候,可能還是會寫到哪,需要啥就去按需定義啥

最終就會看到一個函數上千行,讓你去閱讀和維護,你內心能不拒絕嗎嗎?

發現對模組進行獨立封裝還是不太夠,程式碼裡面太多的冗餘程式碼,這個時候咱們就可以使用介面來做抽象

用介面來做抽象

使用介面來做抽象的話,相當於是提前考慮好這一類的業務需要去考慮哪些問題,需要注意哪些場景,需要實現哪一些介面

不同的物件各自去實現自己的內容就可以了,單獨去維護自己的物件

例如上面的 A 系統的開戶流程

// 開戶 interface{}
type OpenTenant interface{
   ValidateTenantInfo(xxx)xxx // 校驗基本租戶資訊
   CheckPrivilege(xxx) xxx // 檢查租戶是否特權,完成許可權分配
   CheckLine(xxx) xxx // 檢查實際開戶的線路
   ProcessNeccessaryAccount(xxx) xxx //分配各種租戶下的必備賬號
   ProcessNoticeMsg(xxx) xxx// 完成各種系統的對接互動
   AddTenant(xxx) xxx// 進行資料庫操作
}

這僅僅是一個 demo,對於一個開戶 interface{} 來說,A 系統可以去實現,B 系統仍然也可以去實現,各自完成自己的內容,例如這樣

對於優化程式碼的話,我們就可以將上述的一些實現步驟,放到這個介面中來即可

咱們定義介面,更多的是去規範流程和便於維護,這樣還可以讓我們的程式往高內聚低耦合方面去靠,不同的物件之間,完全是安全的,自己玩自己的一套,只不過遵循的規範是一樣的的

儘可能降低圈複雜度

圈複雜度也可以理解為條件複雜度,是一種用來衡量程式碼複雜度的標準

例如一些沒有判斷語句的程式碼,圈複雜度就是 1

如果是 if...else 那麼圈複雜度就是 2 ,簡單的就可以理解為涉及到判斷條件的數量,那麼就 +1

例如有這樣的程式碼

func testDemo() {
    var op OpenTenant
    switch TenantType {
        case A:
            op = a.New()
        case B:
            op = b.New()
        case C:
            op = c.New()
        default:
           ...
    }
    op.ValidateTenantInfo()....
}

那麼就如上demo ,來看,圈複雜度就是 4 ,其中有 3 個判斷條件和一個預設的正常順序,因此是 3 +1 = 4

這個時候,我們可以如何降低圈複雜度呢?

我們完全就可以使用表格的方式,存取資料直接存取表格就可以了,儘可能的減少這些判斷條件,例如我們就可以這樣來寫

var openTenantMap = map[string]openTenantObject{
    A: a.New(),
    B: b.New(),
    C: c.New(),
}
func testDemo(){
    op := openTenantMap[TenantType ]
    ...
    op.ValidateTenantInfo()
    ...
}

這種方式,是不是就可以將圈複雜度降低到 1 了呢?而且看起來也優雅了很多

總結

主要叮囑了我們維護和開發的時候,要重視封裝,重視抽象,重視降低圈複雜度

只要你用心去打磨,自然會變得越來越好

但是可別生搬硬套,畢竟一些客製化化的需求,客製化化的程式碼你去做介面抽象是沒有啥意義的,一起加油吧

到此這篇關於GO中優雅編碼與降低圈複雜度的文章就介紹到這了,更多相關GO編碼與降低圈複雜度內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com