Java設計模式學習筆記(二) 簡單工廠模式

前言

本篇是設計模式學習筆記的其中一篇文章,如對其餘模式有興趣,可從該地址查找設計模式學習筆記彙總地址html

正文開始...git

1. 簡介

簡單工廠模式不屬於GoF23中設計模式之一,但在軟件開發中應用也較爲頻繁,一般作爲學習其餘工廠模式的入門.github

接下來咱們從一個虛構的業務場景遇到的問題開始,到如何使用簡單工廠模式去解決這個業務場景的問題的角度,來介紹這個模式.spring

2. 具體業務

有一個圖表類,能夠在實例化的時候根據傳入的參數建立不一樣的圖表類型,好比柱狀圖、餅狀圖、折線圖等等.設計模式

2.1 業務代碼

/**

 * @author liuboren

 * @Title: 圖標類

 * @Description: 根據不一樣的參數,建立不一樣的圖表

 * @date 2019/7/12 14:31

 */

public class Chart {



    private String type;



    public Chart(Object[][] obj,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()方法來顯示相應的圖表.springboot

2.2 問題

上述代碼主要有如下五個問題ide

  • 過多的"if...else.."不易維護且影響性能
  • 違反了單一職責
  • 違反了開閉原則
  • 與客戶端耦合度高
  • 代碼重複問題

詳細的看看以上的問題函數

2.2.1 過多的"if...else.."不易維護且影響性能

在Chart類中包含不少"if...else..."代碼塊,整個類的代碼至關冗長,代碼越長,閱讀難度、維護難度和測試難度也越大;並且大量條件語句的存在還將影響系統的性能,程序在執行過程當中須要作大量的判斷性能

2.2.2 違反了單一職責

Chart類的職責太重,它負責初始化和顯示全部的圖表對象, 各類圖表對象的初始化代碼和顯示代碼集中在一個類中實現,違反了"單一職責原則",不利於類的重用和維護;學習

並且將大量的對象初始化代碼都寫在構造函數中將致使構造函數很是龐大,對象在建立時須要進行條件判斷,下降了對象建立的效率

2.2.3 違反了開閉原則

當須要增長新類型的圖表時,必須修改Chart的源代碼,違反了"開閉原則".

2.2.4 與客戶端耦合度高

客戶端只能經過new關鍵字來直接建立Chart對象,Chart類與客戶端類耦合度較高,對象的建立和使用沒法分類.

2.2.5 代碼重複問題

客戶端在建立Chart對象以前可能還須要進行大量初始化設置,例如設置柱狀圖的顏色、高度等,若是在Chart類的構造函數中沒有提供一個默認設置,那就只能由客戶端來完成初始設置,這些代碼在每次建立Chart對象時都會出現,致使代碼的重複.

3. 簡單工廠模式

使用簡單工廠模式,能夠在必定程度上解決.

3.1 簡單工廠的基本流程

  1. 首先將須要建立的各類不一樣對象(例如各類不一樣的Chart對象)的相關代碼封裝到不一樣的類中,這些類稱爲具體產品類,而將他們公共的代碼進行抽象和提取後封裝在一個抽象產品類中,每個具體產品類都是抽象產品類的子類

  2. 而後提供一個工廠類用於建立各類產品,在工廠類中提供一個建立產品的工廠方法,該方法能夠根據所傳入的參數不一樣建立不一樣的具體產品對象.

  3. 客戶端只需調用工廠類的工廠方法並傳入相應的參數便可獲得一個產品對象

3.2 定義

定義一個工廠類,它能夠根據參數的不一樣返回不一樣類的實例,被建立的實例一般都具備相同的父類.

由於在簡單工廠模式中用於建立實例的方法是靜態(static)方法,所以簡單工廠模式又被成爲靜態工廠方法(Static Factory Method)模式,他屬於類建立型模式.

3.3 要點

當你須要什麼,只須要摻入一個正確的參數,就能夠獲取你所須要的對象,而無需知道其建立細節.

簡單工廠模式結構比較簡單,其核心是工廠類的設計.

3.4 結構圖

3.5 角色

工廠模式結構圖包含如下幾個角色

  • Factory(工廠角色)
  • Product(抽象產品角色)
  • ConcreteProduct(具體產品角色)

3.5.1 Factory(工廠角色)

工廠角色即工廠類,它是簡單工廠模式的核心,負責實現建立全部產品實例的內部邏輯.

工廠類能夠被外界直接調用,建立所需的產品對象.

在工廠類中提供了靜態的工廠方法factoryMethod(),它的返回類型爲抽象產品類型Product

3.5.2 Product(抽象產品角色)

它是簡單工廠模式的建立目標,全部被建立的對象都充當這個角色的某個具體類的實例.

每一個具體產品角色都繼承了抽象產品角色,須要實如今抽象產品中聲明的抽象方法.

3.5.3 ConcreteProduct(具體產品角色)

它是簡單工廠模式的建立目標,全部被建立的對象都充當這個角色的某個具體類的實例.

每個具體角色都集成了抽象產品角色,須要實如今抽象產品中聲明的抽象方法.

3.6 類設計

在簡單工廠模式中,客戶端經過工廠類來建立一個產品類的實例,而無需用new關鍵字來建立對象,它是工廠模式家族中最簡單的一員

3.6.1Product類

將全部產品公共的代碼移至抽象產品類,並在抽象長品類中聲明一些抽象方法,以供不一樣的具體產品來實現.

/**

 * @author liuboren

 * @Title: 產品抽象類

 * @Description: 抽離公共方法和抽象業務方法

 * @date 2019/7/12 16:38

 */

public abstract class AbstractProduct {

    //全部產品類的公共業務方法

    public void methodSame(){

        // 公共方法的實現

    }



    //聲明抽象業務方法

    public abstract void methodDiff();

}

3.6.2 Product實現類

class  ConcreteProductA extends AbstractProduct{



    @Override

    public void methodDiff() {

        //業務方法的實現

    }

}



class  ConcreteProductB extends AbstractProduct{



    @Override

    public void methodDiff() {

        //業務方法的實現

    }

}

3.6.3 工廠類

class Factory{

    public static AbstractProduct getProduct(String arg){

        AbstractProduct product = null;

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

            product = new ConcreteProductA();

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

            product = new ConcreteProductB();

        }

        return product;

    }

}

3.6.4 客戶端類

class Client{

    public static void main(String[] args) {

        AbstractProduct product;

        product = Factory.getProduct("A");

        product.methodSame();

        product.methodDiff();



    }

}

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

使用簡單工廠模式解決上面業務的問題

4.1 類結構圖

4.2 代碼

Chart類:

/**

 * @author liuboren

 * @Title: 圖形接口

 * @Description:

 * @date 2019/7/15 9:42

 */

public interface Chart {



    public void display();

}

HistogramChart:

/**

 * @author liuboren

 * @Title: 柱狀圖

 * @Description:

 * @date 2019/7/15 9:44

 */

public class HistogramChart implements Chart{

    public HistogramChart() {

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

    }



    @Override

    public void display() {

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

    }

}

PieChart:

/**

 * @author liuboren

 * @Title: 餅狀圖

 * @Description:

 * @date 2019/7/15 9:45

 */

public class PieChart implements Chart{



    public PieChart() {

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

    }



    @Override

    public void display() {

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

    }

}

LineChart:

/**

 * @author liuboren

 * @Title: 折線圖

 * @Description:

 * @date 2019/7/15 9:47

 */

public class LineChart implements Chart {



    public LineChart() {

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

    }



    @Override

    public void display() {

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

    }

}

ChartFactory:

/**

 * @author liuboren

 * @Title: 簡單工廠類

 * @Description:

 * @date 2019/7/15 9:48

 */

public class ChartFactory {



    public static Chart getChart(String type) {

        Chart chart = null;

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

            chart = new HistogramChart();

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

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

            chart = new PieChart();

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

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

            chart = new LineChart();

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

        }

        return chart;

    }

}

Client:

/**

 * @author liuboren

 * @Title: 客戶端類

 * @Description:

 * @date 2019/7/15 9:51

 */

public class Client {

    public static void main(String[] args) {

        Chart chart = ChartFactory.getChart("pie");

        chart.display();

    }

}

4.3 優化

在兩個方面能夠進行優化:

  1. 抽取客戶端的參數到配置文件
  2. 將Chart接口和工廠類合併爲一個抽象類

springboot項目能夠將參數抽取到yml文件中,使用@value註解注入,再也不擴展了.

合併後的的UML類圖:

5. 簡單工廠模式總結

從優勢、缺點及使用場景三個方面進行總結

5.1 優勢

  • 對象建立和使用的分離
  • 減小冗餘類名記憶
  • 經過抽取參數到配置文件提升靈活性

5.1.1 對象建立和使用的分離

工廠類包含必要的判斷邏輯,能夠決定何時建立哪個工廠類的實例,客戶端能夠免除直接建立產品對象的職責,而僅僅"消費"產品,簡單工廠模式實現了對象建立和使用的分離

5.1.2 減小冗餘類名記憶

客戶端無須知道所建立的具體產品類名,只須要知道具體產品類所對應的參數便可,對於一些複雜的類名,經過簡單工廠模式能夠再必定程度減小使用者的記憶量.

5.1.3 經過抽取參數到配置文件提升靈活性

經過引入配置文件,能夠再不修改任何客戶端代碼的狀況下更換和增長新的具體產品類,自義定程度上提升了系統的靈活性.

5.2 缺點

  • 工廠類職責太重
  • 增長系統的複雜度和理解難度
  • 系統擴展困難
  • 靜態方法沒法繼承使用

5.2.1 工廠類職責太重

因爲工廠類集中了全部產品的建立邏輯,職責太重,一旦不能正常工做,整個系統都要受到影響

5.2.2 增長系統的複雜度和理解難度

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

5.2.3 系統擴展困難

一旦增長新產品就不得不修改工廠邏輯,在產品類型較多時,有可能形成工廠邏輯過於複雜,不利於系統的擴展和維護.

5.2.4 靜態方法沒法繼承使用

簡單工廠模式因爲使用了靜態工廠方法,形成工廠角色沒法造成基於繼承的等級結構.

5.3 使用場景

  1. 工廠類負責建立的對象比較少,因爲建立的對象比較少,不會形成工廠方法中的業務邏輯太過複雜.

  2. 客戶端只知道傳入工廠類的參數,對於如何建立對象並不關心.

6. 相關文件

簡單工廠模式github倉庫

UML類圖

相關文章
相關標籤/搜索