天天看點

簡單工廠模式

建立型設計模式:

簡單工廠模式、工廠方法模式、抽象工廠模式、單例模式、原型模式和建造者模式。 統共六種。

本此分享三種工廠模式中的簡單工廠模式。

記錄結構:

–1.實際問題引入(需求)

--2.傳統解決辦法

--3.傳統解決辦法帶來的問題

--4.使用xx模式解決問題

--4.1xx模式簡述

--4.2xx模式類圖表示

--4.3xx模式完整解決方案

--5.方案改進

--6.模式總結

1.簡單工廠模式: 1.1實際問題引入

YY軟體公司欲基于 Java 語言開發一套圖表庫,該圖表庫可以為應用系統提供各種不同外觀的圖表,例如柱 狀圖、餅狀圖、折線圖等。Sunny 軟體公司圖表庫設計人員希望為應用系統開發人員提供一套靈活易用的圖表 庫,而且可以較為友善地對圖表庫進行擴充,以便能夠在将來增加一些新類型的圖表。

2.2傳統解決辦法:

Sunny 軟體公司圖表庫設計人員提出了一個初始設計方案,将所有圖表的實作代碼封裝在一個 Chart 類中,其框 架代碼如下所示:

/**

* Created by tao.liu on 16/10/18.

*/

class Chart {

private String type; //圖表類型

public Chart(Object[][] data, String type) {

this.type = type;

if (type.equalsIgnoreCase("histogram")) {

//初始化柱狀圖

} else if (type.equalsIgnoreCase("pie")) {

//初始化餅狀圖

} else if (type.equalsIgnoreCase("line")) {

//初始化折線圖

}

public void display() {

if (this.type.equalsIgnoreCase("histogram")) {

//顯示柱狀圖

} else if (this.type.equalsIgnoreCase("pie")) {

//顯示餅狀圖

} else if (this.type.equalsIgnoreCase("line")) {

//顯示折線圖

用戶端代碼通過調用 Chart 類的構造函數來建立圖表對象,根據參數 type 的不同可以得到不同類型的圖表,然 後再調用 display() 方法來顯示相應的圖表。

3.傳統解決辦法帶來的問題

不難看出,Chart 類是一個“巨大的”類,在該類的設計中存在如下幾個問題:

(1) 在 Chart 類中包含很多if...else...代碼塊,整個類的代碼相當冗長,代碼越長,閱讀難度、維護難度和測試 難度也越大;而且大量條件語句的存在還将影響系統的性能,程式在執行過程中需要做大量的條件判斷。

(2) Chart 類的職責過重,它負責初始化和顯示所有的圖表對象,将各種圖表對象的初始化代碼和顯示代碼集中在 一個類中實作,違反了“單一職責原則”,不利于類的重用和維護;而且将大量的對象初始化代碼都寫在構造函 數中将導緻構造函數非常龐大,對象在建立時需要進行條件判斷,降低了對象建立的效率。

(3) 當需要增加新類型的圖表時,必須修改 Chart 類的源代碼,違反了“開閉原則”。

(4) 用戶端隻能通過 new 關鍵字來直接建立 Chart 對象,Chart 類與用戶端類耦合度較高,對象的建立和使用無 法分離。

(5) 用戶端在建立 Chart 對象之前可能還需要進行大量初始化設定,例如設定柱狀圖的顔色、高度等,如果在 Chart 類的構造函數中沒有提供一個預設設定,那就隻能由用戶端來完成初始設定,這些代碼在每次建立 Chart 對 象時都會出現,導緻代碼的重複。

面對一個如此巨大、職責如此重,且與用戶端代碼耦合度非常高的類,我們應該怎麼辦?本章将要介紹的簡單工廠模式将在一定程度上解決上述問題。

4.使用簡單工廠模式解決問題

4.1簡單工廠模式簡述:

簡單工廠模式并不屬于 GoF 23 個經典設計模式,但通常将它作為學習其他工廠模式的基礎,它的設計思想很簡單,其基本流程如下:

首先将需要建立的各種不同對象(例如各種不同的 Chart 對象)的相關代碼封裝到不同的類中,這些類稱為具體 産品類,而将它們公共的代碼進行抽象和提取後封裝在一個抽象産品類中,每一個具體産品類都是抽象産品類的子類;然後提供一個工廠類用于建立各種産品,在工廠類中提供一個建立産品的工廠方法,該方法可以根據所傳入的參數不同建立不同的具體産品對象;用戶端隻需調用工廠類的工廠方法并傳入相應的參數即可得到一個産品對象。

4.2簡單工廠模式類圖表示

簡單工廠模式

簡單工廠模式類圖

在簡單工廠模式結構圖中包含如下幾個角色:

Factory(工廠角色):工廠角色即工廠類,它是簡單工廠模式的核心,負責實作建立所有産品執行個體的内部邏 輯;工廠類可以被外界直接調用,建立所需的産品對象;在工廠類中提供了靜态的工廠方法 factoryMethod(),它的傳回類型為抽象産品類型 Product。

Product(抽象産品角色):它是工廠類所建立的所有對象的父類,封裝了各種産品對象的公有方法,它的 引入将提高系統的靈活性,使得在工廠類中隻需定義一個通用的工廠方法,因為所有建立的具體産品對象都 是其子類對象。

ConcreteProduct(具體産品角色):它是簡單工廠模式的建立目标,所有被建立的對象都充當這個角色的 某個具體類的執行個體。每一個具體産品角色都繼承了抽象産品角色,需要實作在抽象産品中聲明的抽象方法。

在簡單工廠模式中,用戶端通過工廠類來建立一個産品類的執行個體,而無須直接使用 new 關鍵字來建立對象,它是工廠模式家族中最簡單的一員。

分部代碼展示:

1.在使用簡單工廠模式時,首先需要對産品類進行重構,不能設計一個包羅萬象的産品類,而需根據實際情況設計 一個産品層次結構,将所有産品類公共的代碼移至抽象産品類,并在抽象産品類中聲明一些抽象方法,以供不同 的具體産品類來實作,典型的抽象産品類代碼如下所示:

abstract class Product {

//所有産品類的公共業務方法

public void methodSame() {

//公共方法的實作

//聲明抽象業務方法

public abstract void methodDiff();

2.在具體産品類中實作了抽象産品類中聲明的抽象業務方法,不同的具體産品類可以提供不同的實作,典型的具體 産品類代碼如下所示:

class ConcreteProduct extends Product {

//實作業務方法

public void methodDiff() {

//業務方法的實作

3.簡單工廠模式的核心是工廠類,在沒有工廠類之前,用戶端一般會使用 new 關鍵字來直接建立産品對象,而在引 入工廠類之後,用戶端可以通過工廠類來建立産品,在簡單工廠模式中,工廠類提供了一個靜态工廠方法供客戶 端使用,根據所傳入的參數不同可以建立不同的産品對象,典型的工廠類代碼如下所示:

class Factory {

//靜态工廠方法

public static Product getProduct(String arg) {

Product product = null;

if (arg.equalsIgnoreCase("A")) {

//初始化設定productA

product = new ConcreteProductA();

} else if (arg.equalsIgnoreCase("B")) {

//初始化設定productB

product = new ConcreteProductB();

return product;

4.在用戶端代碼中,我們通過調用工廠類的工廠方法即可得到産品對象,典型代碼如下所示:

class Client {

public static void main(String args[]) {

Product product;

//通過工廠類建立産品對象,沒有通過new關鍵字

product = Factory.getProduct("A");

product.methodSame();

product.methodDiff();

4.3簡單工廠模式完整解決方案

完整解決方案:

為了将 Chart 類的職責分離,同時将 Chart 對象的建立和使用分離,Sunny 軟體公司開發人員決定使用簡單工 廠模式對圖表庫進行重構,重構後的結構如圖所示:

圖檔 1.2 圖表庫結構圖

在圖中,Chart 接口充當抽象産品類,其子類HistogramChart、PieChart 和 LineChart 充當具體産品類,Ch artFactory 充當工廠類。完整代碼如下所示:

//抽象圖表接口:抽象産品類

interface Chart {

public void display();

//柱狀圖類:具體産品類

class HistogramChart implements Chart {

public HistogramChart() {

System.out.println("建立柱狀圖!");

System.out.println("顯示柱狀圖!");

//餅狀圖類:具體産品類

class PieChart implements Chart {

public PieChart() {

System.out.println("建立餅狀圖!");

System.out.println("顯示餅狀圖!");

//折線圖類:具體産品類

class LineChart implements Chart {

public LineChart() {

System.out.println("建立折線圖!");

System.out.println("顯示折線圖!");

//圖表工廠類:工廠類

class ChartFactory {

public static Chart getChart(String type) {

Chart chart = null;

chart = new HistogramChart();

System.out.println("初始化設定柱狀圖!");

chart = new PieChart();

System.out.println("初始化設定餅狀圖!");

chart = new LineChart();

System.out.println("初始化設定折線圖!");

return chart;

編寫如下用戶端測試代碼:

Chart chart;

chart = ChartFactory.getChart("histogram"); //通過靜态工廠方法建立産品

chart.display();

編譯并運作程式,輸出結果如下:

建立柱狀圖!

初始化設定柱狀圖!

顯示柱狀圖!

在用戶端測試類中,我們使用工廠類的靜态工廠方法建立産品對象,如果需要更換産品,隻需修改靜态工廠方法 中的參數即可,例如将柱狀圖改為餅狀圖,隻需将代碼:

chart = ChartFactory.getChart("histogram");

//改為

chart = ChartFactory.getChart("pie");

編譯程式,運作結果是:

建立餅狀圖!

初始化設定餅狀圖!

顯示餅狀圖!

5.方案改進

YY軟體公司開發人員發現在建立具體 Chart 對象時,每更換一個 Chart 對象都需要修改用戶端代碼中靜态 工廠方法的參數,用戶端代碼将要重新編譯,這對于用戶端而言,違反了“開閉原則”,有沒有一種方法能夠在 不修改用戶端代碼的前提下更換具體産品對象呢?答案是肯定的,下面将介紹一種常用的實作方式。

我們可以将靜态工廠方法的參數存儲在 XML 或 properties 格式的配置檔案中,如下 config.xml 所示:

histogram

再通過一個工具類 XMLUtil 來讀取配置檔案中的字元串參數,XMLUtil 類的代碼如下所示:

import javax.xml.parsers.*;

import org.w3c.dom.*;

import org.xml.sax.SAXException;

import java.io.*;

public class XMLUtil {

//該方法用于從XML配置檔案中提取圖表類型,并傳回類型名

public static String getChartType() {

try {

//建立文檔對象

DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();

DocumentBuilder builder = dFactory.newDocumentBuilder();

Document doc;

doc = builder.parse(new File("config.xml"));

//擷取包含圖表類型的文本節點

NodeList nl = doc.getElementsByTagName("chartType");

Node classNode = nl.item(0).getFirstChild();

String chartType = classNode.getNodeValue().trim();

return chartType;

} catch(Exception e) {

e.printStackTrace();

return null;

修改之後用戶端調用代碼如下所示:

String type = XMLUtil.getChartType(); //讀取配置檔案中的參數

chart = ChartFactory.getChart(type); //建立産品對象

不難發現,在上述用戶端代碼中不包含任何與具體圖表對象相關的資訊,如果需要更換具體圖表對象,隻需修改配置檔案 config.xml,無須修改任何源代碼,符合“開閉原則”。

6.模式總結: 簡單工廠模式提供了專門的工廠類用于建立對象,将對象的建立和對象的使用分離開,它作為一種最簡單的工廠模式在軟體開發中得到了較為廣泛的應用。 簡單工廠模式的主要優點如下: (1) 工廠類包含必要的判斷邏輯,可以決定在什麼時候建立哪一個産品類的執行個體,用戶端可以免除直接建立産品對象的職責,而僅僅“消費”産品,簡單工廠模式實作了對象建立和使用的分離。 (2) 用戶端無須知道所建立的具體産品類的類名,隻需要知道具體産品類所對應的參數即可,對于一些複雜的類名,通過簡單工廠模式可以在一定程度減少使用者的記憶量。 (3) 通過引入配置檔案,可以在不修改任何用戶端代碼的情況下更換和增加新的具體産品類,在一定程度上提高了系統的靈活性。 簡單工廠模式的主要缺點如下: (1) 由于工廠類集中了所有産品的建立邏輯,職責過重,一旦不能正常工作,整個系統都要受到影響。 (2) 使用簡單工廠模式勢必會增加系統中類的個數(引入了新的工廠類),增加了系統的複雜度和了解難度。

(3) 系統擴充困難,一旦添加新産品就不得不修改工廠邏輯,在産品類型較多時,有可能造成工廠邏輯過于複雜,不利于系統的擴充和維護。

(4) 簡單工廠模式由于使用了靜态工廠方法,造成工廠角色無法形成基于繼承的等級結構。 适用場景 在以下情況下可以考慮使用簡單工廠模式:

(1) 工廠類負責建立的對象比較少,由于建立的對象較少,不會造成工廠方法中的業務邏輯太過複雜。

(2) 用戶端隻知道傳入工廠類的參數,對于如何建立對象并不關心。