框架,依賴注入,以及介面

上學時經常聽到兩句話:

1 針對介面編程,而不是實現。

Programming to the interface, not the implementation.

2 對修改關閉,對擴展開放

Close to modification, open to extension.

這兩句話在那時唯一的作用就是應付考試和面試,知道上班後接觸了一些實際代碼才有所體會。

依賴注入有兩個使用原則:

1 Only inject what you exactly want. 僅注入所需要的依賴

2 Dont misuse or abuse the Dependency Container(DC). 不要濫用依賴容器,因為依賴容器本身在你的代碼中,也是一項依賴。

這裡的「依賴」可以指代為第三方庫,文件等等。

在各種RESTFUL API開發框架中,依賴注入是一個很重要的概念。比如,Silex框架中:

// controller.php public function post(Application $app, $entity) { $app[entity_manager]->persist($entity);}

此代碼用於創建一個entity,$app[entity_manager]直接返回一個entity manager用來持久化對象。在Silex框架中,Application是一個貫穿整個代碼生命周期的依賴容器,給定名字key,返回相應的值value。語法看起來感覺就是一個哈希,其實不錯,它的本質就是一個大的哈希表,只不過這裡的值可以是各種類型,函數,常量,類等等。

這段代碼看起來一切無大礙,甚至其官方的文檔例子中也有類似的代碼。但是這段代碼隱藏的不足:

1 單元測試時,不得不mock一個app,還要mock一個entity manager。將來在app中注入更多的依賴項時,你測試時也需要mock更多。

2 你的controller代碼是緊緊耦合於silex框架的,因為你直接傳參了一個Application。將來如果打算使用另一個框架時,你不得不重寫這個controller的代碼。

於是重構如下,去除Application依賴:

// controller.php public function put(EntityManager $em, $entity) { $em->persist($entity);}

到此為止,這個代碼看似去除了對特定框架Silex的依賴。但是注意,EntityManager類,它可以是MySQL,也可以是Mongo。不同的資料庫有不同的EntityManager庫,所以對於EntityManager類,你也應該定義一個你自己的類(或者自己的介面),而不是直接使用第三方的類。因為一旦你直接使用了第三方的庫,你的代碼就和其產生了耦合。這使得後期的測試難以開展,也使得本身代碼不能很好的擴展。

警惕:當使用依賴注入框架時,其框架本身也是一個依賴項。這裡不是說抵制各種框架,框架自然是好東西,提高了整個生產效率,但是使用各種框架時,我們得思考,到底使用的是什麼?就我的認識而言,使用開發框架時,我們使用的是其提供的一個完善的協作流程和其提供的各種開發模塊。各種框架都已定義好一套完整的流程:從接收一個http請求,到返回一個http響應,中間的數個步驟可以由我們自己任意的添加和修改。 我們專註的是每個模塊的開發和其功能實現,因此,在開發核心的功能模塊時,不要隨意使用第三方庫,而是使用自己定義的介面(實現上可以隨意的使用各種庫)。

所以另一個通用的原則:所有在頂層的類,都統一是自己的定義的介面。介面,說到底了,就像是指定了一個標準。寫代碼就像是在造車,需要一個輪子。這個輪子,僅僅是一個概念而已,出現在草圖上。後期真正開始實現時,需要一個木頭的輪子,還是塑料輪子,這些都可以慢慢實現,只要它能轉。這個概念,在設計之初就以確定,而後期的擴展,塑料木頭,你都可以隨便用。

推薦閱讀:

類的設計原則
面向新手的雜談:Flyweight(續)
遊戲開發與程序設計知識總結01——設計模式
設計模式之「Decorator」註疏#02
單例模式小結

TAG:設計模式 | 編程技巧 | API |