設計模式筆記(一)——設計模式的引入與三大工廠模式

1. 設計模式的引入

1.1  什麼是設計模式?

設計模式(Design Pattern)是軟件開發人員在軟件開發過程中面臨的一般問題的解決方案。這些解決方案是衆多軟件開發人員經過相當長的一段時間的試驗和錯誤總結出來的。

設計模式是一套被反覆使用的、多數人知曉的、經過分類編目的、代碼設計經驗的總結。使用設計模式是爲了重用代碼、讓代碼更容易被他人理解、保證代碼可靠性。

毫無疑問,設計模式於己於他人於系統都是多贏的,設計模式使代碼編制真正工程化,設計模式是軟件工程的基石,如同大廈的一塊塊磚石一樣。項目中合理地運用設計模式可以完美地解決很多問題,每種模式在現實中都有相應的原理來與之對應,每種模式都描述了一個在我們周圍不斷重複發生的問題,以及該問題的核心解決方案,這也是設計模式能被廣泛應用的原因。

1.2  爲什麼要用設計模式?

使用設計模式的目的:爲了代碼可重用性、讓代碼更容易被他人理解、保證代碼可靠性。

設計模式使代碼編寫真正工程化;設計模式是軟件工程的基石脈絡,如同大廈的結構一樣。

1.3  設計模式之間的關係


2. 設計模式的六大原則

2.1  開放-封閉原則(Open Close Principle)

開放-封閉原則的意思是:對擴展開放,對修改關閉。在程序需要進行拓展的時候,不能去修改原有的代碼,實現一個熱插拔的效果。簡言之,是爲了使程序的擴展性好,易於維護和升級。

考過60分是必須的,但怎麼考過(遵守紀律、小抄、手機。。。),是可以擴展的。

2.2  里氏代換原則(Liskov Substitution Principle)

里氏代換原則中說,任何基類可以出現的地方,子類一定可以出現。里氏代換原則是對開放-封閉原則的補充。實現開放-封閉原則的關鍵步驟就是抽象化,而基類與子類的繼承關係就是抽象化的具體實現,所以里氏代換原則是對實現抽象化的具體步驟的規範。

Map接口可以用的地方,HashMap一定也可以用,LinkedHashMap自然也不用多說。

2.3  依賴倒轉原則(Dependence Inversion Principle)

這個原則是開放-封閉原則的基礎,具體內容:針對接口編程,而不是對實現編程。或者可以這樣說:抽象不應該依賴於細節,細節應該依賴於抽象(GoF的原話)。

電腦的主板上有許多接口,而不是把硬件焊接在主板上。

2.4  接口隔離原則(Interface Segregation Principle)

這個原則的意思是:使用多個隔離的接口,比使用單個接口要好。它還有另外一個意思是:降低類之間的耦合度。由此可見,其實設計模式就是從大型軟件架構出發、便於升級和維護的軟件設計思想,它強調降低依賴,降低耦合。

中央集權強調所有的事務都要到京城辦理,而地方分權相當於多個地方把中央政府的權利分解成若干項,每個地方只負責其中一塊內容。

2.5  迪米特法則(Demeter Principle)

迪米特法則,又稱最少知道原則,它是指:一個實體應當儘量少地與其他實體之間發生相互作用,使得系統功能模塊相對獨立

你媽媽告訴你,只跟自己熟的人說話,而不要跟陌生人說話。

2.6  合成/聚合複用原則(Composite Reuse Principle)

合成/聚合複用原則是指:儘量使用合成/聚合的方式,而不是使用繼承。

羊身上有毛,羊和羊毛的關係就是合成。如果羊沒了,羊毛也就沒了。

羊羣是由多隻羊“聚合”而成,屬於數據庫概念中的一對多。

不同的手機軟件,具有不同的功能。通過下載多個不同功能的軟件(而不是手機繼承一個手機的概念類,實現所有功能),完成日常操作需要。

2.7  網上的其他說法

l  開放-封閉原則:實現熱插拔,提高擴展性

l  里氏代換原則:實現抽象的規範,實現子父類互相替換

l  依賴倒轉原則:針對接口編程,實現開放-封閉原則的基礎

l  接口隔離原則:降低耦合度,接口單獨設計,互相隔離

l  迪米特法則:功能模塊儘量獨立

l  合成複用原則:儘量使用聚合,組合,而不是繼承

 

3. UML類圖的使用規則

UML:統一建模語言,是用來對軟件密集系統進行可視化建模的一種語言。

詳細的類圖使用可以參考如下鏈接(百度一搜一大堆):

http://www.uml.org.cn/oobject/201610282.asp

 

這裏只給出一個通用圖,其中已經包括了比較全的類圖使用規則:


網上有比較多的UML類圖設計工具,其實在MyEclipse中已經集成了插件(手動滑稽)


4. 三大工廠模式

明白了基本原則,下面再對三大工廠模式進行回顧:

4.1  簡單工廠模式

簡單工廠模式的UML類圖應該是最簡單的:


查詢類中只需要簡單判斷。

public IDBUtil createQuery(String dbName) {
    IDBUtil util;
    switch (dbName) {
        case "MySQL":
            util = new MySQLDemo();
            break;
        case "MSSQL":
            util = new MSSQLDemo();
            break;
        default:
            util = null;
            break;
    }
    return util;
}


客戶端中只需要傳入要使用的數據庫,就可以實現查詢效果。

如果需要改實現類,只需要改switch的判斷主體即可:

public static void main(String[] args) {
    Query query = new Query();
    IDBUtil util = query.createQuery("MySQL");
    Integer result = util.get(5);
    System.out.println(result);
}


但這種操作已經違反了開放-封閉原則!需要改進!如果需要添加Oracle數據庫,只需要新建一個類,實現IDBUtil接口,再在Query中添加case分支判斷,Client中修改查詢的數據庫即可。

 

4.2  工廠方法模式

工廠方法模式的概念:

定義一個用於創建對象的接口,讓子類決定實例化哪一個接口的實現類。

工廠方法模式可以讓創建對象的過程延遲到子類中進行。

 

——————用工廠方法模式改進上面的數據庫查詢器——————

UML圖:


查詢工廠QueryFactory有兩個子類,每個子類只能創建它對應依賴的查詢對象。

關鍵服務器代碼:

public interface QueryFactory {
    IDBUtil createQuery();
}

public class MySQLFactory implements QueryFactory {
    public IDBUtil createQuery() {
        return new MySQLDemo();
    }
}

public class OracleFactory implements QueryFactory {
    public IDBUtil createQuery() {
        return new OracleDemo();
    }
}

public class MySQLDemo implements IDBUtil {
    public Integer getEmployee(Integer uuid) {
        return uuid;
    }
}

 

客戶端只需要用工廠即可:

QueryFactory factory = new MySQLFactory();
IDBUtil util = factory.createQuery();


如果需要修改查詢的數據庫,只需要改工廠即可。

如果需要增加新的數據庫查詢,只需要新建一個工廠,再新建查詢類,讓工廠依賴查詢類,最後修改上面的工廠即可。再配合反射,已經完成了不同數據庫之間的切換。(關於反射和配置文件的部分比較簡單,這裏不再贅述)

 

但發現現在的設計只能滿足查詢一個表,如果要查詢的是多個表,工廠方法模式就無法完成了。

 

4.3  抽象工廠模式

抽象工廠模式的概念:

提供一個創建一系列相關的或者相互依賴對象的接口,而無需指定它們具體的類。

 

這種模式使得以後操作的都是接口的方法,實際創建的對象客戶端也不知道。

 

——————用抽象工廠模式改寫兩個表的查詢——————

UML圖:

 

一個最終的工廠可以創建多個子工廠(對應多個數據庫),最終工廠中定義了可以查詢多個數據庫表的抽象方法,每個子工廠分別實現這些抽象方法並映射到不同的實際查詢類中,再由查詢類調用它們的查詢方法執行結果。

這樣的好處:客戶端無需關心服務器的查詢實現,只需要用工廠創建即可。

服務器的每個抽象QueryUtil都是對應一個表,而這些接口的實現類就是分別對應不同數據庫的實現(很像多對多)。

 

關鍵代碼:

public interface DepartmentQueryUtil {
    Integer get(Integer uuid);
}

public interface EmployeeQueryUtil {
    String get(String uuid);
}

public class MySQLDepartmentQuery implements DepartmentQueryUtil {
    public Integer get(Integer uuid) {
        return uuid;
    }
}

public class OracleDepartmentQuery implements DepartmentQueryUtil {
    public Integer get(Integer uuid) {
        return uuid;
    }
}

public class MySQLEmployeeQuery implements EmployeeQueryUtil {
    public String get(String uuid) {
        return uuid;
    }
}

public interface AbstractFactory {
    DepartmentQueryUtil createDepartmentQueryFactory();
    EmployeeQueryUtil createEmployeeQueryFactory();
}

public class MySQLFactory implements AbstractFactory {
    public DepartmentQueryUtil createDepartmentQueryFactory() {
        return new MySQLDepartmentQuery();
    }
    public EmployeeQueryUtil createEmployeeQueryFactory() {
        return new MySQLEmployeeQuery();
    }
}

public class Client {
    public static void main(String[] args) throws Exception {
        Properties properties = new Properties();
        properties.load(new InputStreamReader(new FileInputStream("db.properties"), "UTF-8"));
        AbstractFactory factory = (AbstractFactory) Class.forName(properties.getProperty("db")).newInstance();
        DepartmentQueryUtil departmentQuery = factory.createDepartmentQueryFactory();
        Integer result = departmentQuery.get(3);
        System.out.println(result);
    }
}

//配置文件的內容:
db=design.抽象工廠模式.MySQLFactory


配合配置文件後,只需要在配置文件中寫入要實例化的工廠,剩下的所有事情都是接口操作了,沒有任何類的操作

 

抽象工廠模式幾乎完美的體現了開放-封閉原則、依賴倒轉原則

  • 要修改數據庫的時候,只需要改配置文件,不關係到程序!
  • 要增加新的表查詢,只需要增加新的QueryUtil接口和對應的實現類,再把Factory類增加新的使用方法,客戶端就可以進行正常調用!
    • 注意這裏是新增方法,而不是修改,所以符合開放-封閉原則
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章