天天看點

Java設計模式——工廠模式詳解

一、概述

1.1使用場景。

  在任何需要生成複雜對象的地方,都可以使用工廠方法模式, 直接用new可以完成的不需要用工廠模式。個人了解,重點就是這個複雜 (構造函數有很多參數)和是否可以直接用new。

二、工廠模式的五種寫法

2.1簡單的靜态工廠模式。

/**
 * 圖形抽象類
 */
public abstract class Shap {
    /**
     * 描述形狀(畫畫)
     */
    public abstract void draw();
}
           
/**
 * 圓形類
 */
public class Circle extends Shap {
    public void draw() {
        System.out.println("這是圓形...");
    }
}
           
/**
 * 長方形類
 */
public class Rectangle extends Shap {
    public void draw() {
        System.out.println("這是長方形...");
    }
}
           
/**
 * 正方形類
 */
public class Square extends Shap {
    public void draw() {
        System.out.println("這是正方形...");
    }
}
           
/**
 * 簡單靜态工廠類
 */
public class SimpleStaticShapFactory {
    // 長方形
    public static final int TYPE_RECTANGLE = ;
    // 正方形
    public static final int TYPE_SQUARE = ;
    // 圓形
    public static final int TYPE_CIRCLE = ;

    public static Shap createShap(int type) {
        switch (type) {
            case TYPE_RECTANGLE:
                return new Rectangle();
            case TYPE_SQUARE:
                return new Square();
            case TYPE_CIRCLE:
            default:
                return new Circle();
        }
    }
}
           
/**
 * 測試類
 */
public class Test {

    public static void main(String[] args) {
        // 建立執行個體
        Shap shap = SimpleStaticShapFactory.createShap(SimpleStaticShapFactory.TYPE_CIRCLE);
        shap.draw();
        // 建立執行個體
        Shap shap2 = SimpleStaticShapFactory.createShap(SimpleStaticShapFactory.TYPE_SQUARE);
        shap2.draw();
    }
}

// 運作結果
這是圓形...
這是正方形...
           

特點:

(1)它是一個具體的類,非接口抽象類。有一個重要的createShap()方法,利用if或者 switch建立産品并傳回。

(2)createShap()方法通常是靜态的,是以也稱之為靜态工廠。

缺點:

(1)擴充性差(我想增加一種圖形,除了新增一個圖形類,還需要修改工廠類方法)。

(2)不同的圖形類需要不同額外參數的時候不支援。

2.2基于反射的簡單工廠模式。

/**
 * 工廠類
 */
public class ReflectFactory {
    /**
     * @param clzzz Class對象
     * @return 執行個體對象
     */
    public static <T extends Shap> T createShap(Class<T> clzz) {
        T instance = null;
        try {
            instance = (T) Class.forName(clzz.getName()).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return instance;
    }
}
           
/**
 * 測試類
 */
public class Test {
    public static void main(String[] args) {
        //建立執行個體
        Shap shap = ReflectFactory.createShap(Rectangle.class);
        shap.draw();
        //建立執行個體
        Shap shap2 = ReflectFactory.createShap(Square.class);
        shap2.draw();
    }
}
// 運作結果
這是長方形...
這是正方形...
           

特點:

(1)它也是一個具體的類,非接口抽象類。但它的createShap()方法,是利用反射機制生成對象傳回,好處是增加一種産品時,不需要修改createShap()的代碼。

缺點:

(1)這種寫法粗看牛逼,細想之下,不談reflection的效率還有以下問題。

(2)個人覺得不好,因為Class.forName(clzz.getName()).newInstance()調用的是無參構造函數生成對象,它和new Object()是一樣的性質,而工廠方法應該用于複雜對象的初始化 ,當需要調用有參的構造函數時便無能為力了,這樣像為了工廠而工廠。

(3)不同的産品需要不同額外參數的時候 不支援。

2.3多方法靜态工廠模式(常用)。

/**
 * 工廠類
 */
public class MultipartStaticFactory {
    /**
     * 建立圓形
     * @return Circle
     */
    public static Shap createCircle() {
        return new Circle();
    }
    /**
     * 建立長方形
     * @return Rectangle
     */
    public static Shap createRectangle() {
        return new Rectangle();
    }
    /**
     * 建立正方形
     * @return Square
     */
    public static Shap createSquare() {
        return new Square();
    }
}
           
/**
 * 測試類
 */
public class Test {
    public static void main(String[] args) {
        // 建立執行個體
        Shap shap = MultipartStaticFactory.createCircle();
        shap.draw();
        // 建立執行個體
        Shap shap2 = MultipartStaticFactory.createSquare();
        shap2.draw();
    }
}

//運作結果
這是圓形...
這是正方形...
           

特點:

(1)使用前面兩張方法實作的工廠,都有一個缺點:不同的類需要不同額外參數的時候不支援。

(2) 如果使用時傳遞的type、Class出錯,将不能得到正确的對象,容錯率不高。 而多方法的工廠模式為不同産品,提供不同的生産方法,使用時需要哪種産品就調用該種産品的方法,使用友善、容錯率高。

(3)這個例子可以感受到工廠方法的魅力了吧:友善建立同種類型的複雜參數對象。

此種設計模式還在Executors源碼用到:

  Executor是生成各種線程池的工廠,其采用的便是多方法靜态工廠模式。

/**
 * java.util.concurrent.Executors類部分源碼
 */
public class Executors {
    /**
     * ThreadPoolExecutor方法有5個參數,有部分參數寫法固定,而工廠類就隻需要傳入一個參數
     */
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
    /**
     * 假如JDK想增加建立ForkJoinPool類的方法,隻想配置parallelism參數,便在類裡增加一個如下的方法
     */
    public static ExecutorService newWorkStealingPool(int parallelism) {
        return new ForkJoinPool
            (parallelism,
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
    }

    public static ExecutorService newWorkStealingPool() {
        return new ForkJoinPool
            (Runtime.getRuntime().availableProcessors(),
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
    }

    public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>(),
                                      threadFactory);
    }
           

2.4普通工廠模式。

/**
 * 抽象工廠類,生産圖形
 */
public abstract class ShapFactory {
    abstract Shap createShap();
}
           
/**
 * 具體工廠子類,圓形工廠
 */
public class CircleFactory extends ShapFactory {
    @Override
    Shap createShap() {
        return new Circle();
    }
}
           
/**
 * 具體工廠子類,長方形工廠
 */
public class RectangleFactory extends ShapFactory {

    @Override
    Shap createShap() {
        return new Rectangle();
    }
}
           
/**
 * 具體工廠子類,正方形工廠
 */
public class SquareFactory extends ShapFactory {
    @Override
    Shap createShap() {
        return new Square();
    }
}
           
/**
 * 測試類
 */
public class Test {
    public static void main(String[] args) {
        // 建立圓形工廠
        ShapFactory shapFactory = new CircleFactory();
        // 建立圓形執行個體
        Shap circle = shapFactory.createShap();
        circle.draw();

        // 建立正方形工廠
        ShapFactory shapFactory2 = new SquareFactory();
        // 建立正方形執行個體
        Shap Square = shapFactory2.createShap();
        Square.draw();
    }
}
// 運作結果
這是圓形...
這是正方形...
           

特點:

(1)普通工廠就是把簡單工廠【2.1簡單的靜态工廠模式】中具體的工廠類,劃分成兩層:抽象工廠層和具體的工廠子類層。

(2)可以看出,普通工廠模式特點:不僅僅做出來的産品要抽象, 工廠也應該需要抽象。

(3)工廠方法使一個産品類的執行個體化延遲到其具體工廠子類。

(4)工廠方法的好處就是更擁抱變化。當需求變化,隻需要增删相應的類,不需要修改

(5)而簡單工廠【2.1簡單的靜态工廠模式】需要修改工廠類的createShap()方法和具體圖形類,多方法靜态工廠【2.3多方法靜态工廠模式】模式需要增加一個靜态方法和具體圖形類。

缺點:

(1) 引入抽象工廠層後,每次新增一個具體圖形類,也要同時新增一個具體工廠類。

2.4抽象工廠模式。

/**
 * 飲料抽象類
 */
public abstract class Drink {
    /**
     * 描述每種飲料多少錢
     */
    public abstract void prices();
}
           
/**
 * 可樂類
 */
public class Cola extends Drink {
    @Override
    public void prices() {
        System.out.println("可口可樂3.0元...");
    }
}
           
/**
 * 牛奶類
 */
public class Milk extends Drink {
    @Override
    public void prices() {
        System.out.println("牛奶2.5元...");
    }
}
           
/**
 * 抽象工廠類
 */
public abstract class DrinkAndDraw {
    /**
     * 喝飲料
     * 
     * @return
     */
    public abstract Drink doDrink();
    /**
     * 畫畫
     * 
     * @return
     */
    public abstract Shap doDraw();
}
           
/**
 * 具體工廠類1
 */
public class FactoryOne extends DrinkAndDraw {
    /**
     * 喝可樂
     */
    @Override
    public Drink doDrink() {
        return new Cola();
    }
    /**
     * 畫圓形
     */
    @Override
    public Shap doDraw() {
        return new Circle();
    }
}
           
/**
 * 具體工廠類2
 */
public class FactoryTwo extends DrinkAndDraw {
    /**
     * 喝牛奶
     */
    @Override
    public Drink doDrink() {
        return new Milk();
    }
    /**
     * 畫正方形
     */
    @Override
    public Shap doDraw() {
        return new Square();
    }
}
           
/**
 * 測試類
 */
public class Test {
    public static void main(String[] args) {
        // 上幼稚園的小明一邊喝可口可樂,一邊在畫圓形
        // 建立工廠1的執行個體,傳回的方法是可樂執行個體的方法和圓形執行個體的方法
        DrinkAndDraw drinkAndDraw = new FactoryOne();
        // 調用可樂執行個體的喝可樂方法
        drinkAndDraw.doDrink().prices();
        // 調用圓形執行個體的畫圓形方法
        drinkAndDraw.doDraw().draw();

        // 上幼稚園的小明一邊喝牛奶,一邊在畫正方形
        DrinkAndDraw drinkAndDraw2 = new FactoryTwo();
        drinkAndDraw2.doDrink().prices();
        drinkAndDraw2.doDraw().draw();
    }
}
           
// 運作結果
可口可樂元...
這是圓形...
牛奶元...
這是正方形...
           

特點:

(1)将工廠也抽象了,在使用時,工廠和産品都是面向接口程式設計。

缺點:

(1)如果每次擴充新抽象類,比如讀書抽象類,那就需要新加具體實作類如讀小說還是讀教科書,然後需要修改抽象工廠類,而後所有的具體工廠子類,都被牽連,需要同步被修改。

繼續閱讀