設計模式(二)創建型模式 - 工廠方法模式

更多最新文章歡迎大家訪問我的個人博客😄:豆腐別館
  
上文介紹的簡單工廠模式提到了當所需生產的產品逐漸增多時,其違反了單一職責原則及開閉原則,而工廠方法模式,即是對簡單工廠模式的進一步抽象化。

一、模式的定義

  • 定義創建產品對象的工廠接口,將產品對象的實際創建工作推遲到具體子工廠類當中。

二、模式的實現

接着上文的栗子,我們已經知道了男人都愛喫豆腐,還愛喫大豬蹄子。在簡單工廠方法模式中,我們是通過一個食物工廠類,再依據傳入的參數來確認需要生產哪種食物。同時我們也指出,當產品變多後這樣處理違反了我們的單一職責原則以及開閉原則。那我們要怎麼辦呢?

在解決問題前我們需要再明確遍問題出現的原因,不就是因爲人類變心喜歡喫上了越來越多的產品,導致工廠嚴重堵車嘛。知道原因了那可不就好辦了,打死變心的,呸, 一個工廠處理不過來我給你兩個(N)個工廠幫忙分擔行不行?emm,感覺好有道理,說幹就幹:

首先同樣的,能喫到哪樣東西的前提是要有這麼個東西,同時爲了方便用戶自定義具體食品,我們也需要新建一個食物父類,如下:

package com.doufuplus.patterns.factory.method;

/**
 * 食物類
 * 轉載請註明出處,更多技術文章歡迎大家訪問我的個人博客站點:https://www.doufuplus.com
 *
 * @author 丶doufu
 * @date 2019/11/27
 */
public abstract class Food {

    public abstract void eat();
}

具體食物繼承自食物類:

package com.doufuplus.patterns.factory.method;

/**
 * 豆腐類
 * 轉載請註明出處,更多技術文章歡迎大家訪問我的個人博客站點:https://www.doufuplus.com
 *
 * @author 丶doufu
 * @date 2019/11/27
 */
public class Doufu extends Food {

    @Override
    public void eat() {
        System.out.println("軟軟的豆腐...");
    }
}
package com.doufuplus.patterns.factory.method;

/**
 * 豬蹄類
 * 轉載請註明出處,更多技術文章歡迎大家訪問我的個人博客站點:https://www.doufuplus.com
 *
 * @author 丶doufu
 * @date 2019/11/27
 */
public class Trotter extends Food {

    @Override
    public void eat() {
        System.out.println("大大的豬蹄...");
    }
}

- Round 1 簡單工廠拆分

  1. 新建豆腐工廠類:
package com.doufuplus.patterns.factory.method;

/**
 * 豆腐工廠類
 * 轉載請註明出處,更多技術文章歡迎大家訪問我的個人博客站點:https://www.doufuplus.com
 *
 * @author 丶doufu
 * @date 2019/11/27
 */
public class DoufuFactory {

    /**
     * 獲取豆腐
     */
    public static Food getDoufu() {
        return new Doufu();
    }

}
  1. 新建豬蹄工具類:
package com.doufuplus.patterns.factory.method;


/**
 * 豬蹄工廠類
 * 轉載請註明出處,更多技術文章歡迎大家訪問我的個人博客站點:https://www.doufuplus.com
 *
 * @author 丶doufu
 * @date 2019/11/27
 */
public class TrotterFactory {

    /**
     * 獲取豬蹄
     */
    public static Food getTrotter() {
        return new Trotter();
    }

}
  1. 現在工廠拆分也拆分了,想喫豆腐了就是這樣的:
    Round 1運行結果
    好嘛,這下清晰多了,想喫什麼就從什麼工廠裏面拿,如果這時候我不想喫豆腐了,只想喫大豬蹄子補補要怎麼辦?那好辦,改改代碼唄,把上面的DoufuFactory.getDoufu();改爲TrotterFactory.getTrotter()就好了。這樣當然是可以的,只是這樣的改動就變成不止需要替換掉工廠類,還需要去找到更改後相應的方法,那麼有什麼更好的方式可以使代碼改動更少呢?

- Round 2 抽象/多態工廠拆分

身爲一個有志向的青年,本着能躺着就不站着的原則。我們需要針對上面提出的問題對代碼做出如下改進:

  1. 再次新建食物工廠接口,提供抽象方法,讓具體子類去決定生成何種食物:
package com.doufuplus.patterns.factory.method;


/**
 * 食物工廠類
 * 轉載請註明出處,更多技術文章歡迎大家訪問我的個人博客站點:https://www.doufuplus.com
 *
 * @author 丶doufu
 * @date 2019/11/27
 */
public interface FoodFactory {

    /**
     * 獲取食物
     */
    Food getFood();
}

  1. 將原來的DoufuFactory以及FoodFactory都實現上面的FoodFactory,如:
package com.doufuplus.patterns.factory.method;

/**
 * 豆腐工廠類
 * 轉載請註明出處,更多技術文章歡迎大家訪問我的個人博客站點:https://www.doufuplus.com
 *
 * @author 丶doufu
 * @date 2019/11/27
 */
public class DoufuFactory implements FoodFactory {

    /**
     * 獲取豆腐
     */
    @Override
    public Food getFood() {
        // ...
        // 放蔥花撒鹽
        // ...
        return new Doufu();
    }
}
  1. 那麼這時候我們的調用方式就是這樣的:
    Round 2運行結果01
  2. 當豆腐喫膩了,只想喫豬蹄時,就是這樣的:Round 2運行結果02
    可以看到這時候的代碼改動就變少了,且不需要費心去找相應的方法,因爲方法名始終是一致的。

ps:當需要生成的產品不多且不會增加,一個具體工廠類就可以完成任務時,可刪除抽象工廠類。這時工廠方法模式將退化到上文的簡單工廠模式。

三、模式的結構

回顧下上面的代碼我們來捋一下工廠方法模式的主要類結構,如下:

  • 抽象工廠(Abstract Factory):提供了創建產品的接口,調用者通過它訪問具體工廠的工廠方法 newProduct() 來創建產品。如代碼中的FoodFactory
  • 具體工廠(ConcreteFactory):主要是實現抽象工廠中的抽象方法,完成具體產品的創建。如代碼中的DoufuFactoryTrotterFactory
  • 抽象產品(Product):定義了產品的規範,描述了產品的主要特性和功能。如代碼中的Food類。
  • 具體產品(ConcreteProduct):實現了抽象產品角色所定義的接口,由具體工廠來創建,它同具體工廠之間一一對應。如代碼中的DoufuTrotter
    工廠方法類結構圖

四、模式的優缺點

1. 優點

  • 用戶只需要知道具體工廠的名稱就可得到所要的產品,無須知道產品的具體創建過程。(與簡單工廠模式類似)
  • 在系統增加新的產品時只需要添加具體產品類和對應的具體工廠類,無須對原工廠進行任何修改,滿足開閉原則。

2. 缺點

  • 每增加一個產品就要增加一個具體產品類和一個對應的具體工廠類,當工廠類越來越多時,易造成工廠氾濫,增加系統複雜度。

五、模式的應用場景

  • 客戶只知道創建產品的工廠名,而不知道具體的產品名。如 TCL 電視工廠、海信電視工廠等。
  • 創建對象的任務由多個具體子工廠中的某一個完成,而抽象工廠只提供創建產品的接口。
  • 客戶不關心創建產品的細節,只關心產品的品牌。

參考:C語言中文網-設計模式

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章