還記得個人軟件工程老師是這麼說的:軟件應該往高內聚,低耦合的方向進行設計。html
當時,還身爲一個初學者的我,不太明白老師的這句話——既然面向對象提供給了咱們」繼承「這種高耦合的概念,那爲什麼咱們還要低耦合高內聚呢?難道放着繼承的概念不用,而改成面向過程嗎?數據庫
帶着這一疑問,我請教了個人老師,他給個人回答是:經過接口來分隔分離邏輯,就能夠達到低耦合的效果。架構
咱們來回顧一下前一篇所學習的"控制反轉"設計思想,其實質就是上面所說的"經過接口來分離邏輯"。函數
但以上一篇的示例來講,達到這個目標,還有一些欠缺,首先讓咱們來回顧一下上一篇的代碼內容學習
/// <summary> /// 這是一個負責登陸的接口類型 /// </summary> public interface ILoginChecker { /// <summary> /// 登陸,須要的參數是用戶名和密碼,須要返回一個bool類型表示登陸是否成功 /// </summary> /// <param name="loginName"></param> /// <param name="password"></param> /// <returns></returns> bool Login(string loginName, string password); } public class LoginWindow : Form { private ILoginChecker loginChecker; /// <summary> /// 經過構造函數決定使用哪一個ILoginChecker來負責登陸流程 /// </summary> /// <param name="chcker"></param> public LoginWindow(ILoginChecker chcker) { this.loginChecker = chcker; } void LoginButton_Click(object sender, EventArgs e) { string loginName = null, password = null; //從控件上取值 //判斷空值 //等一切OK時 if (this.loginChecker.Login(loginName, password)) { //登陸成功 } else { //登陸失敗 } } }
確定會有一些人帶着這樣的疑問:在代碼的某個地方必定寫着類型這樣的內容:this
///實例化某個實現了ILoginChecker接口的類型 ILoginChecker checker = new XXXLoginChecker(); LoginWindow window = new LoginWindow(checker); window.Show();
那麼隨着之後功能的更新,在不斷地修改這裏的ILoginChecker checker = new XXXLoginChecker();spa
時間久了,記不得這段代碼寫在哪兒,也是很頭疼的一件事,最重要的是,依然沒達到」易維護「的效果,和以前相同,每次的須求改更,或是環境變化,都須要重寫這句話。設計
若是能有一個容器,可以在運行時(注1)判斷使用哪一個ILoginChecker類型,那就方便多了。code
運行時:與"編譯時"相對應,好比我說定義一個變量Int32 i,這個i的類型就是Int32,這是在編譯時決定的,由於程序在由代碼編譯爲程序時,已經能夠肯定i就是一個Int32類型了。orm
再好比說我定義一個變量Object obj,這個obj的類型雖然是Object,可是會根據實際的賦值,發生類型的變換,而賦的什麼值給它,程序在編譯時是沒法得知的,只有在運行到這裏的時候才能知道,這就叫運行時。
咱們來假設一個現實場景:
場景中,咱們把ILoginChecker這個接口
一、門衛是由保安公司派出的
二、保安公司根據每一個需求方的要求不一樣,配備不一樣的保安
三、保安公司擁有知足各類需求的保安
這樣一來,咱們就要再補充一個至關因而保安公司的類型了,用來建立ILoginChecker實例
因此咱們起個名字叫LoginCheckerFactory
裏面只有一個主要方法:CreateLoginChecker,根據咱們指定的名稱,返回一個具體的IloginChecker實例,咱們來看一下示例代碼:
class LoginCheckerFactory { public ILoginChecker CreateLoginChecker(string checkerName) { switch (checkerName) { case "Database": return new DatabaseLoginChecker(); //由數據庫完成的登陸驗證 case "WebService": return new WebServiceLoginChecker(); //由WebService完成的登陸驗證 case " TCP": return new TCPLoginChecker(); //由TCP通信完成的登陸驗證 default: throw new ApplicationException("不存在這個名稱的Checker實例"); } } }
此時,你會發現,登陸的斷定流程是依賴於一個字符串,Database或WebService或TCP。到了這一步,我想大部分人都明白,這個字符串只要寫在配置文件裏,就大功告成了。
而後項目發佈,在不一樣的運行環境中間,我只須要改一下配置文件,就能實現各類方式的登陸了。
對於更新與維護是一樣的便捷,你能夠把不一樣版本的ILoginChecker都放在這個工廠裏,而後根據外部的一些版本號來更改實例。
在BUG的修正中,你也沒必要直接修改類型自己,能夠拷備一個出來,好比叫WebServiceLoginChecker2,這樣即保證了程序原有的可運行性,又能夠進行BUG的修正,一旦出現了」動一發觸全身「的狀況,也很是容易全身而退。
這樣的設計思想、模式,咱們將其稱之爲工廠模式。工廠模式還分爲簡單工廠模式和抽象工廠模式,可是其最核心的思想,就是建立一個工廠,由工廠在運行時,進行動態的實例建立、返回。大大降底的類與類、模塊與模塊之間的耦合度,爲更新與維護提供了很是好的隔離環境。
經過第1、第二篇文章的學習,一種初步的構架思路已經產生。
一、分析當前方法要的主要事情
二、將可能存在變動的邏輯,創建接口,待之後實現
三、考慮到上述接口的實現多樣性,創建工廠類型,由工廠類型負責建立接口實例
利與弊的權衡
從作產品的角度考慮,一個好的基本構架是產品最核心的保障,有了這樣的保障,能夠說,除非到你換語種的那一天,不然永遠不會存在(推倒重來)的那一天,由於你的每個環節都被低耦合了,它們所有均可以被單獨替換。
從作項目的角度考慮,一個好的基本構架是項目中最耗時間、最耗成本的階段。在面向結果的項目負責人眼中,相對於可維護性進度纔是最重要的,因此在作項目這種狀況下,對使用架構應該進行一個速與質的權衡。
文章爲做者原創,轉載請標明出處,謝謝 http://www.cnblogs.com/ShimizuShiori/p/4929300.html