2021-06-17:nestjs及相關的設計思想和名詞

由于工作需要,需要選擇一個NodeJS的框架來寫后端業(yè)務,由于很久沒有Coding了,所以起初的選型邏輯也非常簡單,基本是以4四點:A、背后的團隊 B、成熟度 C、社區(qū)用戶量 4、問題響應速度。很快變從EggJS、NestJS、ThinkJS及Koa/express幾個熱門框架中選擇了EggJS。

這主要是考慮到:

  1. 符合以上的4點要求,可靠性比較強
  2. 自己之前有過一些簡單的了解和應用。

不過在開發(fā)和使用過程中,由于不斷要去一些社區(qū)看一些問題,沒多久便看到不少開發(fā)者頻繁提到NestJS,而且從一些介紹中了解,似乎NestJS非常與眾不同,未來前景很好,甚至可以說NestJS才是真正解決了架構問題的服務端框架。這很快引起了我的好奇和注意。

于是自己開始花一些時間去了解學習這個框架,初看文檔,NestJS在我們腦海中立即打下了下面一些標簽:

  1. NodeJS中的Spring框架
  2. 幾個概念:IoC、DI、AOP
  3. SOLID設計原則
  4. 裝飾器寫法
  5. TypeScript

其實4和5對于一直關注在前端領域的同學們來說并不稀奇,TS補充了JS弱類型的一些缺陷,而且語法對JS完全兼容,非常適合前端使用。關于裝飾器,本來就是TS以及ES7中的新特性。不了解這塊的通過看一些文檔也可以很快熟悉起來,這里并沒有什么值得去說的。

真正讓我開了眼睛的反而是前3點,這些概念對于一些資深的后端及軟件開發(fā)的同事來說可能并不算陌生。但是對于眾多一直使用JS的前端開發(fā)者而言,還是比較新鮮的,尤其是沒有參與過服務端開發(fā)的同學而言。

我雖然早年做JAVA以及在使用YUI和Angular時對這些概念有過一些了解,但這么多年過去,也都忘得差不多了。這幾天經(jīng)過各種Google,重新溫習了下這些對于軟件設計來說非常重要的知識,我們一個個來說一下。

一、 Spring框架

Spring框架是Java中一個風行多年的框架。盡管很多人不喜歡JAVA框架,但也不得不說,這么多年的語言及框架發(fā)展下來,不少好的軟件設計思想在JAVA的諸多框架中融入已經(jīng)非常完善。Spring能夠風行這么多年也是如此。
很重要的就是在Spring中融入了比如OOP、IoC、DI等優(yōu)秀的軟件設計思想,結合JAVA語言的特點,實現(xiàn)了很好的擴展性、靈活性、可維護性。對于大型的軟件開發(fā)項目而言,這些點都非常重要。

二、IoC與DI

在我剛看到這兩個概念時,查詢了大量的文章和資料,但大部分的文章都講的云里霧里,似乎都不得要點,對于沒有任何相關語言和項目經(jīng)驗的人很難理解。有些說通過IOC降低對象的依賴,但是舉的實例又都千篇一律,完全看不出來怎么就降低了依賴,看起來只是換了一個寫法而已。

不過功夫不負有心人,再看了大量國內(nèi)外文章后,自己終于有了一點理解。

先說概念本身:
IoC(Inversion of Control):中文翻譯為控制反轉。
DI(Dependency Injection):中文譯為依賴注入。

那么什么叫控制反轉?既然叫控制反轉,必然應該是相對于控制正轉的。一般我們在項目開發(fā)中,一定會遇到多個類(對象)之間的依賴關系。舉個例子,一個汽車(Car類)是依賴一個發(fā)動機(Engine類)的,按照正常的思維習慣,我們的代碼一般會這么寫:

class Engine{
    constructor(){
        
    }
}

// Constructor injection
class Car{
    constructor(){
        this.engine = new Engine();
    }
}

這種寫法有幾個問題:

  1. 對象之間形成強耦合。即Car強依賴于Engine,任何適合調(diào)用Car,必須同時引用到Engine。假如未來要替換或替換Car的Engine,只能修改Car這個類本身;或者如果Engine的參數(shù)結構做個改變,那么就需要修改所有依賴Engine的類。
  2. 對象復用性變得很低。比如Car現(xiàn)在強依賴于一個EngineA。假如我們需要一個使用EngineB的Car,就必須重寫一個Car了。
  3. 測試復雜度變高。如果我們要測試Car的邏輯,這時候就必須執(zhí)行Engine的完整邏輯,但Engine的邏輯可能很復雜,這對于我們的測試來講毫無意義,只會造成損耗。在實際項目中,可能Engine的背后有各種三方調(diào)用,數(shù)據(jù)庫操作等,但Car可能只是幾個簡單的Method。

以上的這種依賴關系,也叫做下層控制上層結構,因為一旦下層(Engine)改變,就需要去調(diào)整上層(Car)的代碼,這種結構不符合軟件設計中的依賴倒置原則(Dependency Inversion Principle)。比較好的設計應該是上層控制下層,即上層決定使用什么樣的下層,然后由下層去具體實現(xiàn)。

IoC和DI便是為解決這個問題而產(chǎn)生。簡單說,就是通過約定接口來定義不同對象之間的相互調(diào)用能力,但不在一個類的內(nèi)部主動創(chuàng)建對象,而是通過特定方式注入到類中。

個人總結的一個原則:一個類對其它類的調(diào)用都通過注入的方式來使用,而不是通過諸如在內(nèi)部初始化或者其它隱式傳入(這在express/eggjs之類傳統(tǒng)框架較為常見)。

最簡單的依賴倒置代碼結構:

class Engine{
    constructor(){
        
    }
}

// Constructor injection
class Car{
    constructor(engine){
        this.engine = engine;
    }
}

const engine = new Engine();
const car = new Car(engine);

表面看起來,只是把engine實例由內(nèi)部初始化改為外部初始化后傳入。但這種結構從根本上解決了上面的幾個問題。

  1. 只要約定engine的接口規(guī)范,未來可以有很多個engine類型和示例,而Car不需要關系到底是什么Engine,只要符合Engine的接口規(guī)范,都可以傳入。
  2. Car有很強的復用性。不用關心具體的Engine,只要符合約定的接口。就可以使用這個Car類產(chǎn)生各類Engine不同的Car實例。
  3. 代碼的測試性變強。我們在測試Car的時候,不需要傳入真實的Engine,只要實現(xiàn)Engine接口即可,內(nèi)部實現(xiàn)可以完全mock。

DI本質(zhì)上和IoC講的基本是一個意思,DI更像是IoC的一個具體實現(xiàn)方式。

DI(依賴注入)的方式除了通過構造器外(見上述的代碼),還可以通過set方法注入。

// Setter injection
class Car{
    constructor(){
        
    }
    setEngine(engine){
        this.engine = engine;
    }
}

不過我們無需關系具體的注入方式,更重要的是要知道這么一個小的變化背后的思想是什么,為什么要這么做,解決了什么問題。

除了前面的IoC和DI,還有一個概念叫AOP(Aspect Oriented Programming),中文譯為:面向切片編程。單看這個單詞或者翻譯,很難直接理解其含義。

AOP可以這么理解,我們可以把一個業(yè)務看做很多個小業(yè)務邏輯組合而成。每個小業(yè)務邏輯可以看做是業(yè)務的一個切片,為了提高一些功能復用,我們可以通過AOP的方式,很方便在具體業(yè)務切片的前面或者后面添加特定的邏輯處理,從而改變原來的邏輯,從而實現(xiàn)特定的業(yè)務邏輯。

常見的應用場景一般為一些系統(tǒng)級的功能處理,如權限校驗、緩存、錯誤處理等。系統(tǒng)設計上只要預留好鉤子提供對于模塊前后的攔截處理能力,即可視為支持了AOP。如koa的中間件模型,即可理解為是AOP的一種。

正因為有了AOP的模型,我們可以在控制器中只關心自身的業(yè)務邏輯即可,而不用每個業(yè)務邏輯中去重復處理類似緩存、授權等公共邏輯。這使得系統(tǒng)的結構變得更加靈活可擴展。

三、SOLID

無論說到的AOP,還是DI、IoC等,其實都源自于2004年Robert大叔提出的SOLID設計原則。SOLID是五大原則的首字母縮寫,具體為:

  1. S: Single Responsibility Principle(單一職責原則)
  2. O: Open-Closed Principle(開放封閉原則)
  3. L: Liskov-Substitution Principle(Liskov替換原則)
  4. I: Interface Segregation Principle(接口聚合原則)
  5. D: Dependency Inversion Principle(依賴倒置原則)

由于解釋比較長,而且網(wǎng)上有非常很多詳細的解釋,這里就不贅述了??梢渣c擊上面的連接查看喜歡看中文的,也可以查看http://www.itdecent.cn/p/1c6498da3862這篇文章做簡單了解。

最后

了解上面這些關鍵的概念和思想,我們再來看restjs,就比較容易理解其代碼結構及設計思想了,當然也能夠更好去使用好它。

隨著前端生態(tài)的不斷拓展,尤其隨著Nodejs在后端應用的深入,作為一個想做全棧開發(fā)的前端人員,千萬不要僅僅把后端理解為對數(shù)據(jù)庫的CRUD這么簡單,而應該多請教后端的一些資深大牛們,深入去學習更多軟件設計理念和思想。

唯有如此,才能成為一名真正的全棧工程師~

[參考資料]:

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容