前言
設計模式(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
抽象類,該類的子類有 BuddhistCalendar
、JapaneseImperialCalendar
、GregorianCalendar
、RollingCalendar
等
getInstance
方法,根據參數獲取一個Calendar
子類對象,該方法實際將參數傳給 createCalendar
方法,createCalendar
在根據參數經過 provider
或 switch
或者 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
的子類擔任
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());
}
}
// ...省略...
}

工廠角色爲 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);
}
查看其子類依賴關係

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

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

工廠角色爲 iLoggerFactory
接口的子類如 LoggerContext
,抽象產品角色爲 Logger
,具體產品角色爲 Logger
的子類,主要是 NOPLogger
和 Logger
類
小結
下一篇介紹工廠方法及典型應用
參考:
劉偉:設計模式Java版
慕課網java設計模式精講 Debug 方式+內存分析
更多內容請訪問個人我的博客:http://laijianfeng.org/
關注【小旋鋒】微信公衆號,及時接收博文推送
本文分享自微信公衆號 - 小旋鋒(whirlysBigData)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。