1. 程式人生 > >JAVA設計模式(01):建立型-工廠模式【簡單工廠模式】(Simple Factory)

JAVA設計模式(01):建立型-工廠模式【簡單工廠模式】(Simple Factory)

     工廠模式是最常用的一類建立型設計模式,通常我們所說的工廠模式是指工廠方法模式,它也是使用頻率最高的工廠模式。本章將要學習的簡單工廠模式是工廠方法模式的“小弟”,它不屬於GoF 23種設計模式,但在軟體開發中應用也較為頻繁,通常將它作為學習其他工廠模式的入門。此外,工廠方法模式還有一位“大哥”——抽象工廠模式。這三種工廠模式各具特色,難度也逐個加大,在軟體開發中它們都得到了廣泛的應用,成為面向物件軟體中常用的建立物件的工具。

1 圖表庫的設計

       Sunny軟體公司欲基於Java語言開發一套圖表庫,該圖表庫可以為應用系統提供各種不同外觀的圖表,例如柱狀圖、餅狀圖、折線圖等。Sunny

軟體公司圖表庫設計人員希望為應用系統開發人員提供一套靈活易用的圖表庫,而且可以較為方便地對圖表庫進行擴充套件,以便能夠在將來增加一些新型別的圖表。

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

public 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()方法來顯示相應的圖表。

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

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

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

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

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

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

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

2 簡單工廠模式概述

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

       首先將需要建立的各種不同物件(例如各種不同的Chart物件)的相關程式碼封裝到不同的類中,這些類稱為具體產品類,而將它們公共的程式碼進行抽象和提取後封裝在一個抽象產品類中,每一個具體產品類都是抽象產品類的子類;然後提供一個工廠類用於建立各種產品,在工廠類中提供一個建立產品的工廠方法,該方法可以根據所傳入的引數不同建立不同的具體產品物件;客戶端只需呼叫工廠類的工廠方法並傳入相應的引數即可得到一個產品物件。

       簡單工廠模式定義如下:

       簡單工廠模式(Simple Factory Pattern):定義一個工廠類,它可以根據引數的不同返回不同類的例項,被建立的例項通常都具有共同的父類。因為在簡單工廠模式中用於建立例項的方法是靜態(static)方法,因此簡單工廠模式又被稱為靜態工廠方法(Static Factory Method)模式,它屬於類建立型模式。

簡單工廠模式的要點在於:當你需要什麼,只需要傳入一個正確的引數,就可以獲取你所需要的物件,而無須知道其建立細節。簡單工廠模式結構比較簡單,其核心是工廠類的設計,其結構如圖1所示:

1  簡單工廠模式結構圖

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

       ● Factory(工廠角色):工廠角色即工廠類,它是簡單工廠模式的核心,負責實現建立所有產品例項的內部邏輯;工廠類可以被外界直接呼叫,建立所需的產品物件;在工廠類中提供了靜態的工廠方法factoryMethod(),它的返回型別為抽象產品型別Product

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

       ● ConcreteProduct(具體產品角色):它是簡單工廠模式的建立目標,所有被建立的物件都充當這個角色的某個具體類的例項。每一個具體產品角色都繼承了抽象產品角色,需要實現在抽象產品中宣告的抽象方法。

       在簡單工廠模式中,客戶端通過工廠類來建立一個產品類的例項,而無須直接使用new關鍵字來建立物件,它是工廠模式家族中最簡單的一員。

       在使用簡單工廠模式時,首先需要對產品類進行重構,不能設計一個包羅永珍的產品類,而需根據實際情況設計一個產品層次結構,將所有產品類公共的程式碼移至抽象產品類,並在抽象產品類中宣告一些抽象方法,以供不同的具體產品類來實現,典型的抽象產品類程式碼如下所示:

public abstract class Product {  
    //所有產品類的公共業務方法  
    public void methodSame() {  
        //公共方法的實現  
    }  
  
    //宣告抽象業務方法  
    public abstract void methodDiff();  
} 

       在具體產品類中實現了抽象產品類中宣告的抽象業務方法,不同的具體產品類可以提供不同的實現,典型的具體產品類程式碼如下所示:

public class ConcreteProduct extends Product {  
    //實現業務方法  
    public void methodDiff() {  
        //業務方法的實現  
    }  
}  

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

public class Factory {  
    //靜態工廠方法  
    public static Product getProduct(String arg) {  
        Product product = null;  
        if (arg.equalsIgnoreCase("A")) {  
            product = new ConcreteProductA();  
            //初始化設定product  
        }  
        else if (arg.equalsIgnoreCase("B")) {  
            product = new ConcreteProductB();  
            //初始化設定product  
        }  
        return product;  
    }  
}  

       在客戶端程式碼中,我們通過呼叫工廠類的工廠方法即可得到產品物件,典型程式碼如下所示:

public class Client {  
    public static void main(String args[]) {  
        Product product;   
        product = Factory.getProduct("A"); //通過工廠類建立產品物件  
        product.methodSame();  
        product.methodDiff();  
    }  
} 

3 完整解決方案

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

圖表庫結構圖

       在圖2中,Chart介面充當抽象產品類,其子類HistogramChartPieChartLineChart充當具體產品類,ChartFactory充當工廠類。完整程式碼如下所示:

//抽象圖表介面:抽象產品類  
public interface Chart {  
    public void display();  
}  
  
//柱狀圖類:具體產品類  
class HistogramChart implements Chart {  
    public HistogramChart() {  
        System.out.println("建立柱狀圖!");  
    }  
      
    public void display() {  
        System.out.println("顯示柱狀圖!");  
    }  
}  
  
//餅狀圖類:具體產品類  
class PieChart implements Chart {  
    public PieChart() {  
        System.out.println("建立餅狀圖!");  
    }  
      
    public void display() {  
        System.out.println("顯示餅狀圖!");  
    }  
}  
  
//折線圖類:具體產品類  
class LineChart implements Chart {  
    public LineChart() {  
        System.out.println("建立折線圖!");  
    }  
      
    public void display() {  
        System.out.println("顯示折線圖!");  
    }  
}  
  
//圖表工廠類:工廠類  
public class ChartFactory {  
    //靜態工廠方法  
    public static Chart getChart(String type) {  
        Chart chart = null;  
        if (type.equalsIgnoreCase("histogram")) {  
            chart = new HistogramChart();  
            System.out.println("初始化設定柱狀圖!");  
        }  
        else if (type.equalsIgnoreCase("pie")) {  
            chart = new PieChart();  
            System.out.println("初始化設定餅狀圖!");  
        }  
        else if (type.equalsIgnoreCase("line")) {  
            chart = new LineChart();  
            System.out.println("初始化設定折線圖!");              
        }  
        return chart;  
    }  
}  

       編寫如下客戶端測試程式碼:

public class Client {  
    public static void main(String args[]) {  
        Chart chart = ChartFactory.getChart("histogram"); //通過靜態工廠方法建立產品  
        chart.display();  
    }  
} 

       編譯並執行程式,輸出結果如下:

建立柱狀圖!

初始化設定柱狀圖!

顯示柱狀圖!

       在客戶端測試類中,我們使用工廠類的靜態工廠方法建立產品物件,如果需要更換產品,只需修改靜態工廠方法中的引數即可,例如將柱狀圖改為餅狀圖,只需將程式碼:

chart = ChartFactory.getChart("histogram");

改為:

chart = ChartFactory.getChart("pie");

       編譯並執行程式,輸出結果如下:

建立餅狀圖!

初始化設定餅狀圖!

顯示餅狀圖!

4 方案的改進

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

       我們可以將靜態工廠方法的引數儲存在XMLproperties格式的配置檔案中,如下config.xml所示:

<?xml version="1.0" encoding="UTF-8"?>  
<config>    
    <chartType>histogram</chartType>    
</config>   

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

public class XMLUtil {  
    //該方法用於從XML配置檔案中提取圖表型別,並返回型別名    
    public static String getChartType() {  
        SAXReader reader = new SAXReader();  
        try {  
            String path = XMLUtil.class.getClassLoader().  
                    getResource("com/somnus/designPatterns/simpleFactory/config.xml").getPath();  
            Document document = reader.read(new File(path));  
            String chartType = document.selectSingleNode("/config/chartType").getText();  
            return chartType;  
        } catch (DocumentException e) {  
            e.printStackTrace();  
            return null;  
        }  
    }    
}  


       在引入了配置檔案和工具類XMLUtil之後,客戶端程式碼修改如下:

public class Client {  
    public static void main(String args[]) { 
        String type = XMLUtil.getChartType(); //讀取配置檔案中的引數  
        Chart chart = ChartFactory.getChart(type); //建立產品物件  
        chart.display();  
    }  
}  

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

疑問

思考

在簡單工廠模式中增加新的具體產品時是否符合“開閉原則”?如果不符合,原有系統需作出哪些修改?

5 簡單工廠模式的簡化

       有時候,為了簡化簡單工廠模式,我們可以將抽象產品類和工廠類合併,將靜態工廠方法移至抽象產品類中,如圖3所示:

簡化的簡單工廠模式

       在圖3中,客戶端可以通過產品父類的靜態工廠方法,根據引數的不同建立不同型別的產品子類物件,這種做法在JDK等類庫和框架中也廣泛存在。

6 簡單工廠模式總結

      簡單工廠模式提供了專門的工廠類用於建立物件,將物件的建立和物件的使用分離開,它作為一種最簡單的工廠模式在軟體開發中得到了較為廣泛的應用。

         1. 主要優點

       簡單工廠模式的主要優點如下:

       (1) 工廠類包含必要的判斷邏輯,可以決定在什麼時候建立哪一個產品類的例項,客戶端可以免除直接建立產品物件的職責,而僅僅“消費”產品,簡單工廠模式實現了物件建立和使用的分離。

       (2) 客戶端無須知道所建立的具體產品類的類名,只需要知道具體產品類所對應的引數即可,對於一些複雜的類名,通過簡單工廠模式可以在一定程度減少使用者的記憶量。

       (3) 通過引入配置檔案,可以在不修改任何客戶端程式碼的情況下更換和增加新的具體產品類,在一定程度上提高了系統的靈活性。

         2. 主要缺點

       簡單工廠模式的主要缺點如下:

       (1) 由於工廠類集中了所有產品的建立邏輯,職責過重,一旦不能正常工作,整個系統都要受到影響。

       (2) 使用簡單工廠模式勢必會增加系統中類的個數(引入了新的工廠類),增加了系統的複雜度和理解難度。

       (3) 系統擴充套件困難,一旦新增新產品就不得不修改工廠邏輯,在產品型別較多時,有可能造成工廠邏輯過於複雜,不利於系統的擴充套件和維護。

       (4) 簡單工廠模式由於使用了靜態工廠方法,造成工廠角色無法形成基於繼承的等級結構。

        3. 適用場景

       在以下情況下可以考慮使用簡單工廠模式:

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

       (2) 客戶端只知道傳入工廠類的引數,對於如何建立物件並不關心。

疑問

練習

使用簡單工廠模式設計一個可以建立不同幾何形狀(如圓形、方形和三角形等)的繪圖工具,每個幾何圖形都具有繪製draw()和擦除erase()兩個方法,要求在繪製不支援的幾何圖形時,提示一個UnSupportedShapeException


相關推薦

JAVA設計模式01建立-工廠模式簡單工廠模式Simple Factory

     工廠模式是最常用的一類建立型設計模式,通常我們所說的工廠模式是指工廠方法模式,它也是使用頻率最高的工廠模式。本章將要學習的簡單工廠模式是工廠方法模式的“小弟”,它不屬於GoF 23種設計模式,但在軟體開發中應用也較為頻繁,通常將它作為學習其他工廠模式的入門。此

Java設計模式簡介建立模式

設計模式(Design pattern)是一套被反覆使用、多數人知曉的、經過分類編目的、程式碼設計經驗的總結。使用設計模式是為了可重用程式碼、讓程式碼更容易被他人理解、保證程式碼可靠性。 毫無疑問,設計模式於己於他人於系統都是多贏的,設計模式使程式碼編制真正工程化,設計模式是軟體工程的基石,如同大廈

JAVA設計模式24建立-工廠模式工廠方法模式(Factory Method)

簡單工廠模式雖然簡單,但存在一個很嚴重的問題。當系統中需要引入新產品時,由於靜態工廠方法通過所傳入引數的不同來建立不同的產品,這必定要修改工廠類的原始碼,將違背“開閉原則”,如何實現增加新產品而不影響已有程式碼?工廠方法模式應運而生,本文將介紹第二種工廠模式——工廠方法模

人人都能讀懂的設計模式1建立模式

簡介 設計模式用於解決反覆出現的問題,是解決特定問題的指導方針。設計模式不是在應用中引用的類、package 或者庫,而是在某些特定場景下解決特定問題的指導方針。 設計模式用於解決反覆出現的問題,是解決某些特定問題的指導方針。 維基百科中這樣描述設計模式: 在軟體工

作業系統程序間共享儲存區的通訊):建立一個共享儲存區,大小4個位元組int大小建立一個子程序,然後子父程序獨自執行。父程序寫入一個數字到共享儲存區,子程序在共享儲存區把該數字讀出

題目:建立一個共享儲存區,大小4個位元組(int大小)。建立一個子程序,然後子父程序獨自執行。父程序寫入一個數字到共享儲存區,子程序在共享儲存區把該數字讀出。 程式碼量很少,純屬應付作業 筆者在telnet上寫這些程式碼,由於知識技術及英語太菜,不知道怎樣在telnet上覆製出來這些程式碼,

Java設計模式建立模式原型模式

一、定義: 用原型例項指定建立物件的種類,並通過拷貝這些原型建立新的物件。 UML類圖: 原型模式主要用於物件的複製,它的核心是就是類圖中的原型類Prototype。Prototype類需要具備以下兩個條件: (1)實現Cloneable介面:在java語言有一個Cloneab

Java設計模式建立模式建造者模式

一、定義: 建造者模式將一個複雜物件的構建與表示分離,使得同樣的構建過程可以建立不同的表示。 建造者模式的UML結構圖: 建造者模式主要包含四個角色:        Builder:抽象建造者。它宣告為建立一

Java設計模式建立模式單例模式

一、概念: java中單例模式是一種常見的設計模式,單例模式的寫法有好幾種,這裡主要介紹三種:懶漢式單例、餓漢式單例、登記式單例。 單例模式有以下特點: (1)單例類只能有一個例項; (2)單例類必須自己建立自己的唯一例項; (3)單例類必須給所有其他物件提供這一例項。 單例

Java設計模式建立模式抽象工廠模式

例子背景: 隨著客戶的要求越來越高,寶馬車需要不同配置的空調和發動機等配件。於是這個工廠開始生產空調和發動機,用來組裝汽車。這時候工廠有兩個系列的產品:空調和發動機。寶馬320系列配置A型號空調和A型號發動機,寶馬230系列配置B型號空調和B型號發動機。   一、概念:

Java設計模式建立模式工廠模式簡單工廠模式+工廠方法模式

在面向物件程式設計中, 最通常的方法是一個new操作符產生一個物件例項,new操作符就是用來構造物件例項的。但是在一些情況下, new操作符直接生成物件會帶來一些問題。舉例來說,許多型別物件的建立需要一系列的步驟:你可能需要計算或取得物件的初始位置;選擇生成哪個子物件例項;或在你生成你需要的物件

Java設計模式簡介行為模式

其實每個設計模式都是很重要的一種思想,看上去很熟,其實是因為我們在學到的東西中都有涉及,儘管有時我們並不知道,其實在Java本身的設計之中處處都有體現,像AWT、JDBC、集合類、IO管道或者是Web框架,裡面設計模式無處不在。因為我們篇幅有限,很難講每一個設計模式都講的很詳細。 本章講講

Java設計模式簡介行為模式

本章講到第三種設計模式——行為型模式,共11種:策略模式、模板方法模式、觀察者模式、迭代子模式、責任鏈模式、命令模式、備忘錄模式、狀態模式、訪問者模式、中介者模式、直譯器模式。 先來張圖,看看這11中模式的關係: 第一類:通過父類與子類的關係進行實現。第二類:兩個類之間。第三類:類的狀態。第

Java設計模式簡介結構模式

我們接著討論設計模式,上篇文章我講完了5種建立型模式,這章開始,我將講下7種結構型模式:介面卡模式、裝飾模式、代理模式、外觀模式、橋接模式、組合模式、享元模式。其中物件的介面卡模式是各種模式的起源,我們看下面的圖:   6、介面卡模式(Adapter): 介面卡模式將某個

設計模式2——建立——工廠相關簡單工廠Simple factory工廠方法Factory method,抽象工廠Abstract factory

概要 這裡試圖描述23個設計模式中的兩個工廠(Factory)相關的設計模式:工廠方法(Factorymethod),抽象工廠(Abstract factory)。 注意點: 這兩個都屬於建立型設計模式。 由於這兩個設計模式都

JAVA設計模式13行為-責任鏈模式Responsibility

“一對二”,“過”,“過”……這聲音熟悉嗎?你會想到什麼?對!紙牌。在類似“鬥地主”這樣的紙牌遊戲中,某人出牌給他的下家,下家看看手中的牌,如果要不起上家的牌則將出牌請求再轉發給他的下家,其下家再進行判斷。一個迴圈下來,如果其他人都要不起該牌,則最初的出牌者可以打出新的牌。在這個過程中,牌作為一個

JAVA設計模式16行為-策略模式Strategy

俗話說:條條大路通羅馬。在很多情況下,實現某個目標的途徑不止一條,例如我們在外出旅遊時可以選擇多種不同的出行方式,如騎自行車、坐汽車、坐火車或者坐飛機,可根據實際情況(目的地、旅遊預算、旅遊時間等)來選擇一種最適合的出行方式。在制訂旅行計劃時,如果目的地較遠、時間不多,但

JAVA設計模式07結構-橋接模式Bridge

     在正式介紹橋接模式之前,我先跟大家談談兩種常見文具的區別,它們是毛筆和蠟筆。假如我們需要大中小3種型號的畫筆,能夠繪製12種不同的顏色,如果使用蠟筆,需要準備3×12 = 36支,但如果使用毛筆的話,只需要提供3種型號的毛筆,外加12個顏料盒即可,涉及到的物

JAVA設計模式22行為-直譯器模式Interpreter

       Sunny軟體公司欲為某玩具公司開發一套機器人控制程式,在該機器人控制程式中包含一些簡單的英文控制指令,每一個指令對應一個表示式(expression),該表示式可以是簡單表示式也可以是複合表示式,每一個簡單表示式由移動方向(direction),移動方式(action)和移動距離(distan

JAVA設計模式學習筆記建立模式——01

設計模式的六大原則 1.開閉原則(Open Close Principle) 開閉原則的意思是:對擴充套件開放,對修改關閉。在程式需要進行拓展的時候,不能去修改原有的程式碼,實現一個熱插拔的效果。簡言之,是為了使程式的擴充套件性好,易於維護和升級。想要達到

面向物件設計設計模式結構模式附 Demo & UML類圖

本篇是面向物件設計系列文章的第三篇,講解的是設計模式中的結構型模式: 外觀模式 介面卡模式 橋接模式 代理模式 裝飾者模式 享元模式 該系列前面的兩篇文章: 面向物件設計的六大設計原則(附 Demo 及 UML 類圖) 面向物件設計的設計模式(一):建