天天看點

GOF設計模式-建立型模式-簡單工廠模式工廠三兄弟之簡單工廠模式

工廠三兄弟之簡單工廠模式

模式定義:

簡單工廠模式(Simple Factory Pattern):定義一個工廠類,它可以根據參數的不同傳回不同類的 執行個體,被建立的執行個體通常都具有共同的父類。因為在簡單工廠模式中用于建立執行個體的方法是 靜态(static)方法,是以簡單工廠模式又被稱為靜态工廠方法(Static Factory Method)模式,它屬 于類建立型模式。

多讀幾遍定義 然後我們來看下它的結構:

GOF設計模式-建立型模式-簡單工廠模式工廠三兄弟之簡單工廠模式

簡單工廠模式的要點在于:當你需要什麼,隻需要傳入一個正确的參數,就可以擷取你所需 要的對象,而無須知道其建立細節。 

來來來  舉個栗子:

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

然後程式員出了個設計方案,并将圖表的實作代碼封裝在Chart類中:

/**  * description:  
* Created by yueli  
* on 2019/7/9  
*/ 
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對象時都會出現,導緻代碼的重複。

面對這種如此巨大、職責如此重,且與用戶端代碼耦合度非常高的類,我們應該怎麼辦?

先上代碼 看看我們用簡單工廠模式改造後的效果:

/**
 * description:
 * Created by yueli
 * on 2019/7/9
 */
//抽象圖表接口:抽象産品類
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("顯示折線圖!");                 
    }     
}

//圖表工廠類:工廠類 
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;                
    }     
}
//編寫如下用戶端測試代碼:
class Client {                 
    public static void main(String    args[])    {                             
        Chart  chart;                            
        chart  =  ChartFactory.getChart("histogram");    //通過靜态工廠方法建立産品 
 chart.display();                  
        }     
}      

現在的代碼結構:

GOF設計模式-建立型模式-簡單工廠模式工廠三兄弟之簡單工廠模式

是不是對強迫症很治愈!!!當我們需要切換執行個體時唯一的改動隻是用戶端的入參:

chart = ChartFactory.getChart("pie");

但是還是有改動,不符合開閉原則了,我要換圖表還是要改用戶端的代碼。這時候我們的解決辦法是将靜态工廠方法的參數存儲在properties中然後在程式中引用它:

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

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

至此簡單工廠模式的設計demo就講完了。

下面我們分析下這種模式:

簡單工廠模式的優點:

1.用戶端免除了建立産品對象的職責,僅僅是“消費“産品,實作了對象和使用的分離;

2.用戶端無需知道具體産品類的類名,隻需知道對應産品的對應入參,減少記憶量;

3.通過引入配置檔案可以不修改用戶端的情況下更換産品,提高靈活性;

缺點:

1.工廠類集中了所有産品的建立邏輯,職責過重;

2.式勢必會增加系統中類的個數,增加了系統的複雜 度和了解難度;

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

4.由于使用了靜态工廠方法,造成工廠角色無法形成基于繼承的等級結構。

适用場景:

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

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

來來思考下為啥靜态?

static方法可以通過類名通路,也可以通過類的執行個體通路。

static方法不能通路類中非static的資料。

比如

class A {      static void F(){} };

 在main函數中可以

A a;

a.F();

也可以 A.F();

普通方法又叫執行個體方法,隻能通過類的執行個體通路。

他隻能a.F();     

一個JAVA類被加載的順序:

1.加載靜态成員、代碼塊

2.加載非靜态成員、代碼塊

3.調用構造方法。

其實不用靜态方法也可以,隻是用靜态方法之後,就不用初始化工廠而直接得到産品。

繼續閱讀