簡單工廠、工廠方法和抽象工廠的總結

首先,放上項目github地址: https://github.com/codethereforam/java-design-patterns, 我是用java實現的html

1、前言

題目中的這三個設計模式屬於建立型模式,做用是爲了抽象實例化過程java

我以前學過這三個設計模式,但最近發現又沒法釐清這三個的區別了,爲了不下次又忘了,因而想動手記錄下來。mysql

可能有同窗有疑問,提早說一下,下面所展現的類圖IDEA自帶插件UML Support自動生成,而時序圖由插件SequencePlugin自動生成。若是有同窗對類圖和時序圖還不瞭解,請先google自學一下。git

下面我結合模擬場景總結一下這三個模式,具體代碼請點擊本文開頭的github連接。github

2、簡單工廠

模擬場景:一個用戶管理系統,假設只有一張User表。原本用的mysql,但需求忽然發生變化,如今要用Oralce,因爲這兩個數據庫的SQL語句有些差異,須要重寫數據庫層面的代碼,如今要求系統能夠靈活切換數據庫。sql

關鍵代碼:數據庫

public class UserDAOFactory {
    //靜態工廠方法
    public static UserDAO createUserDAO(String database) {
        UserDAO userDAO = null;
        switch (database) {
            case "mysql":
                userDAO = new UserDAOMysqlImpl();
                break;
            case "oracle":
                userDAO = new UserDAOOracleImpl();
                break;
            default:
        }
        return userDAO;
    }
}

分析:若是如今要改用SQL server數據庫,須要添加一個UserDAOSqlserverImpl,而後在UserDAOFactory類的createUserDAO方法中添加一個case,這顯然違背了開閉原則設計模式

  • 特色
    • 工廠類包含必要的邏輯判斷來選擇生產具體產品
    • 用於生產單個產品
  • 優勢
    • 去除客戶端與具體產品的依賴
  • 缺點
    • 添加產品須要修改工廠類,違背開閉原則
  • 角色
    • 抽象產品(UserDAO)
    • 具體產品(UserDAOMysqlImpl & UserDAOOracleImpl)
    • 工廠(UserDAOFactory)

3、工廠方法

模擬場景:和上述簡單工廠模擬場景同樣oracle

分析:若是如今要改用SQL server數據庫,則需添加一個UserDAOSqlserverImpl和相應的工廠UserDAOFactorySqlserverImpl,再更改Main中的實例化代碼,這知足了開閉原則,擴展很方便。但若是支持的數據庫一多,那工廠就會氾濫。google

  • 特色
    • 一個產品對應一個工廠類
    • 用於生產某種類型產品
  • 優勢
    • 方便添加新產品
    • 添加新產品只需添加相應工廠類,符合開閉原則
  • 缺點
    • 產品多時,工廠氾濫
  • 角色
    • 抽象產品(UserDAO)
    • 具體產品(UserDAOMysqlImpl & UserDAOOracleImpl)
    • 抽象工廠(UserDAOFactory)
    • 具體工廠(UserDAOFactoryMysqlImpl & UserDAOFactoryOracleImpl)

4、抽象工廠

模擬場景:在以前的場景基礎上,若是系統原本還有一個日誌表,是用來記錄日誌的。

分析:若是如今要改用SQL server數據庫,則需添加UserDAOSqlserverImplLogDAOSqlserverImplDAOFactorySqlserverImpl,再更改Main中的實例化代碼。但若是如今要添加一個其餘的表,那麼就要改DAOFactory接口和接口中方法的實現,要改動的地方太多。

  • 特色
    • 用於生產一系列產品
  • 優勢
    • 易於改變工廠生產行爲,產生新的產品系列
    • 具體建立過程與客戶端分離,客戶端經過接口操縱實例(factory1.createUserDAO().add())
  • 缺點
    • 添加新產品,要修改抽象工廠接口、具體工廠,改動太多
  • 角色
    • 抽象產品(UserDAO & LogDAO)
    • 具體產品(UserDAOMysqlImpl & UserDAOOracleImpl & LogDAOMysqlImpl & LogDAOOracleImpl)
    • 抽象工廠(DAOFactory)
    • 具體工廠(DAOFactoryMysqlImpl & DAOFactoryOracleImpl)

5、用簡單工廠改進抽象工廠

關鍵代碼(選取DataAccess):

public UserDAO createUserDAO() {
        UserDAO userDAO = null;
        switch (database) {
            case MYSQL:
                userDAO = new UserDAOMysqlImpl();
                break;
            case ORACLE:
                userDAO = new UserDAOOracleImpl();
                break;
            default:
        }
        return userDAO;
}

分析:與抽象工廠相比,該方法減小了三個類,添加了一個DataAccess類,類的數量減小了,系統複雜性下降。若是如今要改用SQL server數據庫,則需添加UserDAOSqlserverImplLogDAOSqlserverImpl,而後在DataAccess類中的createUserDAO方法和createLogDAO方法分別添加一個case,這違背了開閉原則。

  • 特色
    • 用DataAccess取代抽象工廠和具體工廠
    • DataAccess經過判斷控制生產行爲
  • 優勢
    • 減小類
  • 缺點
    • 添加新產品系列,要改動DataAccess中的switch-case

6、用反射改進抽象工廠

關鍵代碼(選取DataAccess):

public static final String PACKAGE_NAME = DataAccess.class.getPackage().getName();

public UserDAO createUserDAO() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        String className = PACKAGE_NAME + ".UserDAO" + database + "Impl";
        return (UserDAO) Class.forName(className).newInstance();
}

分析:若是要換數據庫,則無需修改DataAccess類中的代碼。若是要添加表,只須要添加一個抽象產品接口和兩個具體產品實現,而後在DataAccess中添加一個create**方法,擴展起來很是方便。

  • 優勢
    • 減小類
    • 解決抽象工廠添加產品改動較多的問題,方便擴展
  • 可以使用配置文件繼續完善

7、總結

若是你仔細看到這,你可能會以爲我例子舉的不恰當,哪裏有系統只有一個表的呢,前面的場景直接考慮抽象工廠就好了。我認可我舉的例子有問題,以前寫代碼時沒有發現,應該是當時理解的還不夠深刻。

簡單工廠和工廠方法的模擬場景應該改成:系統原本有一直表,但如今要添加表,而不是換數據庫。而抽象工廠的模擬場景應該改成:在上述的基礎上要換數據庫。若是你理解了三個模式,我想這兩個模擬場景你應該也知道怎麼實現了。

本文的例子我參考了大話設計模式,但其餘代碼和文字是我本身的理解。若是有錯誤,望各位不吝賜教,在評論區指出。

8、參考資料

相關文章
相關標籤/搜索