1. 程式人生 > >設計模式之 --- 工廠模式(上)

設計模式之 --- 工廠模式(上)

設計模式 http imp 實戰 cti 將在 als access actor

設計模式之 --- 工廠模式(上)

簡介

工廠設計模式專門負責將大量有共同接口的類實例化,工廠模式可以動態的決定將哪一個類實例化,不必事先知道每次要實例化那一個類,工廠模式有以下幾種形態:

技術分享圖片

一、簡單工廠模式

簡介

? 簡單工廠模式(Simple Factory Pattern ):又稱為靜態工廠方法模式(Static Factory Method ),是類創建型設計模式之一。

? 我們可以通過對工廠方法傳入不同參數來返回具體類的實例對象,並且所有返回的實例對象都具有共同的父類。

使用場景

? 將類的實例化操作和對象的使用分開,讓用戶可以不用知道具體實例化的細節直接使用,從而避免在客戶端代中碼顯示指定,實現解耦。

? 工廠類負責創建的對象比較少,由於創建的對象較少,不會造成工廠方法中的業務邏輯太過復雜。

? 客戶端只知道傳入工廠類的參數,對於如何創建對象不關心:客戶端既不需要關心創建細節,甚至連類名都不需要記住,只需要知道類型所對應的參數。

UML類圖

技術分享圖片

示例

抽象產品類 《汽車》

/**
 * 抽象產品類 
 */
public interface Car {
    /**
     * 作為汽車都可以跑
     */
    void run();

    /**
     * 作為汽車也可以剎車
     */
    void braking();
}

具體產品類 《小轎車》

/**
 *  小轎車
 */
public class SedanCar implements Car {
    public void run() {
        System.out.println("小轎車啟動了!");
    }

    public void braking() {
        System.out.println("小轎車剎車了!");
    }
}

具體產品類 《SUV汽車》

/**
 * SUV 型汽車
 */
public class SuvCar implements Car {
    public void run() {
        System.out.println("SUV 啟動了!");
    }
  
    public void braking() {
        System.out.println("SUV 剎車了!");
    }
}

具體工廠類

/**
 *  具體工廠類
 */
public final class CarFactory {
    /**
     * 小轎車
     */
    public static final String TYPE_SEDAN = "SEDAN_CAR";

    /**
     * SUV
     */
    public static final String TYPE_SUV = "SUV";

    /**
     * 靜態工廠方法
     *
     * @param type 汽車類型
     * @return
     */
    public static Car create(String type) {
        if (type.equals(TYPE_SEDAN)) {
            return new SedanCar();
        } else if (type.equals(TYPE_SUV)) {
            return new SuvCar();
        }else {
            throw new RuntimeException("沒有找到匹配的對象!");
        }
    }
}

測試

/**
 * 工廠模式測試類
 */
public class FactoryTest {
    public static void main(String[] args) {
        // 簡單工廠模式測試
        Car sedanCar = CarFactory.create(CarFactory.TYPE_SEDAN);
        Car suvCar = CarFactory.create(CarFactory.TYPE_SUV);
        sedanCar.run();
        sedanCar.braking();
        suvCar.run();
        suvCar.braking();
    }
}

角色簡介

從上面的類圖及示例可以看出,簡單工廠涉及到工廠角色、抽象產品角色、具體產品角色等三個角色。

  • Factory : 工廠角色

    工廠角色負責創建所有實例的內部邏輯實現,由一個具體的Java類實現

  • Car:抽象的產品角色

    抽象產品角色是創建所有具體實例對象的共同父類,負責描述所有實例所共用的公共接口,可以由一個Java接口或者Java抽象類實現

  • SedanCar、SuvCar:具體產品角色

    具體產品角色是創建目標,為實現抽象產品角色的某個具體產品對象,由一個具體Java類實現

優點

? 該模式的核心是工廠類,這個類含有必要的邏輯判斷,可以決定在什麽時候創建哪一個產品類的實例。而客戶端可以免除直接創建產品對象的責任,僅僅負責“消費”產品。簡單工廠模式通過這種方法實現了對責任的分割。

缺點

? 當產品有復雜的多層次等級結構時,工廠類只有他自己,以不變應萬變就是模式的缺點

? 這個工廠類集中了所有產品的創建邏輯,形成了一個無所不知的全能類,有人把這種類叫做上帝類(God Class),他將負責所有產品創建,一旦這個工廠不能正常工作,那整個產品生產線都將受到影響

? 將這麽多的邏輯放到一個類裏面的另外一個缺點是,當產品類有不同的接口種類時,工廠類需要判斷在什麽時候創建某種產品,這種對時機的判斷和對哪一種具體產品的判斷邏輯混合在一起,使得系統將來的功能擴展變的困難,這一缺點將在工廠方法模式中克服

? 由於簡單工廠模式使用了靜態方法作為工廠方法,而靜態方法無法由子類繼承,因此工廠角色無法形成基於繼承的等級結構,這一缺點將在工廠方法模式中克服

? 簡單工廠模式只在有限程度上支持了“開-閉”原則

二、工廠方法模式

簡介

? 工廠方法模式(Factory Method Pattern)又稱為工廠模式,也叫虛擬構造器(VirtualConstructor)模式或者多態工廠(Polymorphic Factory)模式,它屬於類創建型模式。

? 工廠方法模式是簡單工廠模式的進一步抽象和推廣,由於使用了多態性,工廠方法保持了簡單工廠模式的優點,而且克服了它的缺點

使用場景

? 在任何需要生成復雜對象的地方,都可以使用工廠方法模式,復雜對象適合使用工廠模式,用new就可以完成創建的對象無需使用工廠模式

? 一個類通過其子類來指定創建哪個對象,在工廠方法模式中,對於抽象工廠類只需要提供一個創建產品的接口,而由其子類來確定具體要創建的對象,利用面向對象的多態性和裏氏代換原則,在程序運行時,子類對象將覆蓋父類對象,這種方式可以使系統更容易擴展

UML類圖

技術分享圖片

示例

還是上面的示例進行改造如下:

抽象產品類 《Car》

/**
 * 抽象產品類
 */
public interface Car {
    /**
     * 作為汽車都可以跑
     */
    void run();

    /**
     * 作為汽車也可以剎車
     */
    void braking();
}

具體產品類 《SedanCar》

/**
 *  小轎車
 */
public class SedanCar implements Car {
    public void run() {
        System.out.println("小轎車啟動了!");
    }

    public void braking() {
        System.out.println("小轎車剎車了!");
    }
}

具體產品類 《SuvCar》

/**
 * SUV 型汽車
 */
public class SuvCar implements Car {
    public void run() {
        System.out.println("SUV 啟動了!");
    }

    public void braking() {
        System.out.println("SUV 剎車了!");
    }
}

抽象工廠類 《CarFactory》

public interface CarFactory {
    /**
     *  抽象工廠方法,具體生產什麽對象由具體子類提供
     * @return 返回具體實例對象
     */
    Car create();
}

具體工廠類 《SedanCarFactory》

public class SedanCarFactory implements CarFactory {
    public Car create() {
        return new SedanCar();
    }
}

具體工廠類 《SuvCarFactory》

public class SuvCarFactory implements CarFactory {
    public Car create() {
        return new SuvCar();
    }
}

測試

/**
 * 工廠模式測試類
 */
public class FactoryTest {
    public static void main(String[] args) {
        // 工廠方法模式測試
        SedanCarFactory sedanCarFactory = new SedanCarFactory();
        Car car = sedanCarFactory.create();
        car.run();
        car.braking();

        SuvCarFactory suvCarFactory = new SuvCarFactory();
        Car carSuv = suvCarFactory.create();
        carSuv.run();
        carSuv.braking();
    }
}

角色簡介

  • 抽象產品角色:Car

    工廠方法模式所創建對象的超類型,也就是具體產品對象的共同父類或者共同擁有的接口

  • 具體產品角色:SedanCar、SuvCar

    這個角色實現了抽象產品角色所聲明的接口,工廠方法所創建的每一個對象都是某個具體產品角色的實例

  • 抽象工廠角色:CarFactory

    擔任這個角色的是工廠方法模式的核心,它是與應用程序無關的,任何在模式中創建對象的工廠類必須實現這個接口

  • 具體工廠角色:SedanCarFactory、SuvCarFactory

    這個角色實現了抽象工廠的接口具體的Java類,這個角色含有與應用密切相關的邏輯,並且受到應用程序的調用創建產品對象

工廠方法模式改造

? 以上實現方式比較常見,有時候我們也可以利用反射的方式簡潔地來生產具體產品對象,此時,只需要我們在工廠方法中傳入一個Class類來決定是哪一個產品類:

/**
 * 工廠方法的另一種寫法
 *
 * 使用反射可以更簡潔的生產產品對象
 */
public interface CarFactory2 {
    <T extends Car> T create(Class<T> clazz);
}

對於具體的工廠類,則通過反射獲取類的實例即可:

public class CarFactory2Impl implements CarFactory2 {
    public <T extends Car> T create(Class<T> clazz) {
        Car car = null;
        try {
            car = (Car) Class.forName(clazz.getName()).newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return (T) car;
    }
}

最後我們看下測試實現:

public class FactoryTest {
    public static void main(String[] args) {
        // 工廠方法模式測試第二種寫法
        CarFactory2Impl carFactory2 = new CarFactory2Impl();
        Car sedanCar1 = carFactory2.create(SedanCar.class);
        sedanCar1.run();
        sedanCar1.braking();

        Car suvCar1 = carFactory2.create(SuvCar.class);
        suvCar1.run();
        suvCar1.braking();
    }
}

打印結果:

小轎車啟動了!
小轎車剎車了!
SUV 啟動了!
SUV 剎車了!

優點

? 首先要說的是工廠方法模式克服簡單工廠模式的缺點

? 工廠方法模式通過抽象工廠角色,僅負責給出具體工廠子類必須實現的接口,而不接觸具體產品生產的實現細節,這種進一步抽象化的結果使工廠方法模式可以允許在不修改具體工廠角色的情況下引進新的產品,符合了“開-閉”原則,這一點比簡單工廠模式更優秀

? 基於工廠角色和產品角色的多態性設計是工廠方法模式的關鍵。它能夠使工廠可以自主確定創建何種產品對象,而如何創建這個對象的細節則完全封裝在具體工廠內部。工廠方法模式之所以又被稱為多態工廠模式,是因為所有的具體工廠類都具有同一抽象父類 ,這樣的設計可以提高系統擴展性

缺點

? 在添加新產品時,需要編寫新的具體產品類,而且還要提供與之對應的具體工廠類,系統中類的個數將成對增加,在一定程度上增加了系統的復雜度和導致類結構的復雜化,有更多的類需要編譯和運行,也會給系統帶來一些額外的開銷。

總結

? 總的來說,工廠設計模式是一個很好的設計模式,它的優點和缺點已經總結了,在平常的開發中這種缺點也是不可避免的,所在在某些簡單或者復雜的場景下,對於工廠方法模式的使用,需要設計者自己來權衡 其中的利弊了

參考資料

Java與模式 - 書

Android 源碼設計模式解析與實戰 - 書

設計模式之 --- 工廠模式(上)