至于Product和ProductRepository的代碼就非常簡單了,這里不多解釋。
示例到這里,也許大家有個(gè)疑問:上面的代碼有些什么問題?
q ProductService依賴于ProductRepository。在沒有任何變化的情況下,這沒有什么問題。但是如果現(xiàn)在項(xiàng)目換了數(shù)據(jù)存儲(chǔ)設(shè)備,例如將數(shù)據(jù)庫換成了XML文件,或者數(shù)據(jù)庫的訪問技術(shù)從ADO.NET 換成了Entity Framework,那么ProductRepository的代碼就得改變,這會(huì)使得整個(gè)項(xiàng)目都需要重新編譯,重新部署。問題來了:此時(shí)系統(tǒng)中只有ProductRepository一個(gè)變化點(diǎn),為什么非得要求整個(gè)項(xiàng)目重新編譯,重新部署呢?難道不能只重新編譯和部署那個(gè)變化的模塊呢?
q 代碼不具有測試性能。要想知道此段功能代碼是否按照了我們的意愿運(yùn)行,可以通過人工審核,然后通過GUI界面獲取數(shù)據(jù)來進(jìn)行調(diào)試,此時(shí)的邏輯相對(duì)而言比較簡單,此方法也還行得通。不過,一旦業(yè)務(wù)邏輯變得復(fù)雜或代碼量的劇增,那么很難確保代碼不會(huì)出錯(cuò),而這些錯(cuò)誤很多時(shí)候只會(huì)在運(yùn)行時(shí)才能被發(fā)現(xiàn)。
q 緩存機(jī)制依賴于HttpContext,這不僅僅會(huì)讓測試產(chǎn)生困難(盡管可以有Mock),而且會(huì)對(duì)后續(xù)系統(tǒng)的擴(kuò)展有阻礙(例如采用分布式緩存)。
對(duì)以上的問題進(jìn)行進(jìn)一步分析,可以知道,這都是因?yàn)檫`背了以下設(shè)計(jì)原則:
q ProductService依賴了ProductRepository的具體實(shí)現(xiàn),而ProductRepository是一個(gè)可能的變化點(diǎn)。也就是說:ProductService這個(gè)高層模塊依賴了ProductRepository底層模塊,違背了依賴倒置原則,這也就使得一個(gè)ProductRepository變化,整個(gè)項(xiàng)目都需要重新編譯,重新部署。同理,緩存機(jī)制也是。
q 對(duì)于可測試性的問題,嚴(yán)格來說,上面的代碼是可以測試的,但是測試的時(shí)候必須依賴于外部的數(shù)據(jù)存儲(chǔ)設(shè)備,例如數(shù)據(jù)庫,那么測試的結(jié)果可能會(huì)由于數(shù)據(jù)的變動(dòng)而不一樣,而且每次測試所花的時(shí)間也會(huì)比較長。
接下來嘗試重構(gòu)上面的代碼,讓代碼的組織方式更加的靈活和易于擴(kuò)展。
既然上面代碼主要是違背了DIP依賴倒置原則(再次回顧一下DIP:依賴抽象,而不依賴具體實(shí)現(xiàn))。
那么現(xiàn)在就提出接口IProductRepository,使得ProductService依賴這個(gè)接口,代碼如下: