工廠設計模式有什麼用?

感覺工廠模式沒什麼用,就是把new包了一層,new對象時,本來就必須知道具體的類,包一層後,變成了必須知道具體的工廠類,這真是難以明白,無非就是把new對象時的構造函數和參數屏蔽了,這個模式就為了干這個??


工廠模式並不僅僅是用來new出一個類的對象的。

簡單工廠確實如題主的描述所說,是一個工廠對應一個類的關係。

在這裡,可以舉出一些複雜的例子,與題主的描述不同。

假設有代碼包A和代碼包B,

代碼包B是代碼包A的調用者,A向B暴露介面InterfaceA。

在A的內部結構中,實現了InterfaceA的有ClassA1,ClassA2,ClassA3,……ClassA100。

但是B並不關心這些,因為對於B來說,A的功能只有一個,就是InterfaceA。

這個時候,B想要使用一個InterfaceA的實現,想要new一個出來,但又不想與代碼包A中的複雜的構造邏輯耦合,怎麼辦?

只能向代碼包A中傳遞參數,交給代碼包A自己選擇到底是那個ClassA1還是A100被new出來。

而這個對構造過程進行選擇的邏輯,就是工廠

當然了,我這裡舉的例子是InterfaceA,你也可以用AbstractClassA之類的。

工廠在這裡面起的作用,就是隱藏了創建過程的複雜度,以配合InterfaceA對那一百個子類的複雜度進行隱藏,這樣B只要知道上轉型之後的InterfaceA即可,簡單清晰。


工廠模式的確是讓初學者比較費解的模式。

首先它有簡單工廠和抽象工廠之分。但真正算得上設計模式的,是抽象工廠。而簡單工廠說實在的僅僅是比較自然的再封裝。所以很多教科書會顯擺自己的內涵大幅度的介紹抽象工廠,而有意無意的忽略了簡單工廠。

實際的情況又正好相反,簡單工廠幾乎每個人都會用得上,抽象工廠大部分人一輩子都用不上一次。

下面回答你的問題:簡單工廠封裝了new到底有啥好處?

在實際的項目中,在你通往架構師的道路上,你要培養出一種感覺:要new一個實體對象是件很謹慎的事情(不是指值對象),不要隨便new。最好不要自己new,讓別人去new,傳給你去調用。這樣new錯了也是別人的事,換而言之你的模塊是好質量的,禁得起推敲的。那麼都不願意去new,誰去new?讓專門的一個工廠去new。請注意:這僅僅是解決new的方式之一,此外還要反射啊等等。那你必須要等到真正接觸大型項目才能有體味的。


1.隱藏具體類名,很多類隱藏得很深的,而且可能會在後續版本換掉

2.避免你辛苦的準備構造方法的參數

3.這個工廠類可以被配置成其它類

4.這個工廠對象可以被傳遞

最後來句重要的

new XXX其實也是一種硬編碼!!!


覺得你問的這個問題很有意義,很多人對設計模式都不能靈活的應用

下面我從分析問題的角度,談談為什麼要使用工廠類的設計模式

第 1 個問題:我們經常一些功能類似的類,所以我們的思路是對進行抽象,使用介面暴露公共的方法,使用抽象類來提供公共的實現。

public interface IProduct {
void print(); // 這是要暴露的方法
}

public abstract class AbstractProduct implements IProduct {
protected void printBefore(){
System.out.println("before print"); // 這裡所公共的實現
}
}

public class AProduct extends AbstractProduct {
private String name;
public AProduct(String name){
this.name = name;
}
@Override
public void print() {
this.printBefore();
System.out.println("print A &>&>&>"+name);
}
}

public class BProduct extends AbstractProduct {
private String name;
public BProduct(String name){
this.name = name;
}
@Override
public void print() {
this.printBefore();
System.out.println("print B &>&>&>"+name);
}
}

第 2 個問題:這些功能類似的類的實例化成為了一個問題,每個類的構造方法參數還不一樣,每次 new 對象很麻煩,封裝成簡單工廠模式

public class SimpleFactory {
public static IProduct getProduct(String name){
if("A".equals(name)){
return new AProduct(name);
}else if("B".equals(name)){
return new BProduct(name);
}else{
throw new IllegalArgumentException();
}
}
}

第 3 個問題:簡單工廠模式不利於拓展,違背了**開閉原則**,每次添加一個類,都要修改工廠類(如果是工廠類和業務類是兩個小夥伴分開寫的,那不是要花很多時間來溝通...),所以就有工廠方法模式,其原理就是對簡單工廠也進行抽象。

public interface IFactory {
IProduct getProduct();
}

public class AFactory implements IFactory {
@Override
public IProduct getProduct() {
return new AProduct(AProduct.class.getName());
}
}

public class BFactory implements IFactory {
@Override
public IProduct getProduct() {
return new BProduct(BProduct.class.getName());
}
}

第 4 個問題:突然發現有些糟糕了,因為代碼變得很多了,因為功能類似的產品我們進行 3 層抽象,針對每個產品我們還抽象出了 2 層的工廠類。但是我們在某個具體的業務場景中,不單單是只實例化一個類啊。舉一個例子,在遊戲中,我們要一個戰士配裝備,首先我們需要配一把槍械(槍械有很多,步槍,狙擊槍等,使用問題 1 進行抽象),但是配了槍械之後,我們還需要配子彈啊(繼續使用問題 1 的方法進行抽象),好了,現在可以抽象出 2 層的工廠類了,針對現在的情況我們是不是可以讓一個工廠既生產槍械,又生產子彈呢? 這就是抽象工廠模式。簡單來說,可以把有一些有聯繫或者相近的產品,放到一個工廠去生產,沒有必要單獨再開一個工廠了

下面舉一個具體的案例來說,來說明工廠方法的應用吧

我們都知道 Java 的泛型是採用類型擦除來實現的(在 javac 編譯過程的中把泛型去掉,加上強制類型轉換)。所以我們不能直接 new T()來實例化一個對象。其實可以採用工廠方法模式設計模式來解決。

假設我們有一個類,裡面要用到了泛型的實例化。

public class Foo&(){
private T t;
public Foo(){
t = new T(); // 這個代碼是有問題的,我們使用工廠設計模式進行改進
}
}

我們給出工廠介面如下:

public interface IFactory&(){
T create();
}

進而我們可以採用如下的方法進行改進

public class Foo&(){
private T t;
public &&> Foo(F factory){
// t = new T();
factory.create();
}
}

這個時候,我們可以採用如下的方式實例化 Foo

new Foo(new Ifactory&(){
Integer create(){
return new Integer(0);
}
});

new Foo(new Ifactory&(){
String create(){
return "Hello";
}
});

ps: 為了顯得不會太啰嗦,所以工廠方法的實現,這裡採用內部類來完成。


設計模式的一個重要原則就是:別改代碼,只需要添代碼,以前所有的老代碼,都是有價值的,需要儘力保留

new一個對象時,new的過程是寶貴的如何創建老對象的知識點(有的new很複雜,包括了很多參數),如果這個代碼被修改了,那麼保留的老對象也不知道怎麼使用了,整個體系殘缺了

所以要想辦法保留老對象的new過程,把這個new過程保存分布到一系列工廠類里,就是所謂的工廠模式,一般有三種方式來封裝

簡單工廠:把對象的創建放到一個工廠類中,通過參數來創建不同的對象。

這個缺點是每添一個對象,就需要對簡單工廠進行修改(儘管不是刪代碼,僅僅是添一個switch case,但仍然違背了「不改代碼」的原則)

工廠方法:每種產品由一種工廠來創建,一個工廠保存一個new

基本完美,完全遵循
「不改代碼」的原則

抽象工廠:僅僅是工廠方法的複雜化,保存了多個new

大工程才用的上


可以讓你應對這種情況。你哪天心情好把類名改了。或者把構造函數參數改了(增加或減少參數)。而你代碼中又有N處new了這個類。如果你又沒用工廠。那你慘了。一個一個找來改吧。千萬不要漏噢


工廠模式抽象了對象創建的具體細節,創建的時候只需要用特定函數封裝特定介面的創建細節。


我也是在想, 看了幾個人的評論和一些資料, 寫了個博客, 總結上面以及自己的想法

小鴉之設計模式(2)-工廠模式 - 帰る場所もいない - 博客頻道 - CSDN.NET

題主所說的, 我略有同感, 在寫博客的時候想了下. 主要以下

1. 題主認為工廠模式, 是自己寫完自己用嗎,而且是立刻嗎?

這是"時間"+"人物"的問題, 若干年後, 已經忘記的自己, 或者那不認識的對方.

基於這兩點, 這不是簡單的封裝

2. 工廠模式寫了注釋, 再使用IDE提示功能, 會方便查詢. 同時用factory class里用提示功能查詢會比較快, 我認為這會快過你去文件夾看一下class名, 再在IDE里查看名字以及注釋(這注釋還是帶有冗餘信息的)

3. 用了別人的工廠模式, IDE就自動補充interface, 也就是強制使用interface

我覺得工廠模式挺象字典的, 其他沒提到的在博文里寫了..


個人認為,這樣解釋還是比較系統規範的,

工廠模式屬於創建型模式,它提供了一種創建對象的最佳方式。
在工廠模式中,我們在創建對象時不會對客戶端暴露創建邏輯,並且是通過使用一個共同的介面來指向新創建的對象。

意圖:定義一個創建對象的介面,讓其子類自己決定實例化哪一個工廠類,工廠模式使其創建過程延遲到子類進行。


主要解決:主要解決介面選擇的問題。


何時使用:我們明確地計劃不同條件下創建不同實例時。

如何解決:讓其子類實現工廠介面,返回的也是一個抽象的產品。


關鍵代碼:創建過程在其子類執行。


應用實例:
1、您需要一輛汽車,可以直接從工廠裡面提貨,而不用去管這輛汽車是怎麼做出來的,以及這個汽車裡面的具體實現。
2、Hibernate 換資料庫只需換方言和驅動就可以。

附上傳送門:

工廠模式 | 菜鳥教程


搜噶,抽象工廠還是有點用處的。


推薦閱讀:

遊戲設計模式(一) 序言:架構,性能與遊戲
7個有益的編程習慣
在 C++11 中,如何為匿名的結構體添加構造函數?
《自頂向下方法》筆記 · 編程作業2 · UDPping程序
Spotify 如何對歌曲隨機播放?

TAG:編程 | 軟體設計 | 設計模式 |