這里又出現了一個抽象工廠模式,這個抽象工廠模式又是什么呢?
我們現在來模擬一個場景,現在用的是Mysql數據庫,明天讓你更換為Oracle數據庫。此時,想想要做多少的改動。但我們如果用工廠模式,這會讓你節省大量時間。
首先,我們用工廠方法模式來設計這個程序。
我們畫出類的UML圖。

IFactory作為工廠類的接口,有兩個子類,分別用來構造不同的實例。
IFactory工廠接口代碼如下:
package day_3_facoryMethod_db; /** * 數據庫工廠類 * @author turbo * * 2016年9月6日 */ public interface IFactory { IUser createUser(); }
MysqlFactory代碼如下,省去OracleFactory。
package day_3_facoryMethod_db; /** * @author turbo * * 2016年9月6日 */ public class MysqlFactory implements IFactory { /* (non-Javadoc) * @see day_3_facoryMethod_db.IFactory#createUser() */ @Override public IUser createUser() { return new MysqlUser(); } }
IUser是對User表操作的接口,不同數據庫的操作只需繼承實現它即可。
package day_3_facoryMethod_db; /** * 操作數據庫User表的接口 * @author turbo * * 2016年9月6日 */ public interface IUser { void insert(User user); User getUserById(int userId); }
MysqlUser是對IUser接口的實現,是對User表操作的具體實現。省去OracleUser。
package day_3_facoryMethod_db; /** * Mysql對User表的操作 * @author turbo * * 2016年9月6日 */ public class MysqlUser implements IUser { /* (non-Javadoc) * @see day_3_facoryMethod_db.IUser#insert(day_3_facoryMethod_db.User) */ @Override public void insert(User user) { System.out.println("插入一條數據"); } /* (non-Javadoc) * @see day_3_facoryMethod_db.IUser#getUserById(int) */ @Override public User getUserById(int userId) { System.out.println("獲取一條數據"); return null; } }
現在通過客戶端代碼來觀察是如何做到業務邏輯和數據訪問的解耦的。
package day_3_facoryMethod_db; /** * 客戶端 * @author turbo * * 2016年9月6日 */ public class Main { public static void main(String[] args){ IFactory factory = new MysqlFactory(); //如果需要修改數據庫,則只需將new MysqlFactory()修改為new OracleFactory()。 IUser iu = factory.createUser(); iu.insert(new User()); iu.getUserById(1); } }
如果要更改數據庫,則只需將new MysqlFactory()修改為new OracleFactory()即可,這就是所謂的業務邏輯和數據訪問的解耦。
上面我們實際上重新回顧了工廠方法模式,似乎已經達到了我們想要的效果。但是,數據庫里不止一張表,兩個數據庫又是兩大不同分類,解決這種涉及多個產品系列的問題,有一個專門的工廠模式叫抽象工廠模式。所以實際上,如果增加一個新表,上面的工廠方法模式就有了一個新的名字——抽象工廠模式。
抽象工廠模式:提供一個創建一些列有關或互相依賴對象的接口,而無需制定它們具體的類。
下面我們進階一下:用反射+抽象工廠的方式來設計這個程序。
是否記得在簡單工廠模式中,我們用到了switch或者if。有用到switch和if的地方,我們都可以考慮利用反射技術來去除,以解除分支帶來的耦合。
這其實將會引申一個概念:依賴注入(DI),或者稱為控制反轉,將傳統上由程序代碼直接操控的對象的調用權交給容器,通過容器來實現對象組件的裝配和管理,什么意思?就是直觀上代碼里看不到new 對象,這個操作交給了外部容器,這樣將會大大降低程序的耦合性。比如:Spring框架的IoC容器。
我們先來看如果要實例化上面的的IUser會怎么做?
IUser iu = new MysqlUser();
如果是用反射呢?
Class classType = Class.forName("day_3_facoryMethod_db.MysqlUser"); //“類所在的包名.類名”
Object obj = classType.newInstance();
我們對obj做驗證。
System.out.println(obj instanceof MysqlUser);
輸出為true,用反射機制成功實例化對象。
反射和直接new有什么區別呢?答案就在於:反射使用的字符串,也就是說可以用變量來處理。而new的常規方法是已經編譯好了的,不能隨意靈活更換其實例化對象。所以,思考,如果我們用配置文件的方式,是不是能靈活替換我們想要的實例化對象呢?
