這兒有兩個關(guān)鍵點(diǎn):
1>工廠方法滿足開閉原則么?
2>工廠方法到底用在什么場合?
好像模式的書上都寫著工廠方法滿足開閉原則...
但是我認(rèn)為它并不滿足開閉, 不知道是不是我的認(rèn)識有錯誤
故再此發(fā)文一篇,權(quán)當(dāng)討論與提高.
哪么先從簡單工廠說起,好像所有的講設(shè)計模式的書籍里面工廠模式都是從簡單工廠開始的.
簡單工廠是公認(rèn)的部分支持開閉原則的.
眾所周知工廠模式就是為了解決New這個玩意兒的,先來一個沒有用到工廠的例子
import java.io.*;
interface ITest{
String testFun();
}
class TestA implements ITest{
public String testFun(){
return "A;testFun";
}
}
class TestB implements ITest{
public String testFun(){
return "B;testFun";
}
}
public class TestFactory{
public static void main(String args[]) throws IOException{
//聲明一個變量引用
ITest test = null;
//根據(jù)用戶輸入決定實(shí)例化具體的對象
BufferedReader sin = new BufferedReader(new InputStreamReader(System.in));
String line = sin.readLine();
if(line.equals("A")){
test = new TestA();
}
else if(line.equals("B")){
test = new TestB();
}
//使用對象
if (test != null){
System.out.println(test.testFun());
}
}
}
這兒使用了 new 具體的類,不滿足開閉原則
因?yàn)樾薷幕蛘咴黾?,new這個地方都要改動
比如說我們現(xiàn)在增加了一個類C,那么客戶端這兒就不可避免要改動.
如果這樣的邏輯在多個客戶端用到,就必須都做改動,顯然是不合理的.
基于 "對可變性的封裝" 的指導(dǎo)思想.
把new這個地方的工作封裝到一個類里面,于是就有了簡單工廠.
class SampleFactory{
public static ITest create(String ar){
if(ar.equals("A")){
return new TestA();
}
else if(ar.equals("B")){
return new TestB();
}
return null;
}
}
然后客戶端調(diào)用改為:
public static void main(String args[]) throws IOException{
//聲明一個變量引用
ITest test = null;
//根據(jù)用戶輸入決定實(shí)例化具體的對象
BufferedReader sin = new BufferedReader(new InputStreamReader(System.in));
String line = sin.readLine();
//調(diào)用簡單工廠創(chuàng)建對象
test = SampleFactory.create(line);
//使用對象
if (test != null){
System.out.println(test.testFun());
}
}
于是這兒就能適應(yīng)變化了,如果新加入了C類型,就不用去修改所有使用的客戶端了.客戶端滿足開閉了,
但是工廠又必須修改,而且吧其他類型的創(chuàng)建邏輯影響到了.那咋辦捏?
又基于"對可變性的封裝" 的指導(dǎo)思想,對每一個類型的New 都弄一個工廠,這樣有擴(kuò)展就不得影響
道其他工廠了, 這個就是工廠方法模式, 好像滿足了 開閉原則了,
interface IFactoryMathod{
ITest create();
}
class FactoryA implements IFactoryMathod{
public ITest create(){
return new TestA();
}
}
class FactoryB implements IFactoryMathod{
public ITest create(){
return new TestB();
}
}
然后 客戶端調(diào)用變成:
public static void main(String args[]) throws IOException{
//聲明一個變量引用
ITest test = null;
//聲明一個工廠
IFactoryMathod factory = null;
//根據(jù)用戶輸入決定實(shí)例化具體的對象
BufferedReader sin = new BufferedReader(new InputStreamReader(System.in));
String line = sin.readLine();
//調(diào)用工廠方法創(chuàng)建對象
if(line.equals("A")){
factory = new FactoryA();
}
else if(line.equals("B")){
factory = new FactoryB();
}
else{
factory = new FactoryB();
}
test = factory.create();
//使用對象
if (test != null){
System.out.println(test.testFun());
}
}
但是現(xiàn)在問題有來了,客戶端的這段代碼又瓜了.對客戶端來說,又不滿足開閉了.
我們轉(zhuǎn)了一圈結(jié)果發(fā)現(xiàn)又回到開始的地方了...暈??
哪么我認(rèn)為:工廠方法仍然不滿足開閉, 因?yàn)橹灰诖a里面用了New ,就不可能滿足開閉,如果有添加
總要修改到現(xiàn)有代碼的某一個部分.
這兒就需要引入反射,消除了New,從配置文件里面讀取需要創(chuàng)建的對象.比如說Spring就是這么干的.
然后說了半天,從上面的這個例子好像發(fā)現(xiàn),那個工廠方法沒什么用處得,完全就是用一個方法把new
包起來,大大的有脫褲子放屁的嫌疑..
到底怎么回事情啦,不可能大名頂頂?shù)墓S方法就這樣子??
哪么就進(jìn)入下面的一個論點(diǎn): 工廠方法的使用場合
好像有了反射.工廠方法就可以下崗了...
的確,我認(rèn)為僅僅在工廠方法里面寫一個New XXXX 的話,哪么這樣用工廠方法的確快下崗了..
但是我覺得真正體現(xiàn)工廠方法的意義的在這兒:
還是 拿出 我們的指導(dǎo)思想(馬克思列寧思想...哈哈哈) "對可變性的封裝"
當(dāng)對象的創(chuàng)建不僅僅是一個new XXXX,包含復(fù)雜的業(yè)務(wù)邏輯,而且可以面臨巨大的變動.
比如說: 有個需求是對象的創(chuàng)建需要是從文件序列化出來,如果沒有才New一個,屬性設(shè)置為默認(rèn),
萬一那天有要求分布式應(yīng)用,不能從文件里面反序列,需要改為從數(shù)據(jù)庫里面找到屬性并創(chuàng)建一個出來.
總不可能在每個調(diào)用這個類的客戶端端口都去寫上這么一大堆 if else的業(yè)務(wù)邏輯三.
如果以后再有改動,不就麻煩了..
這個時候就需要工廠方法,把可變的封裝到工廠方法里面,
如果以后有變動或者增加,我們就只是需要修改或者擴(kuò)展具體的工廠方法類,其他的都不會受到干擾.
再結(jié)合反射,將工廠方法類放到配置文件,這樣就能真正的 滿足開閉原則.
可能有同學(xué)有提出來問題:我何不在具體類的構(gòu)造函數(shù)里面去做哪么一大堆啦.這樣有改動的化,
我修改一下那個構(gòu)造函數(shù)不久得了.反正用了反射,仍然滿足開閉原則.
但是如果那個地方的代碼變動的可能很大,你修改了構(gòu)造函數(shù),哪么這個類的其他地方就受到干擾了
站在這個問題的角度說,就是: 當(dāng)一個類的構(gòu)造邏輯頻繁變動,哪么就需要把他封裝,于是把這兒的邏輯放到工廠方法里面了.
這兒有體現(xiàn)了 "對可變性的封裝" 的思想
工廠方法模式并不是因?yàn)楹唵喂S方法不滿足開閉原則而引入
而是因?yàn)?類的構(gòu)造邏輯復(fù)雜且多變,為了將構(gòu)造邏輯封裝而引入
哪么現(xiàn)在得出結(jié)論:
1>工廠方法并不真正滿足開閉原則,但是結(jié)合反射和配置文件能夠滿足.
2>工廠方法使用場合 : 類的構(gòu)造邏輯復(fù)雜且多變,為了將構(gòu)造邏輯封裝而引入
如果僅僅是在工廠方法里面寫一個New,而且也不會發(fā)生變化.就沒有使用工廠方法的意義了.
然后還有這個 思想我覺得比較重要
"對可變性的封裝",或者說 "對不確定性的封裝"
OOAD的核心思想啊!!!
歡迎討論...寫這些的目的就是為了共同進(jìn)步,有什么錯誤或者不足,歡迎指出....