Java設計模式-工廠方法模式

工廠方法模式概述

在簡單工廠模式中只提供一個工廠類,該工廠類處於對產品類進行實例化的中心位置,它須要知道每個產品對象的建立細節,並決定什麼時候實例化哪個產品類。簡單工廠模式最大的缺點是當有新產品要加入到系統中時,必須修改工廠類,須要在其中加入必要的業務邏輯,這違背了「開閉原則」。此外,在簡單工廠模式中,全部的產品都由同一個工廠建立,工廠類職責較重,業務邏輯較爲複雜,具體產品與工廠類之間的耦合度高,嚴重影響了系統的靈活性和擴展性,而工廠方法模式則能夠很好地解決這一問題。html

工廠方法模式定義

在工廠方法模式中,咱們再也不提供一個統一的工廠類來建立全部的產品對象,而是針對不一樣的產品提供不一樣的工廠,系統提供一個與產品等級結構對應的工廠等級結構。工廠方法模式定義以下:java

定義一個用於建立對象的接口,讓子類決定將哪個類實例化。工廠方法模式讓一個類的實例化延遲到其子類。工廠方法模式又簡稱爲工廠模式 (Factory Pattern),又可稱做虛擬構造器模式 (Virtual Constructor Pattern) 或多態工廠模式 (Polymorphic Factory Pattern)。工廠方法模式是一種類建立型模式。node

工廠方法模式結構

一、Product(抽象產品):它是定義產品的接口,是工廠方法模式所建立出來的對象的超類型,也就是產品對象的公共父類。數據庫

二、ConcreteProduct(具體產品):它實現了抽象產品接口,某種類型的具體產品由專門的具體工廠建立,具體工廠和具體產品一一對應。設計模式

三、Factory(抽象工廠):在抽象工廠中,聲明瞭工廠方法,用於返回一個產品。抽象工廠是工廠方法模式的核心,全部建立對象的工廠類都須要實現這個接口。ide

四、ConcreteFactory(具體工廠):它是抽象工廠類的子類,實現抽象工廠類中的定義的工廠方法,並由客戶端調用,返回一個具體的產品實例。測試

工廠方法模式代碼

一、聲明一個抽象產品類:ui

/**
 * Author: YiFan
 * Date: 2018/12/10 21:09
 * Description: 抽象產品
 */
public abstract class Car {
    
    // 抽象方法-汽車名稱描述
    public abstract void desc();
}

二、聲明具體產品類:設計

/**
 * Author: YiFan
 * Date: 2018/12/10 21:11
 * Description: 具體產品類
 */
public class BMWCar extends Car {

    @Override
    public void desc() {
        System.out.println("寶馬汽車");
    }
}

/**
 * Author: YiFan
 * Date: 2018/12/10 21:11
 * Description: 具體產品類
 */
public class AuDiCar extends Car {

    @Override
    public void desc() {
        System.out.println("奧迪汽車");
    }
}

三、聲明抽象工廠類:code

/**
 * Author: YiFan
 * Date: 2018/12/10 21:13
 * Description: 抽象工廠
 */
public abstract class CarFacotry {

    // 抽象工廠方法-返回產品
    public abstract Car produceMethod();
}

五、聲明具體工廠類:

/**
 * Author: YiFan
 * Date: 2018/12/10 21:15
 * Description: 具體工廠類
 */
public class BMWCarFactory extends CarFacotry {

    @Override
    public Car produceCar() {
        return new BMWCar();
    }
}

/**
 * Author: YiFan
 * Date: 2018/12/10 21:17
 * Description: 具體工廠類
 */
public class AuDiCarFactory extends CarFacotry {

    @Override
    public Car produceCar() {
        return new AuDiCar();
    }
}

六、客戶端代碼:

/**
 * Author: YiFan
 * Date: 2018/12/10 21:18
 * Description: 客戶端代碼-測試類
 */
public class CarTest {

    public static void main(String[] args) {
        // 建立一個具體工廠實例
        CarFactory bmwCarFactory = new BMWCarFactory();
        // 調用具體工廠的produceCar()方法建立具體產品
        car.desc();
        CarFactory auDiCarFactory = new AuDiCarFactory();
        Car car1 = auDiCarFactory.produceCar();
        car1.desc();
    }
}

執行結果爲:

寶馬汽車
奧迪汽車

在客戶端代碼中只關心工廠類便可,首先建立一個具體的工廠類實例,爲了使用它建立具體的產品,而後調用其工廠方法建立具體的產品。

工廠方法模式總結

工廠方法模式是簡單工程模式的延伸,它繼承了簡單工廠模式的優勢,同時還彌補了簡單工廠模式的不足。工廠方法模式是使用頻率最高的設計模式之一。

主要優勢

一、工廠方法模式中,工廠方法用來建立客戶所須要的產品,同時向客戶隱藏了哪一種具體產品類將被實例化這一細節,用戶只需關心所需產品對應的工廠(上述代碼示例中汽車生產工廠,實際上咱們不須要知道寶馬汽車工廠,也就是咱們不須要本身建立寶馬工廠的對象,只須要在汽車店中調用預約汽車的方法便可),無需關心細節,甚至無需知道具體產品類的類名。

二、基於工廠角色和產品角色的多態性設計是工廠方法模式的關鍵。它可以讓工廠能夠自主肯定建立何種產品對象,而如何建立這個對象的細節則徹底封裝在具體工廠內部。工廠方法模式之因此又被稱爲多態工廠模式,正是由於全部的具體工廠類都具備同一個抽象父類工廠。

三、在系統添加新產品時,無需修改抽象工廠和抽象產品提供的接口,無需修改客戶端,也無需修改其它的具體工廠和具體產品,只須要添加一個具體產品和具體工廠便可。這樣系統的擴展性變的很是好,徹底符合「開閉原則」;

主要缺點

一、在添加新產品時,須要編寫新的具體產品類,並且還要提供與之對應的具體工廠類,系統中類的個數將成對增長,在必定程度上增長了系統的複雜度,有更多的類須要編譯和運行,會給系統帶來一些額外的開銷。

二、因爲考慮到系統的可擴展性,須要引入抽象層,在客戶端代碼中均使用抽象層進行定義,增長了系統的抽象性和理解難度,且在實現時可能須要用到DOM、反射等技術,增長了系統的實現難度。

適用場景

一、客戶端不知道它所須要的對象的類。在工廠方法模式中,客戶端不須要知道具體產品類的類名,只須要知道所對應的工廠便可,具體的產品對象由具體工廠類建立,可將具體工廠類的類名存儲在配置文件或數據庫中。

二、 抽象工廠類經過其子類來指定建立哪一個對象。在工廠方法模式中,對於抽象工廠類只須要提供一個建立產品的接口,而由其子類來肯定具體要建立的對象,利用面向對象的多態性和里氏代換原則,在程序運行時,子類對象將覆蓋父類對象,從而使得系統更容易擴展。

練習

使用工廠方法模式設計一個程序來讀取各類不一樣類型的圖片格式,針對每一種圖片格式都設計一個圖片讀取器,如 GIF 圖片讀取器用於讀取 GIF 格式的圖片、JPG 圖片讀取器用於讀取 JPG 格式的圖片。需充分考慮系統的靈活性和可擴展性。

練習代碼

代碼結構以下:

ImgReader-抽象產品類

GifReader-具體產品類 JpgReader-具體產品類

ImgReaderFactory-抽象工廠類

GifReaderFactory-抽象工廠類 JpgReaderFactory-抽象工廠類

Test-客戶端代碼

具體代碼以下:

/**
 * Author: YiFan
 * Date: 2018/12/11 09:14
 * Description: 抽象產品類
 */
public interface ImgReader {

    void desc();
}

/**
 * Author: YiFan
 * Date: 2018/12/11 09:15
 * Description: 具體產品類-Gif閱讀器
 */
public class GifReader implements ImgReader {

    @Override
    public void desc() {
        System.out.println("讀取GIF格式圖片");
    }
}

/**
 * Author: YiFan
 * Date: 2018/12/11 09:16
 * Description: 具體產品類-Jpg閱讀器
 */
public class JpgReader implements ImgReader {

    @Override
    public void desc() {
        System.out.println("讀取JPG格式圖片");
    }
}

/**
 * Author: YiFan
 * Date: 2018/12/11 09:17
 * Description: 抽象工廠類
 */
public interface ImgReaderFactory {

    ImgReader createImgReader();
}

/**
 * Author: YiFan
 * Date: 2018/12/11 09:18
 * Description: 具體工廠類-Gif閱讀器工廠
 */
public class GifReaderFactory implements ImgReaderFactory {

    @Override
    public ImgReader createImgReader() {
        return new GifReader();
    }
}

/**
 * Author: YiFan
 * Date: 2018/12/11 09:20
 * Description: 具體工廠類-Jpg閱讀器工廠
 */
public class JpgReaderFactory implements ImgReaderFactory {

    @Override
    public ImgReader createImgReader() {
        return new JpgReader();
    }
}

/**
 * Author: YiFan
 * Date: 2018/12/11 09:31
 * Description: 客戶端
 */
public class Test {

    public static void main(String[] args) {
        ImgReader reader;
        ImgReaderFactory factory;
        // (1)建立GIF閱讀器工廠-建立GIF閱讀器
        factory = new GifReaderFactory();
        reader = factory.createImgReader();
        reader.desc();
    }
}

代碼(1)中在客戶端中須要建立具體的閱讀器工廠,因此若是換一個閱讀器工廠則須要對客戶端代碼進行改動,那麼如何作到不改動客戶端代碼呢?能夠利用 Java 的反射與配置文件進行擴展。

添加一個 XMLUtil 類,負責讀取 XMl 配置文件:

/**
 * Author: YiFan
 * Date: 2018/12/11 09:23
 * Description: 文件讀取類
 */
public class XMLUtil {

    public static Object getBean(String configFileName, String packageName) {
        try {
            //建立DOM文檔對象
            DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder dBuilder = dFactory.newDocumentBuilder();
            Document doc;
            doc = dBuilder.parse(new File(configFileName));

            //獲取包含類名的文本節點
            NodeList nl = doc.getElementsByTagName("imageType");
            Node node = nl.item(0).getFirstChild();
            String cName = node.getNodeValue().trim();
            
            //經過類名生成實例對象並將其返回
            Class c = Class.forName(packageName + "." + cName);//生成的是工廠名
            Object object = c.newInstance();
            return object;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

/**
 * Author: YiFan
 * Date: 2018/12/11 09:31
 * Description: 客戶端
 */
public class Test {

    // 獲取當前類所在的包名
    private static String packageName = Test.class.getPackage().getName();

    public static void main(String[] args) {
        ImgReader reader;
        ImgReaderFactory factory;

        // 強制轉換,將Object對象轉換成工廠對象
        factory = (ImgReaderFactory) XMLUtil.getBean("config.xml", packageName);
        reader = factory.createImgReader();
        reader.desc();
    }
}

配置文件放置項目根目錄下,配置文件以下:

<?xml version="1.0"?>
<config>
    <imageType>GifReaderFactory</imageType>
</config>

直接結果爲:

讀取GIF格式圖片

這樣一來之後若是須要使用其它的閱讀器工廠類建立閱讀器時,就不須要在客戶端代碼中進行修改,只須要修改 config.xml 配置文件便可。

相關文章
相關標籤/搜索