在一次面試中了解到工廠模式在實際應用中的重要性,可以說工廠模式的應用隨處可見,以下是百度百科對工廠模式的介紹
工廠模式是我們最常用的實例化對象模式了,是用工廠方法代替new操作的一種模式。著名的Jive論壇 ,就大量使用了工廠模式,工廠模式在Java程序系統可以說是隨處可見。因為工廠模式就相當於創建實例對象的new,我們經常要根據類Class生成實例對象,如A a=new A() 工廠模式也是用來創建實例對象的,所以以后new時就要多個心眼,是否可以考慮使用工廠模式,雖然這樣做,可能多做一些工作,但會給你系統帶來更大的可擴展性和盡量少的修改量。
在看了幾篇寫工廠模式的文章后,看見一篇我認為比較容易理解的代碼說明:
一.簡單(靜態)工廠模式
1,首先建立一個產品的抽象類
需要生產什么產品(實例對象)就需要首先建立一個相應產品的抽象類
public abstract class INoodles {
/** * 描述每種面條啥樣的 */
public abstract void desc();
}
2.再建立幾種具體的產品類如:
這里建立了三個產品類:蘭州拉面,泡面,干扣面(沒吃過)
public class LzNoodles extends INoodles {
@Override
public void desc() {
System.out.println("蘭州拉面");
}
}
public class PaoNoodles extends INoodles {
@Override
public void desc() {
System.out.println("泡面");
}
}
public class GankouNoodles extends INoodles {
@Override
public void desc() {
System.out.println("干扣面");
}
}
3.在建立完這些產品后就可以建立(造面的)工廠了:
工廠里面包含了我們可以生產的產品(面)
public class SimpleNoodlesFactory {
public static final int TYPE_LZ = 1;//蘭州拉面
public static final int TYPE_PM = 2;//泡面
public static final int TYPE_GK = 3;//干扣面
public static INoodles createNoodles(int type) {
switch (type) {
case TYPE_LZ:
return new LzNoodles();
case TYPE_PM:
return new PaoNoodles();
case TYPE_GK:
default:
return new GankouNoodles();
}
}
}
4.開始根據客人的要求生產產品
如下代碼是生產干扣面(真心不知道這名字是怎么來的)的過程。
INoodles noodles = SimpleNoodlesFactory.createNoodles(SimpleNoodlesFactory.TYPE_GK);
noodles.desc();
該設計模式只可以生產工廠能生產的產品,如需要增加產品,需要在工廠類中增加相應的靜態變量。
特點
1 它是一個具體的類,非接口 抽象類。有一個重要的create()方法,利用if或者 switch創建產品並返回。
2 create()方法通常是靜態的,所以也稱之為靜態工廠。
缺點
1 擴展性差(我想增加一種面條,除了新增一個面條產品類,還需要修改工廠類方法)
2 不同的產品需要不同額外參數的時候 不支持。
二、另一種簡單工廠(反射):
利用反射Class.forName(clz.getName()).newInstance()
實現的簡單工廠:
public class StaticNoodlesFactory {
/** * 傳入Class實例化面條產品類 * * @param clz * @param <T> * @return */
public static <T extends INoodles> T createNoodles(Class<T> clz) {
T result = null;
try {
result = (T) Class.forName(clz.getName()).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
}
特點
1 它也是一個具體的類,非接口 抽象類。但它的create()方法,是利用反射機制生成對象返回,好處是增加一種產品時,不需要修改create()的代碼。
缺點
這種寫法粗看牛逼,細想之下,不談reflection的效率還有以下問題:
1 個人覺得不好,因為Class.forName(clz.getName()).newInstance()調用的是無參構造函數生成對象,它和new Object()是一樣的性質,而工廠方法應該用於復雜對象的初始化 ,當需要調用有參的構造函數時便無能為力了,這樣像為了工廠而工廠。
2 不同的產品需要不同額外參數的時候 不支持
三、 多方法工廠(常用)
使用方法二 三實現的工廠,都有一個缺點:不同的產品需要不同額外參數的時候 不支持。
而且如果使用時傳遞的type、Class出錯,將不能得到正確的對象,容錯率不高。
而多方法的工廠模式為不同產品,提供不同的生產方法,使用時 需要哪種產品就調用該種產品的方法,使用方便、容錯率高。
工廠如下:
public class MulWayNoodlesFactory {
/** * 模仿Executors 類 * 生產泡面 * * @return */
public static INoodles createPm() {
return new PaoNoodles();
}
/** * 模仿Executors 類 * 生產蘭州拉面 * * @return */
public static INoodles createLz() {
return new LzNoodles();
}
/** * 模仿Executors 類 * 生產干扣面 * * @return */
public static INoodles createGk() {
return new GankouNoodles();
}
}
這種我比較青睞,增加一個新面條,只要去增加一個static方法即可,也不修改原方法邏輯
查看java源碼:java.util.concurrent.Executors
類便是一個生成Executor
的工廠 ,其采用的便是 多方法靜態工廠模式:
例如ThreadPoolExecutor
類構造方法有5個參數,其中三個參數寫法固定,前兩個參數可配置,如下寫。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
又如JDK想增加創建ForkJoinPool
類的方法了,只想配置parallelism
參數,便在類里增加一個如下的方法:
public static ExecutorService newWorkStealingPool(int parallelism) {
return new ForkJoinPool
(parallelism,
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null, true);
}
這個例子可以感受到工廠方法的魅力了吧:方便創建 同種類型的 復雜參數 對象。
四 普通工廠
普通工廠就是把簡單工廠中具體的工廠類,划分成兩層:抽象工廠層+具體的工廠子類層。(一般->特殊)
面條工廠(抽象工廠類),作用就是生產面條:
public abstract class NoodlesFactory {
public abstract INoodles create();
}
蘭州拉面工廠 (具體工廠子類):
public class LzFactory extends NoodlesFactory {
@Override
public INoodles create() {
return new LzNoodles();
}
}
泡面工廠 (具體工廠子類):
public class PaoFactory extends NoodlesFactory {
@Override
public INoodles create() {
return new PaoNoodles();
}
}
最愛的干扣面工廠 (具體工廠子類):
public class GankouFactory extends NoodlesFactory {
@Override
public INoodles create() {
return new GankouNoodles();
}
}
使用時:
/** * 普通工廠方法: */
System.out.println("===========================普通工廠方法==============================" +
"\n 這種要多寫一個類,不過更面向對象");
NoodlesFactory factory1 = new GankouFactory();
INoodles gk3 = factory1.create();
gk3.desc();
普通工廠與簡單工廠模式的區別:
可以看出,普通工廠模式特點:不僅僅做出來的產品要抽象, 工廠也應該需要抽象。
工廠方法使一個產品類的實例化延遲到其具體工廠子類.
工廠方法的好處就是更擁抱變化。當需求變化,只需要增刪相應的類,不需要修改已有的類。
而簡單工廠需要修改工廠類的create()方法,多方法靜態工廠模式需要增加一個靜態方法。
六 抽象工廠:
以上介紹的工廠都是單產品系的。抽象工廠是多產品系 (貌似也有產品家族的說法)。
舉個例子來說,每個店(工廠)不僅僅賣面條,還提供飲料賣。
提供飲料賣,飲料是產品,先抽象一個產品類,飲料:
public abstract class IDrinks {
/** * 描述每種飲料多少錢 */
public abstract void prices();
}
然后實現兩個具體產品類:
可樂:
public class ColaDrinks extends IDrinks {
@Override
public void prices() {
System.out.println("可樂三塊五");
}
}
屌絲還是多喝水吧:
public class WaterDrinks extends IDrinks {
@Override
public void prices() {
System.out.println("和我一樣的窮鬼都喝水,不要錢~!");
}
}
抽象飯店,無外乎吃喝(抽象工廠類):
public abstract class AbstractFoodFactory {
/** * 生產面條 * * @return */
public abstract INoodles createNoodles();
/** * 生產飲料 */
public abstract IDrinks createDrinks();
}
蘭州大酒店(具體工廠類):
public class LzlmFoodFactory extends AbstractFoodFactory {
@Override
public INoodles createNoodles() {
return new LzNoodles();//賣蘭州拉面
}
@Override
public IDrinks createDrinks() {
return new WaterDrinks();//賣水
}
}
KFC(具體工廠類):
public class KFCFoodFactory extends AbstractFoodFactory {
@Override
public INoodles createNoodles() {
return new PaoNoodles();//KFC居然賣泡面
}
@Override
public IDrinks createDrinks() {
return new ColaDrinks();//賣可樂
}
}
使用:
/** * 抽象工廠方法: */
System.out.println("==============================抽象方法==============================" )
AbstractFoodFactory abstractFoodFactory1 = new KFCFoodFactory();
abstractFoodFactory1.createDrinks().prices();
abstractFoodFactory1.createNoodles().desc();
abstractFoodFactory1= new LzlmFoodFactory();
abstractFoodFactory1.createDrinks().prices();
abstractFoodFactory1.createNoodles().desc();
輸出:
==============================抽象方法==============================
可樂三塊五
泡面
和我一樣的窮鬼都喝水,不要錢~!
蘭州拉面
小結:
將工廠也抽象了,在使用時,工廠和產品都是面向接口編程,OO(面向對象)的不得了。
六 個人總結和使用場景
一句話總結工廠模式:方便創建 同種產品類型的 復雜參數 對象
工廠模式重點就是適用於 構建同產品類型(同一個接口 基類)的不同對象時,這些對象new很復雜,需要很多的參數,而這些參數中大部分都是固定的,so,懶惰的程序員便用工廠模式封裝之。
(如果構建某個對象很復雜,需要很多參數,但這些參數大部分都是“不固定”的,應該使用Builder模式)
為了適應程序的擴展性,擁抱變化,便衍生出了 普通工廠、抽象工廠等模式。
本文參考的文章的作者更偏向於使用多方法工廠,但我認為普通工廠和抽象工廠使用范圍更廣,而且更好理解。
學習參考
https://blog.csdn.net/zxt0601/article/details/52798423