你有沒有過這樣乙個經歷,乙個專案立項之時,什麼模組化啊,什麼抽象啊,什麼解耦啊,什麼可復用元件啊什麼的,哪個高階用哪個,可是專案發展到中期,隨著模組的增加,什麼可復用,能用就行,什麼模組化,載入就行,久而久之,專案越來越大,隨之也越來越臃腫,越來越難以維護,改一處看似簡單的模組,卻發現八桿子打不著的地方居然也被影響了,真真是寫時一時爽,維護時更加爽!
那專案大了,維護成了難題,如何優化呢,怎麼解決呢!
看英文縮寫,是不是有點高大上,其實這個理念在後端是非常常見的,而前端很少涉及到。不過現代前端也可以在專案中實踐了,而且很契合。
三個準則
乙個案例
放著這些個準則不說,先用我們熟悉的macbook來案例來說明下吧! 我們都知道,mac電腦裡面都是乙個個模組組合成的,換算成**就是這樣樣子的:
// screen.ts
export
default
class
screen
// cpu.ts
export
default
class
cpu// battery
// 電池模式,普通模式,低電量,高電量
type tmode = "normal" | "low" | "high";
export
default
class
battery
= )
}// mac.ts
import screen from
"./screen";
import cpu from
"./cpu";
import battery from
"./battery";
export
default
class
macbook
start() , screen is $ and cpu is $`
);}}
// index.ts
import macbook from
"./mac";
let mac = new macbook();
mac.start();
複製**
首先建立乙個index.ts
啟動檔案,mac殼子mac.ts
,它內部有三個模組,cpu
,screen
和battery
,這個三個屬性分別引用的是檔案外的模組。
**這樣寫,其實沒有什麼問題的,執行index
就能看到結果,檢視到這個mac類的配置,那麼,如果說我要設定mac電池配置mode
為低電量,那麼我就不得不去mac.ts
主模組裡修改電池的配置。
this.battery = new battery();
複製**
這樣改,其實是沒有什麼問題的,但是,mac其中的乙個模組修改了,為什麼殼子mac.ts
這個檔案也要跟著動呢,而且這個殼子裡有mac所有的模組依賴,之前測試通過了,這次修改了,能不能保證一定沒有出錯呢,所以這次的模組改動就是我上面說到的問題,那如何改動呢?
第一次優化
// mac.ts
import screen from
"./screen";
import cpu from
"./cpu";
import battery from
"./battery";
inte***ce imac
export
default
class
macbook
start() , screen is $ and cpu is $`
);}}
// index.ts
import macbook from
"./mac";
import battery from
"./battery";
import cpu from
"./cpu";
import screen from
"./screen";
let mac = new macbook();
mac.start();
複製**
將模組的依賴全都放在了啟動檔案index.ts
處,無論模組如何改動,殼子模組mac.ts
是不是都不用改了,模組之間的耦合度也降低了。
簡單來說,mac.ts
是高層模組,battery.ts
是底層模組,優化之前mac.ts
依賴了battery.ts
,是不是違背了ioc
的第一條準則呢,優化後的**是將高層次的模組所需要的依賴通過引數傳遞到模組內部,這個方法有乙個專業術語 - 依賴注入(dependency injection)。
所需要傳入的引數型別imac
就是所定義的抽象,殼子模組mac.ts
就是依賴了這個抽象,而這個抽象也沒有依賴於某個具體的實現。
那麼問題又來了,如果我想給這個mac例項再增加乙個觸控板模組touchpad.ts
呢,是不是又要修改殼子模組mac.ts
了,難道新增乙個就要修改一次,就沒有乙個通用方案麼?
第二次優化
// mac.ts
type imodule= t | t;
export
default
class
macbook
start()
}// index.ts
import macbook from
"./mac";
import battery from
"./battery";
import cpu from
"./cpu";
import screen from
"./screen";
import touchpad from
"./touchpad";
let mac = new macbook();
mac .use(new cpu())
.use(new screen())
.use([new battery(), new touchpad()])
.start();
複製**
模仿koa
載入模組的use
方法,可以鏈式,這樣殼子模組mac.ts
就完全與低層次模組解藕了,無論mac新增多少個模組它都不會發生修改。mac.ts
內部已經看不到什麼業務**了,所有的配置都放在了最外層,即便修改新增也及其方便。
第三次優化
那麼問題又來了,mac.ts
對模組可是有要求的,不是任何乙個牌子的模組就能安裝到我的mac上,得按照一定的標準是執行,也就是依照一定的約定,這也就是第三個準則,面向介面程式設計而不是面向實現程式設計,下面就用**來展示這個準則:
// mac.ts
type imodule= t | t;
export
default
class
macbook
start()
}複製**
在mac.ts
的啟動方法中,我們看到了,對接的模組內部,一定要有乙個init
屬性,且這個屬性一定是乙個可執行方法,那麼所對接的模組要如何處理呢:
// cpu.ts
export
default
class
cpu start`);
}}複製**
類似於這樣的,要對接這個殼子,就必須在模組內部實現乙個init
方法,這樣這個模組才能在殼子內部起作用。
init
方法對於mac.ts
來說,只是乙個抽象方法,乙個約定的介面,將實現交給了所來對接的各個模組,這不就是面向介面程式設計 而不要面向實現程式設計最好的詮釋麼!
其實在ioc
的術語中,mac.ts
更應該稱作為容器(container),上面稱它為殼子比較貼近現實好理解,它跟業務實現其實沒有太大的關聯,僅僅是做一些初始化的操作,所以殼子不應該隨著它所依賴的模組的改變也跟著改變。 所以就需要一種ioc
的程式設計思想去優化它,依賴注入只是這種思想的一種實現。
最後說一句,思想才是提高程式設計的最佳手段,而不是學習怎麼用框架!
IOC控制反轉思想
傳統的開發需要程式設計師自己手動的new例項化乙個物件,但這種模式如果使用者需求發生改變往往會需要重新修改 例如 public inte ce userdao public class userdaoimpl implements userdao 使用者需求改變,增加userdao的實現 publi...
PHP 專案實踐中使用的IOC容器思想
1.容器的意思就是乙個全域性變數,裡面存了很多物件,如果要用到某個物件就從裡面取,前提就是要先把物件放進去 2.控制反轉就是把自己的控制權交給別人 3.這兩個結合就是,把自己的控制權交給別人並且建立的物件放進乙個全域性變數裡 4.好處就是可以靈活的修改乙個物件的屬性,而不需要去修改類本身的 clas...
IOC框架Ninject實踐總結
ninject是乙個輕量級的基於.net平台的依賴注入 ioc 框架。所謂的ioc,即控制反轉 inversion of control 它是乙個經典的物件導向程式設計法則,它的作用主要是用來幫助應用程式解耦,並把程式分離成乙個個松耦合高內聚的模組。控制反轉還有乙個名字叫依賴注入 dependenc...