讓我們來看這樣一段代碼:
想象這樣一個場景,你是一個手機測試廠商,現(xiàn)在你需要測試所有的Iphone手機孤澎。但是具體的測試方法不一樣沉颂,比如Iphone6有了指紋識別,而Iphone4s沒有悦污。同時你需要考慮铸屉,未來還會新出Iphone8,Iphone9切端。
/*Iphone是一個抽象類彻坛,繼承它的有Iphone5s,Iphone6踏枣,Iphone7等
testIphone用來測試Iphone昌屉。
*/
Iphone testIphone(){
Iphone iphone = new Iphone();
iphone.powerOn();
iphone.takePic();
...
}
這段代碼真是好,體現(xiàn)了一個設計原則(面向接口編程而不是面向實現(xiàn)編程)這樣的話茵瀑,即使以后出來Iphone8间驮,Iphone9也不需要修改代碼。但是马昨,聰明的你看出來上面代碼的問題了嗎竞帽,抽象類不可以創(chuàng)建對象,我們從抽象程度再往下走鸿捧,具體應該這樣寫屹篓。
/*Iphone是一個抽象類,繼承它的有Iphone5s匙奴,Iphone6堆巧,Iphone7等
testIphone用來測試Iphone。
*/
Iphone testIphone(String type){
Iphone iphone;
if (type.equals("Iphone7")){
iphone = new Iphone7();
} else if (type.equals("Iphone8")){
iphone = new Iphone8();
} else if (type.equals("Iphone9")){
iphone = new Iphone9();
}
iphone.powerOn();
iphone.takePic();
...
}
運行倒是可以運行泼菌,但是以后如果Iphone7不在生產了谍肤,還需要去刪掉相關代碼,而且當新生產了Iphone10也得進行修改灶轰,這就違背了開閉原則(對修改關閉谣沸,對擴展開放)。怎么才能把前兩段代碼完美的結合起來呢笋颤,仔細觀察乳附,問題出在創(chuàng)建對象的時候,如果代碼一后面的 new Iphone()部分可以自動跟著type改變就好了伴澄。
設計原則:找出代碼中最可能變化的部分赋除,把他們獨立出來,不要和那些不變化的代碼混在一起非凌。
有了举农,我們把判斷的那一部分拿出來封裝成一個類,這樣testIphone根本不需要知道到底是哪個Iphone敞嗡,創(chuàng)建對象與使用對象完全解耦颁糟。
public Class SimpleIphoneFactory {
public static Iphone createIphone(String type){
Iphone iphone = null;
if (type.equals("Iphone7")){
iphone = new Iphone7();
} else if (type.equals("Iphone8")){
iphone = new Iphone8();
} else if (type.equals("Iphone9")){
iphone = new Iphone9();
}
return iphone;
}
}
/*Iphone是一個抽象類航背,繼承它的有Iphone5s,Iphone6棱貌,Iphone7等
testIphone用來測試Iphone玖媚。
*/
public Class TestIphone {
...
Iphone testIphone(String type){
Iphone = SimpleIphoneFactory.createIphone(type);
iphone.powerOn();
iphone.takePic();
...
}
}
通過創(chuàng)建工廠類來將對象的創(chuàng)建與對象的使用解耦,但你會說婚脱,這樣依舊違背了開閉原則啊今魔,當改變的時候,還是得在工廠類里面進行修改障贸。
沒錯错森,這時候我們繼續(xù)觀察,String type傳來的值其實可以與類名是一樣的篮洁,如果我們在工廠類里把創(chuàng)建實例的部分寫成創(chuàng)建傳來的相應字符串豈不是可以涩维?我們想到了利用反射機制
iphone = (Iphone)Class.forName(type).newInstance();
我們在想,在代碼中我們可以不需要修改type來改變袁波,可以去讀取一個配置文件激挪,這樣做的好處是當bean進行修改時不用重新編譯只需要更改配置文件就可以,聰明的你是否想到了Spring呢锋叨?沒錯垄分,Spring里的bean.xml就相當于是配置文件啦,而ApplicationContext就相當于是工廠娃磺。
ApplicationContext ctx = new FileSystemXmlApplicationContext("bean.xml");
Iphone iphone= null;
iphone= (Iphone) ctx.getBean("Iphone7");
簡單工廠模式中薄湿,我們經常應用到的就是獲得數(shù)據(jù)庫的連接啦,數(shù)據(jù)庫是抽象類偷卧,具體的mysql等是實體類豺瘤。DriverManager是工廠類。通過DriverManager的getConnection就可以獲得相應的連接了听诸。
//getConnection源碼
public static Connection getConnection(String url,
String user, String password) throws SQLException {
java.util.Properties info = new java.util.Properties();
if (user != null) {
info.put("user", user);
}
if (password != null) {
info.put("password", password);
}
return (getConnection(url, info, Reflection.getCallerClass()));
}