[置頂] 設計模式(一)工廠模式

官方定義:設計模式(Design Pattern)是一套被反覆使用、多數人知曉的、通過分類編目的代碼設計經驗總結。java

使用意義:使用設計模式是爲了可重用代碼,讓代碼更容易被他人理解,保證代碼的可靠性。可複用的面向對象軟件系統主要包含兩大類:應用程序工具箱和框架(框架是指構成一類特定軟件可複用設計的一組相互協做的類)。設計模式有助於對框架的理解,成熟的框架一般使用了多種設計模式,若是你能熟悉這些設計模式,毫無疑問,將能迅速的掌握框架的結構。數據庫

工廠模式概念

工廠模式將客戶類與工廠類分開,消費者在任什麼時候候須要某種產品只須要向工廠請求,消費者無須修改就能接納新產品。缺點:產品修改時,工廠類也要作相應的修改。工廠模式是編程過程當中最經常使用的設計模式,由於工廠模式至關於建立實例對象的new, 例如 A a = new A();工廠類也是用來建立實例對象的,那麼,new關鍵字能夠建立對象,爲何還要用工廠類建立實例對象呢?因爲建立實例時一般伴隨有初始化工做,若是隻是簡單的賦值操做,咱們可使用帶參數的構造方法,但若是初始化不只僅是賦值這樣簡單的操做,還包含一些邏輯判斷等大片斷的代碼,若是都寫進構造函數,會使咱們的代碼看起來很臃腫,將多個功能放進一個方法,就像將不少雞蛋放進一個籃子裏,這很危險,而且有悖於咱們面向對象的設計原則。這時須要咱們將長片斷的代碼進行分割,再封裝,之後若是須要修改,只須要修改部分片斷,而不會出現牽一動百的情形。編程

簡單工廠模式

簡單工廠模式的是想源於java中的接口,經過使用接口能夠實現不相關類的相同行爲,而不須要考慮這些類的層次關係,接口就是實現類對外的外觀。主要使用在業務比較簡單的狀況下,由三種角色構成。設計模式

  • 工廠類:是模塊的核心,一般包含邏輯實現,在java中每每是一個具體的實現類
  • 抽象產品:是具體產品繼承的父類或者須要實現的接口,在java中一般是抽象類或者接口
  • 具體產品:工廠類建立的對象就是該類的實例,在java中一般是具體的實現類

簡單工廠類的基本結構


Client與具體實現Impl之間並沒有瓜葛,客戶端經過Factory獲取須要的接口對象,而後調用接口的方法來實現具體的功能。下面舉個例子,經過不一樣的通信工具來通訊,QQ和微信分別實現了API接口。微信

示例說明

/**
 * 簡單工廠模式
 * Created by xian.juanjuan on 2017-7-13 14:07.
 */
public class SimpleFactory {
    //根據條件建立具體的實現對象
    public static MessageApi createMessageApi(int condition){
        MessageApi messageApi = null;
        if (condition == 1){
            messageApi = new QQMessageImpl();
        } else if (condition == 2){
            messageApi = new WeChatMessageImpl();
        }
        return messageApi;
    }
}

class Client{
    public static void main(String[] args){
        //經過簡單工廠獲取接口對象
        MessageApi messageApi = SimpleFactory.createMessageApi(1);
        messageApi.sendMessage("測試簡單工廠模式");
    }
}

/**
 * 通訊接口能夠經過簡單工廠來建立
 */
interface MessageApi{
    //具體的功能方法
    void sendMessage(String msg);
}

/**
 * 接口的具體實現--QQ通訊
 */
class QQMessageImpl implements MessageApi{
    @Override
    public void sendMessage(String msg) {
        System.out.println("經過QQ聊天工具發送消息:"+msg);
    }
}

/**
 * 接口的具體實現--微信通訊
 */
class WeChatMessageImpl implements MessageApi{
    @Override
    public void sendMessage(String msg) {
        System.out.println("經過微信聊天工具發送消息"+msg);
    }
}

問題1:這裏若是再增長一種通信方式(Email)時,就須要修改工廠類,顯然簡單工廠方式不能知足OCP設計原則。

問題2:客戶端在調用工廠時,須要傳入選擇的參數,這就說明客戶端必須知道每一個參數的含義,也須要知道每一個參數對應的功能處理,在必定程度上向客戶端暴露了內部實現細節。框架

每次增長一個實現類就要就要修改工廠類的實現,一般用配置文件類解決這個問題。less

建立FactoryTest.properties配置文件,內容以下,若是新增實現類,只須要修改下面的配置文件便可。ide

ImplClass=com.xianjj.pattern.QQMessageImpl

工廠類以下函數

public class SimpleFactory {

    public static MessageApi createMessageApi(){
        Properties properties = new Properties();
        InputStream inputStream;
        try {
            inputStream = SimpleFactory.class.getResourceAsStream("FactoryTest.properties");
            properties.load(inputStream);
        } catch (IOException e) {
            System.out.println("加載工廠配置文件異常");
            e.printStackTrace();
        }
        //用反射建立對象
        MessageApi messageApi = null;
        try {
            messageApi = (MessageApi)Class.forName(properties.getProperty("ImplClass")).newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return messageApi;
    }
}

簡單工廠模式優缺點

  • 幫助封裝 很是友好的幫咱們實現了組件的封裝,讓組件外部能真正的面向接口編程
  • 解耦 實現客戶端和具體實現類的解耦
  • 可能增長客戶端的複雜度 如何選擇具體的實現類須要仔細斟酌
  • 不方便擴展子工廠 私有化簡單工廠的構造方法,使用靜態方法來建立接口,也就不能經過寫簡單工廠類的子類來改變建立接口的方法的行爲了
  • 不徹底知足OCP
簡單工廠的實質是爲了「選擇實現」,工廠的重點在於選擇,由於實現已經作好了,目的是爲客戶端選擇相應的實現,從而使得客戶端與實現之間解耦。難點在於如何選擇實現,能夠經過傳遞參數,或動態的讀取某個內存的值,或者讀取數據庫中的值來選擇具體的實現。

簡單工廠一般不用建立簡單工廠類的類實例,所以,能夠把簡單工廠類看作一個工具類,直接使用靜態方法就能夠了,因此被稱做靜態工廠。爲了防止客戶端無謂的建立簡單工廠實例,能夠把簡單工廠的構造方法私有化。工具

工廠方法模式

爲了解決簡單工廠模式最大的缺點—不徹底知足OCP原則,設計師們提出了工廠方法模式,工廠方法模式相對於簡單工廠模式來講最大的不一樣在於,對於一個項目或者一個獨立的模塊來講,簡單工廠模式只有一個工廠類,兒工廠方法模式有一組實現了相同接口的工廠類。

在工廠方法模式中,核心的工廠類再也不負責產品的建立,而是將具體的建立工做交給子類去作。這個核心工廠則變爲抽象工廠角色,僅負責給出具體工廠子類必須實現的接口,而不接觸產品建立的細節。在工廠方法模式中通常都有一個平行的等級結構,也就是說工廠和產品是對應的。

工廠方法模式結構


示例說明

果農起初只有一個小型的蘋果園,後來規模擴大,出現了蘋果工廠,葡萄工廠,每一個工廠獨立生產對應的水果,實現專業化和規模化的生產。

/**
 * 工廠方法模式示例
 * Created by xian.juanjuan on 2017-7-14 09:30.
 */
public class ClientTest {
    public static void main(String[] args){
        //實例化水果工廠
        FruitFactory fruitFactory = new AppleFactoryImpl();
        FruitFactory fruitFactory1 = new GrapeFactoryImpl();
        //從水果工廠生產水果
        Fruit fruit = fruitFactory.factory();
        Fruit fruit1 = fruitFactory1.factory();
        //不一樣水果的生長過程
        fruit.plant();
        fruit1.harvest();
    }
}

/**
 * 水果接口
 */
interface Fruit{
    void plant();//種植
    void harvest();//收穫
}

/**
 * 水果工廠的產品:蘋果
 */
class Apple implements Fruit{
    private int treeAget;//樹齡

    @Override
    public void harvest() {
        System.out.println("蘋果已經收穫。。。");
    }

    @Override
    public void plant() {
        System.out.println("蘋果已經種植。。。");
    }

    public int getTreeAget() {
        return treeAget;
    }

    public void setTreeAget(int treeAget) {
        this.treeAget = treeAget;
    }
}

/**
 * 水果工廠的產品:葡萄
 */
class Grape implements Fruit{
    private boolean seedless;//是否有籽

    @Override
    public void harvest() {
        System.out.println("葡萄已經收穫。。。");
    }

    @Override
    public void plant() {
        System.out.println("葡萄已經種植。。。");
    }

    public boolean isSeedless() {
        return seedless;
    }

    public void setSeedless(boolean seedless) {
        this.seedless = seedless;
    }
}

/**
 * 水果工廠接口
 */
interface FruitFactory{
    Fruit factory();
}

/**
 * 蘋果工廠實現
 */
class AppleFactoryImpl implements FruitFactory{
    @Override
    public Fruit factory() {
        Fruit fruit = new Apple();
        System.out.println("水工工廠成功建立一個水果:蘋果");
        return fruit;
    }
}

/**
 * 葡萄工廠實現
 */
class GrapeFactoryImpl implements FruitFactory{
    @Override
    public Fruit factory() {
        Fruit fruit = new Grape();
        System.out.println("水工工廠成功建立一個水果:葡萄");
        return fruit;
    }
}

輸出結果

水工工廠成功建立一個水果:蘋果
水工工廠成功建立一個水果:葡萄
蘋果正在生長。。。
葡萄已經收穫。。。

Process finished with exit code 0

後續工廠繼續擴建,若是須要草莓工廠,只須要新增對應的草莓實現類和草莓工廠實現類便可,對原有實現無任何影響。

工廠方法模式優缺點(相對於簡單工廠模式)

  • 結構複雜度增長
  • 代碼複雜度增長
  • 客戶端編程難度增長(客戶端編程中須要對須要對工廠類進行實例化,而簡單工廠時靜態類不須要實例化)
  • 管理難度增大
  • 知足OCP原則
從維護的角度來講,假如每一個產品類須要進行必定的修改,極可能須要修改對應的工廠類,當同時須要修改多個產品類時,須要找對應的工廠類進行修改,這種模式就變得至關麻煩。綜上,簡單工廠模式更簡單,更方便一些,而工廠方法模式更先進一些。須要根據實際狀況選擇合適的設計模式。

抽象工廠模式

抽象工廠模式是三個最抽象,最具通常性的。抽象工廠模式的目的是給客戶端提供一個接口,能夠建立多個產品族中的產品對象,使用抽象工廠模式須要知足如下條件

  • 系統中有多個產品族,而系統一次只可能消費其中一族產品
  • 同屬於一個產品族的產品一塊兒使用

舉例說明兩個概念

  • 產品族:Android產品族和Apple產品族
  • 產品等級(產品族的產品):手機和充電器。(Android手機和Android充電器是一個產品族,Apple手機和Apple充電器是另外一個產品族;Android手機用Android充電器,蘋果手機用蘋果充電器)一個等級結構是由結構相同的產品組成。

抽象工廠模式的每一個工廠創造出的都是一族的產品,而不是一個或者一組。

產品結構圖


示例說明

用戶買手機配充電器

首先用簡單工廠模式實現

public class AbstractFactoryTest {

    public static void main(String[] args){
        phoneCharging(1, 1);
    }

    public static void phoneCharging(int phoneType, int chargerType){
        //手機工廠和充電器工廠生產手機和充電器
        Phone phone = PhoneFactory.createPhone(phoneType);
        Charger charger = ChargerFactory.createCharger(chargerType);
        //買手機配充電器
        phone.buyPhone();
        charger.configureCharger();
    }
}

/**
 * 手機接口
 */
interface Phone{
    void buyPhone();//買手機
}

class AndroidPhone implements Phone{
    @Override
    public void buyPhone() {
        System.out.println("我買了一部Android手機");
    }
}

class ApplePhone implements Phone{
    @Override
    public void buyPhone() {
        System.out.println("我買了一部Apple手機");
    }
}

/**
 * 充電器接口
 */
interface Charger{
    void configureCharger();//配置充電器
}

class AndroidCharger implements Charger{
    @Override
    public void configureCharger() {
        System.out.println("給我配置了Android手機充電器");
    }
}

class AppleCharger implements Charger{
    @Override
    public void configureCharger() {
        System.out.println("給我配置了Apple手機充電器");
    }
}

/**
 * 手機工廠類
 */
class PhoneFactory{

    public static Phone createPhone(int type){
        Phone phone = null;
        if (1 == type){
            phone =  new AndroidPhone();
        } else if (2 == type){
            phone =  new ApplePhone();
        }
        return phone;
    }
}

/**
 * 充電器工廠類
 */
class ChargerFactory{
    public static Charger createCharger(int type){
        Charger charger = null;
        if (1 == type){
            charger = new AndroidCharger();
        } else if(2 == type){
            charger = new AppleCharger();
        }
        return charger;
    }
}

運行結果

我買了一部Android手機
給我配置了Android手機充電器

Process finished with exit code 0

外部使用的時候,手機與充電器的類型須要匹配,若是phoneCharging(1, 2);那手機和充電器就不兼容;在上述的簡單工廠模式中,並無維護這種關係,爲了解決這個問題 須要使用抽象工廠模式。簡單工廠模式針對的是一個產品等級結構,而抽象工廠模式則須要面對多個產品等級結構。


用同一個工廠等級結構複雜兩個不一樣產品等級結構中的產品對象的建立,即Apple工廠生產Apple手機和Apple手機充電器。因爲這兩個產品族的產品等級結構相同,所以使用同一個工廠族也能夠處理這兩個產品族的建立問題,這就是抽象工廠模式。


使用抽象工廠模式實現

在以前代碼的基礎上,只須要增長抽象工廠類和實現便可。

interface AbstractFactory{
    Phone createPhone();
    Charger createCharger();
}

class AndroidFactory implements AbstractFactory{
    @Override
    public Charger createCharger() {
        return new AndroidCharger();
    }

    @Override
    public Phone createPhone() {
        return new ApplePhone();
    }
}

class AppleFactory implements AbstractFactory{
    @Override
    public Charger createCharger() {
        return new AppleCharger();
    }

    @Override
    public Phone createPhone() {
        return new ApplePhone();
    }
}
測試客戶端
public class AbstractFactoryTest {

    public static void main(String[] args){
        //客戶選擇並建立須要的產品對象
        AbstractFactory factory = new AppleFactory();
        //讓店員配套起來便可
        phoneCharging(factory);
    }

    public static void phoneCharging(AbstractFactory factory){
        //店員找相應的工廠獲取產品
        Phone phone = factory.createPhone();
        Charger charger = factory.createCharger();
        phone.buyPhone();
        charger.configureCharger();
    }
}

因而可知,抽象工廠模式是爲一系列相關的對象或者相互依賴的對象建立一個接口。

抽象工廠模式優缺點

  • 分離接口和實現(客戶端從具體的產品實現中解耦)
  • 使切換產品族變的容易(從Android切換到Apple,客戶端只須要切換一下具體工廠)
  • 不容易擴展新產品(若是給整個產品族新增一個產品,就要修改抽象工廠,致使修改全部的工廠實現類)
工廠模式在此就敘述完了,實際應用中可根據具體需求選擇使用。
相關文章
相關標籤/搜索