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


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

一、前言

題目中的這三個設計模式屬於創建型模式,作用是為了抽象實例化過程

我之前學過這三個設計模式,但最近發現又無法厘清這三個的區別了,為了避免下次又忘了,於是想動手記錄下來。

可能有同學有疑問,提前說一下,下面所展示的類圖IDEA自帶插件UML Support自動生成,而時序圖由插件SequencePlugin自動生成。如果有同學對類圖和時序圖還不了解,請先google自學一下。

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

二、簡單工廠

模擬場景:一個用戶管理系統,假設只有一張User表。本來用的mysql,但需求突然發生變化,現在要用Oralce,由於這兩個數據庫的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)

三、工廠方法

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

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

  • 特點

    • 一個產品對應一個工廠類
    • 用於生產某種類型產品
  • 優點

    • 方便添加新產品
    • 添加新產品只需添加相應工廠類,符合開閉原則
  • 缺點

    • 產品多時,工廠泛濫
  • 角色

    • 抽象產品(UserDAO)
    • 具體產品(UserDAOMysqlImpl & UserDAOOracleImpl)
    • 抽象工廠(UserDAOFactory)
    • 具體工廠(UserDAOFactoryMysqlImpl & UserDAOFactoryOracleImpl)

四、抽象工廠

模擬場景:在之前的場景基礎上,如果系統本來還有一個日志表,是用來記錄日志的。

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

  • 特點

    • 用於生產一系列產品
  • 優點

    • 易於改變工廠生產行為,產生新的產品系列
    • 具體創建過程與客戶端分離,客戶端通過接口操縱實例(factory1.createUserDAO().add())
  • 缺點

    • 添加新產品,要修改抽象工廠接口、具體工廠,改動太多
  • 角色

    • 抽象產品(UserDAO & LogDAO)
    • 具體產品(UserDAOMysqlImpl & UserDAOOracleImpl & LogDAOMysqlImpl & LogDAOOracleImpl)
    • 抽象工廠(DAOFactory)
    • 具體工廠(DAOFactoryMysqlImpl & DAOFactoryOracleImpl)

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

關鍵代碼(選取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

六、用反射改進抽象工廠

關鍵代碼(選取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**方法,擴展起來非常方便。

  • 優點

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

七、總結

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

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

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

八、參考資料


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM