設計模式 | 簡單工廠模式及典型應用

前言

設計模式(Design Pattern)是一套被反覆使用、多數人知曉的、通過分類編目的、代碼設計經驗的總結,使用設計模式是爲了可重用代碼、讓代碼更容易被他人理解而且保證代碼可靠性。html

本文主要介紹簡單工廠模式及典型應用,內容以下:java

  • 簡單工廠模式的介紹python

  • 簡單工廠模式的典型應用及源碼分析mysql

    • Calendar 類獲取日曆類對象sql

    • JDBC 獲取數據庫鏈接數據庫

    • LoggerFactory 獲取 Logger 對象設計模式

簡單工廠模式

工廠模式是最經常使用的一類建立型設計模式,包括 抽象工廠模式,工廠方法模式和簡單工廠模式 這三種,簡單工廠模式是其中最簡單的一種數組

簡單工廠模式(Simple Factory Pattern):定義一個工廠類,它能夠根據參數的不一樣返回不一樣類的實例,被建立的實例一般都具備共同的父類。安全

由於在簡單工廠模式中用於建立實例的方法是靜態(static)方法,所以簡單工廠模式又被稱爲靜態工廠方法(Static Factory Method)模式,它屬於類建立型模式,但不屬於GOF23種設計模式微信

角色

Factory(工廠角色):工廠角色即工廠類,它是簡單工廠模式的核心,負責實現建立全部產品實例的內部邏輯;工廠類能夠被外界直接調用,建立所需的產品對象;在工廠類中提供了靜態的工廠方法factoryMethod(),它的返回類型爲抽象產品類型Product

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

ConcreteProduct(具體產品角色):它是簡單工廠模式的建立目標,全部被建立的對象都充當這個角色的某個具體類的實例。每個具體產品角色都繼承了抽象產品角色,須要實如今抽象產品中聲明的抽象方法

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

示例

抽象產品類 Video,定義了抽象方法 produce()

public abstract class Video {
    public abstract void produce();
}

具體產品類 JavaVideo 和 PythonVideo,都繼承了抽象產品類 Video

public class JavaVideo extends Video {
    @Override
    public void produce() {
        System.out.println("錄製Java課程視頻");
    }
}

public class PythonVideo extends Video {
    @Override
    public void produce() {
        System.out.println("錄製Python課程視頻");
    }
}

工廠類實現的兩種方法:使用if-else判斷和使用反射來建立對象

public class VideoFactory {
    /**
     * 使用if else 判斷類型,type 爲 Java 則返回 JavaVideo, type爲Python則返回 PythonVideo
     */

    public Video getVideo(String type{
        if ("java".equalsIgnoreCase(type)) {
            return new JavaVideo();
        } else if ("python".equalsIgnoreCase(type)) {
            return new PythonVideo();
        }
        return null;
    }

    /**
     * 使用反射來建立對象
     */

    public Video getVideo(Class c{
        Video video = null;
        try {
            video = (Video) Class.forName(c.getName()).newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return video;
    }
}

使用一個客戶端來調用工廠類

public class Test {
    public static void main(String[] args{
        VideoFactory videoFactory = new VideoFactory();
        Video video1 = videoFactory.getVideo("python");
        if (video1 == null) {
            return;
        }
        video1.produce();

        Video video2 = videoFactory.getVideo(JavaVideo.class);
        if (video2 == null) {
            return;
        }
        video2.produce();
    }
}

輸出

錄製Python課程視頻
錄製Java課程視頻
示例.簡單工廠模式類圖

Test 類經過傳遞參數給 VideoFactory.getVideo() 來獲取對象,建立對象的邏輯交給了工廠類 VideoFactory 來完成

簡單工廠模式總結

簡單工廠模式的主要優勢以下:

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

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

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

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

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

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

  • 系統擴展困難,一旦添加新產品就不得不修改工廠邏輯,在產品類型較多時,有可能形成工廠邏輯過於複雜,不利於系統的擴展和維護,且違背開閉原則。

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

適用場景

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

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

簡單工廠模式的典型應用及源碼分析

Calendar 類獲取日曆類對象

Calendar 抽象類,該類的子類有 BuddhistCalendarJapaneseImperialCalendarGregorianCalendarRollingCalendar

getInstance方法,根據參數獲取一個Calendar子類對象,該方法實際將參數傳給 createCalendar 方法,createCalendar 在根據參數經過 providerswitch 或者 if-else 建立相應的子類對象

如下爲 Java8 中的 Calendar 類代碼,Java7 中的實現爲 if-else 方式

public static Calendar getInstance(TimeZone zone, Locale aLocale{
    return createCalendar(zone, aLocale);
}

private static Calendar createCalendar(TimeZone zone, Locale aLocale{
    CalendarProvider provider = LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale).getCalendarProvider();
    if (provider != null) {
        try {
            return provider.getInstance(zone, aLocale);
        } catch (IllegalArgumentException iae) {
        }
    }

    Calendar cal = null;

    if (aLocale.hasExtensions()) {
        String caltype = aLocale.getUnicodeLocaleType("ca");
        if (caltype != null) {
            switch (caltype) {
                case "buddhist":
                    cal = new BuddhistCalendar(zone, aLocale); break;
                case "japanese":
                    cal = new JapaneseImperialCalendar(zone, aLocale); break;
                case "gregory":
                    cal = new GregorianCalendar(zone, aLocale); break;
            }
        }
    }
    if (cal == null) {
        if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
            cal = new BuddhistCalendar(zone, aLocale);
        } else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja" && aLocale.getCountry() == "JP") {
            cal = new JapaneseImperialCalendar(zone, aLocale);
        } else {
            cal = new GregorianCalendar(zone, aLocale);
        }
    }
    return cal;
}
Calendar的繼承關係

能夠看到抽象產品角色工廠角色都由  Calendar 擔任,具體產品角色Calendar 的子類擔任

JDBC 獲取數據庫鏈接

通常JDBC獲取MySQL鏈接的寫法以下:

//加載MySql驅動
Class.forName("com.mysql.jdbc.Driver");
DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test""root""123456");

首先經過反射加載驅動類 com.mysql.jdbc.Driver 類,而後再經過 DriverManager 獲取鏈接

看看 com.mysql.jdbc.Driver 的代碼,該類主要的內容是靜態代碼塊,其會隨着類的加載一塊執行

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    public Driver() throws SQLException {
    }
    static {
        try {
            DriverManager.registerDriver(new Driver());
        } catch (SQLException var1) {
            throw new RuntimeException("Can't register driver!");
        }
    }
}

靜態代碼塊:new 一個 Driver 類並註冊到 DriverManager 驅動管理類中

public static synchronized void registerDriver(java.sql.Driver driver, DriverAction da) throws SQLException {
    /* Register the driver if it has not already been added to our list */
    if(driver != null) {
        registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
    } else {
        throw new NullPointerException();
    }
    println("registerDriver: " + driver);
}

其中的 registeredDrivers 是一個 CopyOnWriteArrayList 對象

private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();

CopyOnWriteArrayList是Java併發包中提供的一個併發容器,它是個線程安全且讀操做無鎖的ArrayList,寫操做則經過建立底層數組的新副原本實現,是一種讀寫分離的併發策略,咱們也能夠稱這種容器爲"寫時複製器",Java併發包中相似的容器還有CopyOnWriteSet  
一篇CopyOnWriteArrayList的文章:https://www.cnblogs.com/chengxiao/p/6881974.html

再經過 DriverManager.getConnection 獲取鏈接對象的主要代碼以下:經過for循環從已註冊的驅動中(registeredDrivers)獲取驅動,嘗試鏈接,成功則返回鏈接

private static Connection getConnection(String url, java.util.Properties info, Class<?> caller) throws SQLException {
    // ...省略...
    println("DriverManager.getConnection(\"" + url + "\")");
    for(DriverInfo aDriver : registeredDrivers) {
        // If the caller does not have permission to load the driver then skip it.
        if(isDriverAllowed(aDriver.driver, callerCL)) {
            try {
                println("    trying " + aDriver.driver.getClass().getName());
                Connection con = aDriver.driver.connect(url, info);
                if (con != null) {
                    // Success!
                    println("getConnection returning " + aDriver.driver.getClass().getName());
                    return (con);
                }
            } catch (SQLException ex) {
                if (reason == null) {
                    reason = ex;
                }
            }
        } else {
            println("    skipping: " + aDriver.getClass().getName());
        }
    }
    // ...省略...
}
Connection 接口及子類實現關係

工廠角色爲 DriverManager 類,抽象產品角色爲 Connection,具體產品角色則不少

Logback 中的 LoggerFactory 獲取 Logger 對象

查看 LoggerFactory 類的 getLogger 方法,可看到調用了 iLoggerFactory.getLogger(),其中 iLoggerFactory 是一個接口

public static Logger getLogger(String name) {
    ILoggerFactory iLoggerFactory = getILoggerFactory();
    return iLoggerFactory.getLogger(name);
}

public static Logger getLogger(Class clazz) {
    return getLogger(clazz.getName());
}

iLoggerFactory 接口只有一個 getLogger 方法

public interface ILoggerFactory {
    Logger getLogger(String var1);
}

查看其子類依賴關係

iLoggerFactory接口子類的依賴關係

再看一個子類 LoggerContext 對 ILoggerFactory 的實現

image

可看到這是經過 if-else 方式的簡單工廠模式

Logger 接口及子類實現關係

工廠角色爲 iLoggerFactory 接口的子類如 LoggerContext,抽象產品角色爲 Logger,具體產品角色爲 Logger 的子類,主要是 NOPLoggerLogger

小結

下一篇介紹工廠方法及典型應用

參考:  
劉偉:設計模式Java版  
慕課網java設計模式精講 Debug 方式+內存分析


更多內容請訪問個人我的博客:http://laijianfeng.org/

關注【小旋鋒】微信公衆號,及時接收博文推送


本文分享自微信公衆號 - 小旋鋒(whirlysBigData)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索