設計模式詳解(二):工廠方法模式、抽象工廠模式

2. 工廠方法模式html

(1)概念java

工廠方法模式的定義是:定義一個用於建立對象的接口,讓子類決定實現哪個類。設計模式

即工廠父類負責定義建立產品對象的公共接口,工廠子類負責生成具體的產品對象。框架

將產品類的實例化操做延遲到工廠子類中完成,即經過工廠子類來肯定究竟應該實例化哪個具體產品類。dom

工廠方法模式是簡單工廠模式的延伸與改進,既繼承了其封裝性等優勢,又彌補了其缺陷(提升擴展性),使其符合原則的要求。ide

在工廠方法模式中,每個具體工廠只能生產一種具體產品。具體工廠與具體產品一一對應。工具

(2)類圖、典型代碼ui

Factory(抽象類或接口):抽象工廠類,聲明瞭工廠方法,用於返回一個產品。抽象工廠是工廠方法模式的核心,它與應用程序無關。spa

ConcreteFactory(實現類):具體工廠類,實現抽象工廠類中聲明的方法,可由客戶端調用,返回一個具體產品類的實例。設計

Product(抽象類或接口):抽象產品類,定義了產品的接口,是產品對象的共同父類或接口。

ConcreteProduct(實現類):具體產品類,實現了抽象產品類的方法,其具體產品由對應的具體工廠建立。

典型代碼以下:

//抽象工廠類
public interface Factory {
    public Product factoryMethod();
}
//具體工廠類之一
public class ConcreteFactory implements Factory {
    @Override
    public Product factoryMethod() {
        return new ConcreteFactory();
    }
}

(3)舉例

原有一個工廠生產一種電視機,現分爲兩個子工廠:海爾工廠生產海爾電視機,海信工廠生產海信電視機。

根據工廠方法模式設計類圖以下:

實現代碼以下:

//抽象產品類,定義全部產品必須實現的方法
public interface TV {
    public void play();
}
//具體產品類1--海爾電視
public class HaierTV implements TV {

    @Override
    public void play() {
        // 海爾電視的功能
        System.out.println("海爾電視播放中...");
    }

}
//具體產品類2--海信電視
public class HisenseTV implements TV {

    @Override
    public void play() {
        // 海信電視的功能
        System.out.println("海信電視播放中...");
    }

}
//抽象工廠類,定義全部工廠必須實現的方法
public interface TVFactory {
    public TV produceTV();
}
//具體產品類1--海爾電視
public class HaierTV implements TV {

    @Override
    public void play() {
        // 海爾電視的功能
        System.out.println("海爾電視播放中...");
    }

}
//具體產品類2--海信電視
public class HisenseTV implements TV {

    @Override
    public void play() {
        // 海信電視的功能
        System.out.println("海信電視播放中...");
    }

}
//客戶端調用類--調用具體電視工廠生產對應電視
public class Client {

    public static void main(String[] args) {
        TVFactory factory;
        TV tv;

        // 產生海爾電視並調用其功能
        factory = new HaierTVFactory();
        tv = factory.produceTV();
        tv.play();

        // 產生海信電視並調用其功能
        factory = new HisenseTVFactory();
        tv = factory.produceTV();
        tv.play();
    }

}

輸出結果以下:

海爾電視播放中...
海信電視播放中...

若是此時還須要增長一種電視機品牌TCL,則只須要新建一個實現TV接口的具體產品類和一個實現TVFactory接口的具體工廠類便可,不須要修改其餘代碼:

//新增具體產品類3--TCL電視
public class TCLTV implements TV {

    @Override
    public void play() {
        // TCL電視的功能
        System.out.println("TCL電視播放中...");
    }

}
//新增具體工廠類3--專門生產TCL電視的TCL工廠
public class TCLTVFactory implements TVFactory {

    @Override
    public TV produceTV() {
        // 生產一個TCL電視的對象
        return new TCLTV();
    }

}

在客戶端調用類中增長調用TCL工廠生產TCL電視的語句:

        // 產生TCL電視並調用其功能
        factory = new TCLTVFactory();
        tv = factory.produceTV();
        tv.play();

輸出結果以下:

海爾電視播放中...
海信電視播放中...
TCL電視播放中...

(4)優缺點、適用場景

工廠方法模式的優勢:

  用戶只需關心所需產品對應的工廠,無須關心建立細節(屏蔽產品類),甚至不須要知道具體產品的類名;

  典型的解耦框架,高層模塊須要知道產品的抽象類,其餘的實現類都不用關心;

  良好的封裝性,代碼結構清晰,優秀的擴展性,同時符合開閉原則。

工廠方法模式的缺點:

  在添加新產品時成對增長了類的個數,增長了系統的複雜度,編譯和運行更多的類也會增長系統的開銷;

  考慮到可擴展性引入抽象層,在客戶端代碼中均使用抽象層進行定義,增長了系統的抽象性和理解難度。

工廠方法模式的適用場景:

  須要靈活、可擴展的框架時;

  當一個類(好比客戶端類)不知道所須要的對象的類時(須要知道其對應的工廠);

  一個類經過其子類來肯定建立那個對象。

 

3. 抽象工廠模式

(1)概念

抽象工廠模式的定義是:爲建立一組相關或相互依賴的對象提供一個接口,並且無需指定他們的具體類。

抽象工廠模式是工廠方法模式的泛化版,即工廠方法模式只是抽象工廠模式的一種特殊狀況。

在抽象工廠模式中,每個具體工廠能夠生產多個具體產品。

(2)類圖、典型代碼

 

AbstractFactory(抽象類或接口):抽象工廠類,用於聲明生產產品的方法。

ConcreteFactory(實現類):具體工廠類,具體實現生產產品的方法,返回一個具體產品。

AbstractProduct(抽象類或接口):抽象產品類,定義了每種產品的功能方法。

ConcreteProduct(實現類):具體產品類,具體實現了抽象產品類定義的功能方法。

典型代碼以下:

// 抽象工廠類
public interface AbstractFactory {
    public AbstractProductA createProductA();
    public AbstractProductB createProductB();
}
// 具體工廠類之一
public class ConcreteFactory implements AbstractFactory{
    public AbstractProductA createProductA(){
        return new ConcreteProductA();
    }
    public AbstractProductB createProductB(){
        return new ConcreteProductB();
    }
}

(3)舉例

一個電器工廠能夠生產多種電器,好比海爾工廠能夠生產海爾電視和海爾空調,TCL工廠能夠生產TCL電視和TCL空調。

根據抽象工廠模式設計類圖以下:

實現代碼以下:

//抽象產品類1--電視類
public interface TV {
    public void play();
}
//電視具體產品類1--海爾電視
public class HaierTV implements TV {

    @Override
    public void play() {
        System.out.println("海爾電視播放中...");
    }

}
//電視具體產品類2--TCL電視
public class TCLTV implements TV {

    @Override
    public void play() {
        System.out.println("TCL電視播放中...");
    }

}
//抽象產品類2--空調類
public interface AirConditioner {
    public void changeTemperature();
}
//空調具體產品類1--海爾空調
public class HaierAirConditioner implements AirConditioner {

    @Override
    public void changeTemperature() {
        System.out.println("海爾空調吹風中...");
    }

}
//空調具體產品類2--TCL空調
public class TCLAirConditioner implements AirConditioner {

    @Override
    public void changeTemperature() {
        System.out.println("TCL空調吹風中...");
    }

}
//抽象工廠類,定義全部工廠必須實現的方法
public interface Factory {
    public TV produceTV();

    public AirConditioner produceAirConditioner();
}
//具體工廠類1--海爾工廠
public class HaierFactory implements Factory{

    @Override
    public TV produceTV() {
        return new HaierTV();
    }

    @Override
    public AirConditioner produceAirConditioner() {
        return new HaierAirConditioner();
    }

}
//具體工廠類2--TCL工廠
public class TCLFactory implements Factory{

    @Override
    public TV produceTV() {
        return new TCLTV();
    }

    @Override
    public AirConditioner produceAirConditioner() {
        return new TCLAirConditioner();
    }

}

運行結果以下:

海爾電視播放中...
海爾空調吹風中...
———————————————
TCL電視播放中...
TCL空調吹風中...

(4)優缺點、適用場景

抽象工廠模式的優勢:

  隔離了具體類的生成,使客戶並不知道什麼被建立;

  產品內的約束爲非公開狀態(好比不一樣產品的生產比例,這對調用工廠類的高層模塊是透明的);

抽象工廠模式的缺點:

  在添加新的產品對象時,難以擴展抽象工廠來生產新種類的產品(對接口進行擴展會致使全部子類的修改);

抽象工廠模式的適用場景:

  一個系統不依賴產品類實例如何被建立、組合和表達的細節時;

  系統中有多個產品族,而每次只使用其中某一產品族時;

  屬於同一產品族的產品將一塊兒使用;

  多個對象有相同的約束時。

 

在實際的應用開發中,通常將具體類的類名寫入配置文件中,再經過Java的反射機制讀取XML格式的配置文件,根據存儲在XML文件中的類名字符串生成對象。

新建XML文件config.xml以下:

<?xml version="1.0" encoding="UTF-8"?>
<config>
    <className>xxxFactory</className>
</config>

新建工具類文件XMLUtil.java以下:

package com.test.util;

import java.io.File;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class XMLUtil {
    // 該方法用於從XML配置文件中提取類名字符串,並返回一個實例對象
    public static Object getBean() {
        try {
            // 建立DOM文檔對象
            DocumentBuilderFactory dFactory = DocumentBuilderFactory
                    .newInstance();
            DocumentBuilder builder = dFactory.newDocumentBuilder();
            Document doc = builder.parse(new File(
                    "./src/com/test/util/config.xml"));// 若不在同一路徑下,必須指定文件具體路徑,用正斜槓/或雙反斜槓\\

            // 獲取包含類名的文本節點
            NodeList nlist = doc.getElementsByTagName("className");
            Node classNode = nlist.item(0).getFirstChild();
            String cName = classNode.getNodeValue();

            // 經過類名生成實例對象並將其返回
            Class c = Class.forName("com.test.factory_method." + cName);// 若不在同一路徑下,必須寫出類的全名
            Object obj = c.newInstance();
            return obj;

        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

改進工廠方法模式中的客戶端代碼以下:

//客戶端調用類--調用具體電視工廠生產對應電視
public class Client {

    public static void main(String[] args) {
        TVFactory factory;
        TV tv;

//        // 產生海爾電視並調用其功能
//        factory = new HaierTVFactory();
//        tv = factory.produceTV();
//        tv.play();
//
//        // 產生海信電視並調用其功能
//        factory = new HisenseTVFactory();
//        tv = factory.produceTV();
//        tv.play();
//        
//        // 產生TCL電視並調用其功能
//        factory = new TCLTVFactory();
//        tv = factory.produceTV();
//        tv.play();
        
        // 將具體類名寫入配置文件中,再經過Java反射機制讀取XML格式文件,根據類名生成對象並返回
        factory = (TVFactory) XMLUtil.getBean();
        tv = factory.produceTV();
        tv.play();
    }

}

將config.xml文件中的「xxxFactory」更改成須要生成對象的類名「HaierTVFactory」,運行客戶端結果以下:

海爾電視播放中...

抽象工廠模式中例子的客戶端改進與此相同。

 

 

6大設計原則,與常見設計模式(概述):http://www.cnblogs.com/LangZXG/p/6204142.html

類圖基礎知識:http://www.cnblogs.com/LangZXG/p/6208716.html

 

注:轉載請註明出處   http://www.cnblogs.com/LangZXG/p/6249425.html

相關文章
相關標籤/搜索