简单工厂模式在Logback源码以及JDK源码中的应用
創(chuàng)建型模式
目錄
一、簡單工廠模式在Logback源碼以及JDK源碼中的應(yīng)用
1、簡單工廠模式
1.1?簡單工廠模式的UML類圖
1.2?日常生活中看簡單工廠模式
1.3?具體例子
2、簡單工廠模式在Logback源碼中的應(yīng)用
3、簡單工廠模式在JDK源碼中的應(yīng)用
3.1 Calendar 類為例簡單工廠模式
3.2 JDBC簡單工廠模式
4、簡單工廠模式的優(yōu)缺點(diǎn)
4.1 簡單工廠模式的優(yōu)點(diǎn)
4.2 簡單工廠模式的缺點(diǎn)
4.3 簡單工廠模式的適用環(huán)境
一、簡單工廠模式在Logback源碼以及JDK源碼中的應(yīng)用
1、簡單工廠模式
簡單工廠模式(Simple Factory Pattern):又稱為靜態(tài)工廠方法(Static Factory Method)模式,它屬于創(chuàng)建型模式。簡單工廠模式的實(shí)質(zhì)是由一個(gè)工廠類根據(jù)傳入的參數(shù),動(dòng)態(tài)決定應(yīng)該創(chuàng)建哪一個(gè)產(chǎn)品類(這些產(chǎn)品類繼承或?qū)崿F(xiàn)一個(gè)父類或接口)的實(shí)例。
1.1?簡單工廠模式的UML類圖
1.2?日常生活中看簡單工廠模式
就拿巴扎黑理發(fā)這件事來說,服務(wù)員就充當(dāng)了工廠類,她根據(jù)巴扎黑想要剪的價(jià)位來安排一位合適的理發(fā)師。理發(fā)師這個(gè)稱謂就相當(dāng)于抽象產(chǎn)品,它描述了所有理發(fā)師都具備的能力——理發(fā)。服務(wù)員安排的這位總監(jiān)就相當(dāng)于具體產(chǎn)品。
簡單工廠模式中,對于某個(gè)具體業(yè)務(wù)而言,client 無需知道由誰來處理這個(gè)業(yè)務(wù),怎么處理,它只需告訴工廠類這個(gè)業(yè)務(wù)的類型,并調(diào)用工廠類提供的靜態(tài)方法拿到一個(gè)具體的產(chǎn)品,然后調(diào)用產(chǎn)品的業(yè)務(wù)方法來完成業(yè)務(wù)。就像巴扎黑去餐廳吃飯,他只要告訴服務(wù)員他想吃什么,然后自然會(huì)有一位師傅幫他做好,自己根本就不需要關(guān)心這個(gè)師傅是誰以及怎么做出這道美味菜肴。這樣看來,吃飯這件事也不麻煩。
但是設(shè)想一下,巴扎黑很早就起床了,他想自己做菜來犒勞自己,他有充足的時(shí)間來準(zhǔn)備。首先他得考慮想要吃什么,于是有了下面的思考:
要想吃點(diǎn)好的,腦袋里還得裝一本厚厚的菜譜,想要嘗嘗新菜,還得不斷豐富大腦里的菜譜,著實(shí)麻煩。有時(shí)候腦袋不夠使,還會(huì)把菜譜記錯(cuò),倒不如直接去餐廳點(diǎn)菜吃來的省事兒。
1.3?具體例子
1.3.1 場景
實(shí)現(xiàn)一個(gè)簡單的翻譯功能,要求:能夠?qū)⒁欢沃形奈谋痉g成不同的語言版本
1.3.2 代碼
要實(shí)現(xiàn)翻譯,我們得有一個(gè)翻譯器。我們先定義一個(gè)抽象的翻譯器(AbstractTranslator),用于描述所有翻譯器都具備的功能——翻譯(translate)
/*** 翻譯器抽象類* 抽象產(chǎn)品,定義產(chǎn)品必須實(shí)現(xiàn)的方法** @author piaolingluo* @date 2017-11-08*/ public abstract class AbstractTranslator {@Autowiredprotected BaiduConfig baiduConfig;/*** 翻譯** @param content 待翻譯的內(nèi)容* @return 翻譯的得到的內(nèi)容*/public abstract String translate(String content); }再定義一個(gè)具體的翻譯器——英語翻譯器(EnglishTranslator),繼承AbstractTranslator,它能將一段中文翻譯成英文
/*** 英語翻譯器* 具體產(chǎn)品** @author piaolingluo* @date 2017-11-08*/ @Service public class EnglishTranslator extends AbstractTranslator {@Autowiredprivate Gson gson;@Overridepublic String translate(String content) {TransApi api = new TransApi(baiduConfig.getAppId(), baiduConfig.getSecurityKey());String response = api.getTransResult(content, "auto", "en");TransResult result = gson.fromJson(response, TransResult.class);return result.getTransResult().get(0).getDst();} }再定義一個(gè)具體的翻譯器——日語翻譯器(JapaneseTranslator),繼承AbstractTranslator,它能將一段中文翻譯成日語
/*** 日語翻譯器* 具體產(chǎn)品** @author piaolingluo* @date 2017-11-08*/ @Service public class JapaneseTranslator extends AbstractTranslator {@Autowiredprivate Gson gson;@Overridepublic String translate(String content) {TransApi api = new TransApi(baiduConfig.getAppId(), baiduConfig.getSecurityKey());String response = api.getTransResult(content, "auto", "jp");TransResult result = gson.fromJson(response, TransResult.class);return result.getTransResult().get(0).getDst();} }接著,我們定義系統(tǒng)能能翻譯的語言有哪些,如果后續(xù)支持新的語言,可以追加
/*** 語言枚舉* 定義系統(tǒng)能翻譯的語言** @author piaolingluo* @date 2017-11-08*/ @Getter public enum LanguageEnum {CHINESE("chinese", "中文"),ENGLISH("english", "英語"),JAPANESE("japanese", "日語");private String code;private String name;LanguageEnum(String code, String name) {this.code = code;this.name = name;}public static LanguageEnum valueOfLanguage(String code) {return Stream.of(values()).filter(languageEnum -> languageEnum.getCode().equals(code)).findFirst().orElse(null);} }接著,比較重要的一點(diǎn)是為不同的語言指定各自的實(shí)例,后續(xù)如果支持新的語言,可以在此擴(kuò)展
/*** 語言類型與翻譯器實(shí)例的映射** @author piaolingluo* @date 2017-11-08*/ @Getter public enum TranslatorEnum {ENGLISH_TRANSLATOR(LanguageEnum.ENGLISH, "englishTranslator"),JAPANESE_TRANSLATOR(LanguageEnum.JAPANESE, "japaneseTranslator");/*** 語言*/private LanguageEnum language;/*** 具體翻譯器處理bean的名字*/private String translatorName;TranslatorEnum(LanguageEnum language, String translatorName) {this.language = language;this.translatorName = translatorName;}public static TranslatorEnum valueOfTranslator(String translatorName) {return Stream.of(values()).filter(translatorEnum -> translatorEnum.getTranslatorName().equals(translatorName)).findFirst().orElse(null);}public static LanguageEnum languageOfTranslator(String translatorName) {Optional<TranslatorEnum> optional = Optional.ofNullable(valueOfTranslator(translatorName));return optional.isPresent() ? optional.get().getLanguage() : null;} }最最重要的就是編寫這個(gè)工廠類(Factory),初始化的時(shí)候,將所有的翻譯器按各自能處理的語言類型存放在 TRANSLATOR_MAP 里,并提供一個(gè)靜態(tài)方法,能夠根據(jù)不同的語言拿到具體的翻譯器
/*** 工廠** @author piaolingluo* @date 2017-11-08*/ @Component public class Factory {private static final Map<LanguageEnum, AbstractTranslator> TRANSLATOR_MAP = new HashMap<>();@Autowiredprivate ApplicationContext applicationContext;@PostConstructpublic void init() {applicationContext.getBeansOfType(AbstractTranslator.class).entrySet().stream().filter(entry -> null != TranslatorEnum.valueOfTranslator(entry.getKey())).forEach(entry -> TRANSLATOR_MAP.put(TranslatorEnum.languageOfTranslator(entry.getKey()),entry.getValue()));}/*** 根據(jù)語言枚舉拿到指定語言的翻譯器** @param languageEnum 語言枚舉* @return 指定語言的翻譯器* @throws Exception 當(dāng)拿不到翻譯器時(shí),拋出此異常*/public static AbstractTranslator getTranslator(LanguageEnum languageEnum) throws Exception {AbstractTranslator translator = TRANSLATOR_MAP.get(languageEnum);if (null == translator) {throw new Exception("無法翻譯成這種語言");}return translator;}/*** 根據(jù)語言編碼拿到指定語言的翻譯器** @param languageCode 語言編碼* @return 指定語言的翻譯器* @throws Exception 當(dāng)拿不到翻譯器時(shí),拋出此異常*/public static AbstractTranslator getTranslator(String languageCode) throws Exception {return getTranslator(LanguageEnum.valueOfLanguage(languageCode));} }然后我們寫個(gè) TranslateController ,接收指定的語言編碼和待翻譯的文本,返回翻譯后的文本
/*** 翻譯服務(wù)** @author piaolingluo* @date 2017-11-08*/ @RestController @RequestMapping("translate") public class TranslateController {@GetMapping("{code}")public ResponseEntity<String> translate(@PathVariable("code") String code,@RequestParam(value = "content", required = false) String content) {try {// 調(diào)用工廠類的靜態(tài)方法,傳入語言編碼,拿到具體的翻譯器實(shí)例進(jìn)行翻譯return ResponseEntity.ok(Factory.getTranslator(code).translate(content));} catch (Exception e) {return ResponseEntity.ok(e.getMessage());}} }最后我們測試一下。
demo完整代碼請戳這里
2、簡單工廠模式在Logback源碼中的應(yīng)用
在大家經(jīng)常使用的 Logback 中,也可以看到 LoggerFactory 中有多個(gè)重載的方法 getLogger()。
public static Logger getLogger(String name) {ILoggerFactory iLoggerFactory = getILoggerFactory();return iLoggerFactory.getLogger(name);}public static Logger getLogger(Class clazz) {return getLogger(clazz.getName());}3、簡單工廠模式在JDK源碼中的應(yīng)用
3.1 Calendar 類為例簡單工廠模式
可以說簡單工廠模式在 JDK 源碼中無處不在,下面以 Calendar 類為例講解簡單工廠模式在 JDK 源碼中的應(yīng)用。Calendar 類的 getInstance() 方法源碼如下。
public static Calendar getInstance() {Calendar cal = createCalendar(TimeZone.getDefaultRef(), Locale.getDefault(Locale.Category.FORMAT));cal.sharedZone = true;return cal; }進(jìn)入?createCalendar() 方法中,源碼如下:
private static Calendar createCalendar(TimeZone zone, Locale aLocale) {Calendar cal = null;String caltype = aLocale.getUnicodeLocaleType("ca");if (caltype == null) {// Calendar type is not specified.// If the specified locale is a Thai locale,// returns a BuddhistCalendar instance.if ("th".equals(aLocale.getLanguage())&& ("TH".equals(aLocale.getCountry()))) {cal = new BuddhistCalendar(zone, aLocale);} else {cal = new GregorianCalendar(zone, aLocale);}} else if (caltype.equals("japanese")) {cal = new JapaneseImperialCalendar(zone, aLocale);} else if (caltype.equals("buddhist")) {cal = new BuddhistCalendar(zone, aLocale);} else {// Unsupported calendar type.// Use Gregorian calendar as a fallback.cal = new GregorianCalendar(zone, aLocale);}return cal; }Calendar 的 UML 類圖如下:
3.2 JDBC簡單工廠模式
當(dāng)我們需要MySQL數(shù)據(jù)庫的驅(qū)動(dòng)時(shí),我們就傳MySQL的參數(shù),用Oracle的就傳相應(yīng)的參數(shù)。在寫JDBC的時(shí)候,JDK來實(shí)現(xiàn)的時(shí)候,
Class.forName("com.mysql.jdbc.Driver");通過Class.forName把mysql的驅(qū)動(dòng)加載進(jìn)來,那如果寫ORACLE的驅(qū)動(dòng)呢,這里就變成對應(yīng)的ORACLE的JDBC的jar包,ORACLE的driver類,然后調(diào)用DriverManager的getConnection方法,
@CallerSensitivepublic static Connection getConnection(String url)throws SQLException {java.util.Properties info = new java.util.Properties();return (getConnection(url, info,Reflection.getCallerClass())); }獲取對應(yīng)的數(shù)據(jù)庫連接,JDBC的過程也是非常簡單的,
// Worker method called by the public getConnection() methods.private static Connection getConnection(String url, java.util.Properties info, Class<?> caller) throws SQLException {/** When callerCl is null, we should check the application's* (which is invoking this class indirectly)* classloader, so that the JDBC driver class outside rt.jar* can be loaded from here.*/ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;synchronized(DriverManager.class) {// synchronize loading of the correct classloader.if (callerCL == null) {callerCL = Thread.currentThread().getContextClassLoader();}}if(url == null) {throw new SQLException("The url cannot be null", "08001");}println("DriverManager.getConnection(\"" + url + "\")");// Walk through the loaded registeredDrivers attempting to make a connection.// Remember the first exception that gets raised so we can reraise it.SQLException reason = null;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());}}// if we got here nobody could connect.if (reason != null) {println("getConnection failed: " + reason);throw reason;}println("getConnection: no suitable driver found for "+ url);throw new SQLException("No suitable driver found for "+ url, "08001");}通過Class.forName這種方式,直接通過反射拿到對應(yīng)的Video,只不過MSYQL這里面還是需要通過注冊的,
// Walk through the loaded registeredDrivers attempting to locate someone// who understands the given URL.for (DriverInfo aDriver : registeredDrivers) {// If the caller does not have permission to load the driver then// skip it.if(isDriverAllowed(aDriver.driver, callerClass)) {try {if(aDriver.driver.acceptsURL(url)) {// Success!println("getDriver returning " + aDriver.driver.getClass().getName());return (aDriver.driver);}} catch(SQLException sqe) {// Drop through and try the next driver.}} else {println(" skipping: " + aDriver.driver.getClass().getName());}}?因?yàn)檫@個(gè)可以看出來它是一個(gè)for循環(huán),在遍歷注冊的一個(gè)驅(qū)動(dòng),
?
private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();
?
并且它是CopyOnWriteArrayList,里面是DriverInfo,初始化的時(shí)候他是一個(gè)空的,具體是什么時(shí)候完成注冊的呢,
這個(gè)時(shí)候就會(huì)在registerDriver(new Driver())這個(gè)方法里面直接注冊這個(gè)Driver,那里面的Driver自然就是MySQL的Driver,
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 {// This is for compatibility with the original DriverManagerthrow new NullPointerException();}println("registerDriver: " + driver);}如果不存在就往里放
if(driver != null) {registeredDrivers.addIfAbsent(new DriverInfo(driver, da));}4、簡單工廠模式的優(yōu)缺點(diǎn)
4.1 簡單工廠模式的優(yōu)點(diǎn)
工廠類是整個(gè)模式的關(guān)鍵,包含了必要的邏輯判斷,根據(jù)外界給定的信息,決定究竟應(yīng)該創(chuàng)建哪個(gè)具體類的對象。通過使用工廠類,外界可以從直接創(chuàng)建具體產(chǎn)品對象的尷尬局面擺脫出來,僅僅需要負(fù)責(zé)“消費(fèi)”對象就可以了。而不必管這些對象究竟如何創(chuàng)建及如何組織的。明確了各自的職責(zé)和權(quán)利,有利于整個(gè)軟件體系結(jié)構(gòu)的優(yōu)化。
4.2 簡單工廠模式的缺點(diǎn)
由于工廠類集中了所有實(shí)例的創(chuàng)建邏輯,違反了高內(nèi)聚責(zé)任分配原則,將全部創(chuàng)建邏輯集中到了一個(gè)工廠類中。它所能創(chuàng)建的類只能是事先考慮到的,如果需要添加新的類,就需要改變工廠類了。
4.3 簡單工廠模式的適用環(huán)境
(1) 工廠類負(fù)責(zé)創(chuàng)建的對象比較少:由于創(chuàng)建的對象較少,不會(huì)造成工廠方法中的業(yè)務(wù)邏輯太過復(fù)雜;
(2) 客戶端只知道傳入工廠類的參數(shù),對于如何創(chuàng)建對象不關(guān)心:客戶端既不需要關(guān)心創(chuàng)建細(xì)節(jié),甚至連類名都不需要記住,只需要知道類型所對應(yīng)的參數(shù)。
?
參考文章:
https://www.jianshu.com/p/72567cc1d63f
http://c.biancheng.net/view/8387.html
https://www.cnblogs.com/thiaoqueen/p/11169924.html
?
總結(jié)
以上是生活随笔為你收集整理的简单工厂模式在Logback源码以及JDK源码中的应用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 利用脚本抢选修课
- 下一篇: yy神曲url解析php_单文件PHP版