本文介紹了抽象工廠模式的概念,UML類圖,優缺點,實現方式以及(未)遵循的OOP原則。同時結合J2EE中常用的DAO實例詳解了抽象工廠模式的實現。 ...
原創文章,同步發自作者個人博客,轉載請註明出處 http://www.jasongj.com/design_pattern/abstract_factory/
抽象工廠模式解決的問題
上文《工廠方法模式》中提到,在工廠方法模式中一種工廠只能創建一種具體產品。而在抽象工廠模式中一種具體工廠可以創建多個種類的具體產品。
抽象工廠模式
抽象工廠模式介紹
抽象工廠模式(Factory Method Pattern)中,抽象工廠提供一系列創建多個抽象產品的介面,而具體的工廠負責實現具體的產品實例。抽象工廠模式與工廠方法模式最大的區別在於抽象工廠中每個工廠可以創建多個種類的產品。
抽象工廠模式類圖
抽象工廠模式類圖如下 (點擊可查看大圖)
抽象工廠模式角色劃分
- 抽象產品(或者產品介面),如上文類圖中的IUserDao,IRoleDao,IProductDao
- 具體產品,如PostgreSQLProductDao
- 抽象工廠(或者工廠介面),如IFactory
- 具體工廠,如果MySQLFactory
- 產品族,如Oracle產品族,包含OracleUserDao,OracleRoleDao,OracleProductDao
抽象工廠模式使用方式
與工廠方法模式類似,在創建具體產品時,客戶端通過實例化具體的工廠類,並調用其創建目標產品的方法創建具體產品類的實例。根據依賴倒置原則,具體工廠類的實例由工廠介面引用,具體產品的實例由產品介面引用。具體調用代碼如下
package com.jasongj.client;
import com.jasongj.bean.Product;
import com.jasongj.bean.User;
import com.jasongj.dao.role.IRoleDao;
import com.jasongj.dao.user.IUserDao;
import com.jasongj.dao.user.product.IProductDao;
import com.jasongj.factory.IDaoFactory;
import com.jasongj.factory.MySQLDaoFactory;
public class Client {
public static void main(String[] args) {
IDaoFactory factory = new MySQLDaoFactory();
IUserDao userDao = factory.createUserDao();
User user = new User();
user.setUsername("demo");
user.setPassword("demo".toCharArray());
userDao.addUser(user);
IRoleDao roleDao = factory.createRoleDao();
roleDao.getRole("admin");
IProductDao productDao = factory.createProductDao();
Product product = new Product();
productDao.removeProduct(product);
}
}
抽象工廠模式案例解析
本文所述抽象工廠模式示例代碼可從作者Github下載
上例是J2EE開發中常用的DAO(Data Access Object),操作對象(如User和Role,對應於資料庫中表的記錄)需要對應的DAO類。
在實際項目開發中,經常會碰到要求使用其它類型的資料庫,而不希望過多修改已有代碼。因此,需要為每種DAO創建一個DAO介面(如IUserDao,IRoleDao和IProductDao),同時為不同資料庫實現相應的具體類。
調用方依賴於DAO介面而非具體實現(依賴倒置原則),因此切換資料庫時,調用方代碼無需修改。
這些具體的DAO實現類往往不由調用方實例化,從而實現具體DAO的使用方與DAO的構建解耦。實際上,這些DAO類一般由對應的具體工廠類構建。調用方不依賴於具體工廠而是依賴於抽象工廠(依賴倒置原則,又是依賴倒置原則)。
每種具體工廠都能創建多種產品,由同一種工廠創建的產品屬於同一產品族。例如PostgreSQLUserDao,PostgreSQLRoleDao和PostgreSQLProductDao都屬於PostgreSQL這一產品族。
切換資料庫即是切換產品族,只需要切換具體的工廠類。如上文示例代碼中,客戶端使用的MySQL,如果要換用Oracle,只需將MySQLDaoFactory換成OracleDaoFactory即可。
抽象工廠模式優點
- 因為每個具體工廠類只負責創建產品,沒有簡單工廠中的邏輯判斷,因此符合單一職責原則。
- 與簡單工廠模式不同,抽象工廠並不使用靜態工廠方法,可以形成基於繼承的等級結構。
- 新增一個產品族(如上文類圖中的MySQLUserDao,MySQLRoleDao,MySQLProductDao)時,只需要增加相應的具體產品和對應的具體工廠類即可。相比於簡單工廠模式需要修改判斷邏輯而言,抽象工廠模式更符合開-閉原則。
抽象工廠模式缺點
- 新增產品種類(如上文類圖中的UserDao,RoleDao,ProductDao)時,需要修改工廠介面(或者抽象工廠)及所有具體工廠,此時不符合開-閉原則。抽象工廠模式對於新的產品族符合開-閉原則而對於新的產品種類不符合開-閉原則,這一特性也被稱為開-閉原則的傾斜性。
抽象工廠模式與OOP原則
已遵循的原則
- 依賴倒置原則(工廠構建產品的方法均返回產品介面而非具體產品,從而使客戶端依賴於產品抽象而非具體)
- 迪米特法則
- 里氏替換原則
- 介面隔離原則
- 單一職責原則(每個工廠只負責創建自己的具體產品族,沒有簡單工廠中的邏輯判斷)
- 開閉原則(增加新的產品族,不像簡單工廠那樣需要修改已有的工廠,而只需增加相應的具體工廠類)
未遵循的原則
- 開閉原則(雖然對新增產品族符合開-閉原則,但對新增產品種類不符合開-閉原則)
Java設計模式系列
- Java設計模式(一) 簡單工廠模式不簡單
- Java設計模式(二) 工廠方法模式
- Java設計模式(三) 抽象工廠模式
- Java設計模式(四) 觀察者模式
- Java設計模式(五) 組合模式
- Java設計模式(六) 代理模式 VS. 裝飾模式
- Java設計模式(七) Spring AOP JDK動態代理 vs. cglib
- Java設計模式(八) 適配器模式
- Java設計模式(九) 橋接模式
- Java設計模式(十) 你真的用對單例模式了嗎?
- Java設計模式(十一) 享元模式
- Java設計模式(十二) 策略模式