二、架構(gòu)師內(nèi)功心法之設(shè)計(jì)模式
2.架構(gòu)師內(nèi)功心法之設(shè)計(jì)模式
2.1.課程目標(biāo)
1塔淤、通過(guò)對(duì)本章內(nèi)容的學(xué)習(xí)摘昌,了解設(shè)計(jì)模式的由來(lái)。
2高蜂、介紹設(shè)計(jì)模式能幫我們解決哪些問(wèn)題聪黎。
3、剖析工廠(chǎng)模式的歷史由來(lái)及應(yīng)用場(chǎng)景备恤。
2.2.內(nèi)容定位
不用設(shè)計(jì)模式并非不可以稿饰,但是用好設(shè)計(jì)模式能幫助我們更好地解決實(shí)際問(wèn)題,設(shè)計(jì)模式最重要的
是解耦露泊。設(shè)計(jì)模式天天都在用喉镰,但自己卻無(wú)感知。我們把設(shè)計(jì)模式作為一個(gè)專(zhuān)題滤淳,主要是學(xué)習(xí)設(shè)計(jì)模式
是如何總結(jié)經(jīng)驗(yàn)的梧喷,把經(jīng)驗(yàn)為自己所用砌左。學(xué)設(shè)計(jì)模式也是鍛煉將業(yè)務(wù)需求轉(zhuǎn)換技術(shù)實(shí)現(xiàn)的一種非常有效
的方式脖咐。
2.3.回顧軟件設(shè)計(jì)原則
設(shè)計(jì)原則 | 解釋 |
---|---|
開(kāi)閉原則 | 對(duì)擴(kuò)展開(kāi)放,對(duì)修改關(guān)閉 |
依賴(lài)倒置原則 | 通過(guò)抽象使各個(gè)類(lèi)或者模塊不相互影響汇歹,實(shí)現(xiàn)松耦合屁擅。 |
單一職責(zé)原則 | 一個(gè)類(lèi)、接口产弹、方法只做一件事派歌。 |
接口隔離原則 | 盡量保證接口的純潔性弯囊,客戶(hù)端不應(yīng)該依賴(lài)不需要的接口。 |
迪米特法則 | 又叫最少知道原則胶果,一個(gè)類(lèi)對(duì)其所依賴(lài)的類(lèi)知道得越少越好匾嘱。 |
里氏替換原則 | 子類(lèi)可以擴(kuò)展父類(lèi)的功能但不能改變父類(lèi)原有的功能。 |
合成復(fù)用原則 | 盡量使用對(duì)象組合早抠、聚合霎烙,而不使用繼承關(guān)系達(dá)到代碼復(fù)用的目的。 |
2.4.設(shè)計(jì)模式總覽
寫(xiě)出優(yōu)雅的代碼
更好地重構(gòu)項(xiàng)目
經(jīng)典框架都在用設(shè)計(jì)模式解決問(wèn)題
Spring就是一個(gè)把設(shè)計(jì)模式用得淋漓盡致的經(jīng)典框架蕊连,其實(shí)從類(lèi)的命名就能看出來(lái)悬垃,我來(lái)一一列舉:
設(shè)計(jì)模式名稱(chēng) | 舉例 |
---|---|
工廠(chǎng)模式 | BeanFactory |
裝飾器模式 | BeanWrapper |
代理模式 | AopProxy |
委派模式 | DispatcherServlet |
策略模式 | HandlerMapping |
適配器模式 | HandlerAdapter |
模板模式 | JdbcTemplate |
觀(guān)察者模式 | ContextLoaderListener |
我們的課程中,會(huì)圍繞 Spring 的 IOC甘苍、AOP尝蠕、MVC、JDBC
這樣的思路展開(kāi)载庭,根據(jù)其設(shè)計(jì)類(lèi)型來(lái)設(shè)計(jì)講解順序:
類(lèi)型 | 名稱(chēng) | 英文 |
---|---|---|
創(chuàng)建型模式 | 工廠(chǎng)模式 | Factory Pattern |
單例模式 | Singleton Pattern | |
原型模式 | Prototype Pattern | |
結(jié)構(gòu)型模式 | 適配器模式 | Adapter Pattern |
裝飾器模式 | Decorator Patter | |
代理模式 | Proxy Pattern | |
行為性模式 | 策略模式 | Strategy Pattern |
模板模式 | Template Pattern | |
委派模式 | Delegate Pattern | |
觀(guān)察者模式 | Observer Pattern |
3.工廠(chǎng)模式詳解
3.1.工廠(chǎng)模式的歷史由來(lái)
原始社會(huì)自給自足(沒(méi)有工廠(chǎng))看彼、農(nóng)耕社會(huì)小作坊(簡(jiǎn)單工廠(chǎng),民間酒
坊)囚聚、工業(yè)革命流水線(xiàn)(工廠(chǎng)方法闲昭,自產(chǎn)自銷(xiāo))、現(xiàn)代產(chǎn)業(yè)鏈代工廠(chǎng)(抽象工廠(chǎng)靡挥,富士康)
3.2.簡(jiǎn)單工廠(chǎng)模式
3.2.1.定義
簡(jiǎn)單工廠(chǎng)模式(Simple Factory Pattern)是指由一個(gè)工廠(chǎng)對(duì)象決定創(chuàng)建出哪一種產(chǎn)品類(lèi)的實(shí)例序矩,
但它不屬于GOF 23種設(shè)計(jì)模式。簡(jiǎn)單工廠(chǎng)適用于工廠(chǎng)類(lèi)負(fù)責(zé)創(chuàng)建的對(duì)象較少的場(chǎng)景跋破,且客戶(hù)端只需要
傳入工廠(chǎng)類(lèi)的參數(shù)簸淀,對(duì)于如何創(chuàng)建對(duì)象的邏輯不需要關(guān)心。
3.2.2.demo
public class SimpleFactoryTest {
public static void main(String[] args) {
CourseFactory factory = new CourseFactory();
ICourse course = factory.create(JavaCourse.class);
course.record();
}
}
public class JavaCourse implements ICourse {
public void record() {
System.out.println("錄制Java課程");
}
}
public class CourseFactory {
public ICourse create(Class<? extends ICourse> clazz){
// 反射
try {
if (null != clazz) {
return clazz.newInstance();
}
}catch (Exception e){
e.printStackTrace();
}
return null;
}
}
<img src="http://woshiamiaojiang.gitee.io/image-hosting/SimpleFactory.png" style="zoom: 50%;" />
3.2.3.源碼
Calendar.getInstance()
LoggerFactory.getLogger()
簡(jiǎn)單工廠(chǎng)模式在 JDK 源碼也是無(wú)處不在毒返,現(xiàn)在我們來(lái)舉個(gè)例子租幕,例如 Calendar 類(lèi),看
Calendar.getInstance()方法拧簸,下面打開(kāi)的是Calendar的具體創(chuàng)建類(lèi):
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) {
// fall back to the default instantiation
}
}
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 no known calendar type is explicitly specified,
// perform the traditional way to create a Calendar:
// create a BuddhistCalendar for th_TH locale,
// a JapaneseImperialCalendar for ja_JP_JP locale, or
// a GregorianCalendar for any other locales.
// NOTE: The language, country and variant strings are interned.
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;
}
還有一個(gè)大家經(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.2.4.優(yōu)缺點(diǎn)
- 優(yōu)點(diǎn)
- 簡(jiǎn)單
- 缺點(diǎn)
- 工廠(chǎng)類(lèi)的職責(zé)相對(duì)過(guò)重,不易于擴(kuò)展過(guò)于復(fù)雜的產(chǎn)品結(jié)構(gòu)盆赤。
3.3.工廠(chǎng)方法模式
3.3.1.定義
工廠(chǎng)方法模式(Factory Method Pattern)是指定義一個(gè)創(chuàng)建對(duì)象的接口贾富,但讓實(shí)現(xiàn)這個(gè)接口的類(lèi)
來(lái)決定實(shí)例化哪個(gè)類(lèi),工廠(chǎng)方法讓類(lèi)的實(shí)例化推遲到子類(lèi)中進(jìn)行牺六。在工廠(chǎng)方法模式中用戶(hù)只需要關(guān)心所
需產(chǎn)品對(duì)應(yīng)的工廠(chǎng)颤枪,無(wú)須關(guān)心創(chuàng)建細(xì)節(jié),而且加入新的產(chǎn)品符合開(kāi)閉原則淑际。
3.3.2.demo
public class FactoryMethodTest {
public static void main(String[] args) {
// Python課程工廠(chǎng)
ICourseFactory factory = new PythonCourseFactory();
ICourse course = factory.create();
course.record();
// Java課程工廠(chǎng)
factory = new JavaCourseFactory();
course = factory.create();
course.record();
}
}
public class JavaCourseFactory implements ICourseFactory {
public ICourse create() {
return new JavaCourse();
}
}
public interface ICourseFactory {
ICourse create();
}
public class JavaCourse implements ICourse {
public void record() {
System.out.println("錄制Java課程");
}
}
public interface ICourse {
void record();
}
<img src="http://woshiamiaojiang.gitee.io/image-hosting/FactoryMethod.png" style="zoom: 50%;" />
3.3.3.源碼
ApplicationContext就是工廠(chǎng)方法模式
再來(lái)看看logback中工廠(chǎng)方法模式的應(yīng)用畏纲,看看類(lèi)圖就OK了:
<img src="http://woshiamiaojiang.gitee.io/image-hosting/FactoryMethod2.png" style="zoom: 50%;" />
3.3.4.優(yōu)缺點(diǎn)
- 工廠(chǎng)方法適用于以下場(chǎng)景:
- 創(chuàng)建對(duì)象需要大量重復(fù)的代碼扇住。
- 客戶(hù)端(應(yīng)用層)不依賴(lài)于產(chǎn)品類(lèi)實(shí)例如何被創(chuàng)建、實(shí)現(xiàn)等細(xì)節(jié)盗胀。
- 一個(gè)類(lèi)通過(guò)其子類(lèi)來(lái)指定創(chuàng)建哪個(gè)對(duì)象艘蹋。
- 工廠(chǎng)方法也有缺點(diǎn):
- 類(lèi)的個(gè)數(shù)容易過(guò)多,增加復(fù)雜度票灰。
- 增加了系統(tǒng)的抽象性和理解難度簿训。
3.4.抽象工廠(chǎng)模式
3.4.1.定義
抽象工廠(chǎng)模式(AbastractFactory Pattern)是指提供一個(gè)創(chuàng)建一系列相關(guān)或相互依賴(lài)對(duì)象的接口,無(wú)須指定他們具體的類(lèi)米间∏科罚客戶(hù)端(應(yīng)用層)不依賴(lài)于產(chǎn)品類(lèi)實(shí)例如何被創(chuàng)建、實(shí)現(xiàn)等細(xì)節(jié)屈糊,強(qiáng)調(diào)的是一
系列相關(guān)的產(chǎn)品對(duì)象(屬于同一產(chǎn)品族)一起使用創(chuàng)建對(duì)象需要大量重復(fù)的代碼的榛。需要提供一個(gè)產(chǎn)品類(lèi)
的庫(kù),所有的產(chǎn)品以同樣的接口出現(xiàn)逻锐,從而使客戶(hù)端不依賴(lài)于具體實(shí)現(xiàn)夫晌。
講解抽象工廠(chǎng)之前,我們要了解兩個(gè)概念產(chǎn)品等級(jí)結(jié)構(gòu)和產(chǎn)品族昧诱,看下面的圖:
<img src="http://woshiamiaojiang.gitee.io/image-hosting/image-20200225191403439.png" alt="image-20200225191403439" style="zoom:50%;" />
從上圖中看出有正方形晓淀,圓形和菱形三種圖形,相同顏色深淺的就代表同一個(gè)產(chǎn)品族盏档,相同形狀的代表
同一個(gè)產(chǎn)品等級(jí)結(jié)構(gòu)凶掰。同樣可以從生活中來(lái)舉例,比如蜈亩,美的電器生產(chǎn)多種家用電器懦窘。那么上圖中,顏
色最深的正方形就代表美的洗衣機(jī)稚配、顏色最深的圓形代表美的空調(diào)畅涂、顏色最深的菱形代表美的熱水器,
顏色最深的一排都屬于美的品牌道川,都是美的電器這個(gè)產(chǎn)品族午衰。再看最右側(cè)的菱形,顏色最深的我們指定
了代表美的熱水器冒萄,那么第二排顏色稍微淺一點(diǎn)的菱形臊岸,代表海信的熱水器。同理宦言,同一產(chǎn)品結(jié)構(gòu)下還
有格力熱水器扇单,格力空調(diào)商模,格力洗衣機(jī)奠旺。
再看下面的這張圖蜘澜,最左側(cè)的小房子我們就認(rèn)為具體的工廠(chǎng),有美的工廠(chǎng)响疚,有海信工廠(chǎng)鄙信,有格力工廠(chǎng)。
每個(gè)品牌的工廠(chǎng)都生產(chǎn)洗衣機(jī)忿晕、熱水器和空調(diào)装诡。
<img src="http://woshiamiaojiang.gitee.io/image-hosting/image-20200225191741688.png" alt="image-20200225191741688" style="zoom:50%;" />
3.4.2.demo
public class AbstractFactoryTest {
public static void main(String[] args) {
JavaCourseFactory factory = new JavaCourseFactory();
factory.createNote().edit();
factory.createVideo().record();
}
}
/**
* 抽象工廠(chǎng)CourseFactory類(lèi):
* 抽象工廠(chǎng)是用戶(hù)的主入口
* 在Spring中應(yīng)用得最為廣泛的一種設(shè)計(jì)模式
* 易于擴(kuò)展
*/
public abstract class CourseFactory {
public void init(){
System.out.println("初始化基礎(chǔ)數(shù)據(jù)");
}
protected abstract INote createNote();
protected abstract IVideo createVideo();
}
/**
* 創(chuàng)建Java產(chǎn)品族的具體工廠(chǎng)JavaCourseFactory
*/
public class JavaCourseFactory extends CourseFactory {
public INote createNote() {
super.init();
return new JavaNote();
}
public IVideo createVideo() {
super.init();
return new JavaVideo();
}
}
/**
* 創(chuàng)建Java產(chǎn)品族,Java視頻JavaVideo類(lèi):Java視頻
*/
public class JavaVideo implements IVideo {
public void record() {
System.out.println("錄制Java視頻");
}
}
/**
* 錄播視頻:IVideo接口
*/
public interface IVideo {
void record();
}
/**
* 擴(kuò)展產(chǎn)品等級(jí)Java課堂筆記JavaNote類(lèi):Java筆記
*/
public class JavaNote implements INote {
public void edit() {
System.out.println("編寫(xiě)Java筆記");
}
}
/**
* 課堂筆記:INote接口
*/
public interface INote {
void edit();
}
// 創(chuàng)建Python產(chǎn)品族的具體工廠(chǎng)PythonCourseFactory省略践盼。鸦采。。
<img src="http://woshiamiaojiang.gitee.io/image-hosting/AbstractFactory.png" style="zoom: 33%;" />
上面的代碼完整地描述了兩個(gè)產(chǎn)品族Java課程和Python課程咕幻,也描述了兩個(gè)產(chǎn)品等級(jí)視頻和手記渔伯。抽象工廠(chǎng)非常完美清晰地描述這樣一層復(fù)雜的關(guān)系。但是肄程,不知道大家有沒(méi)有發(fā)現(xiàn)锣吼,如果我們?cè)倮^續(xù)擴(kuò)展
產(chǎn)品等級(jí),將源碼 Source也加入到課程中蓝厌,那么我們的代碼從抽象工廠(chǎng)玄叠,到具體工廠(chǎng)要全部調(diào)整,很顯然不符合開(kāi)閉原則拓提。
3.4.3.源碼
AbstractFactory
AnnotationApplicationContext
Xml
適合長(zhǎng)時(shí)間不變動(dòng)的場(chǎng)景
3.4.3.優(yōu)缺點(diǎn)
抽象工廠(chǎng)缺點(diǎn)
- 規(guī)定了所有可能被創(chuàng)建的產(chǎn)品集合读恃,產(chǎn)品族中擴(kuò)展新的產(chǎn)品困難,需要修改抽象工廠(chǎng)的接口代态。
- 增加了系統(tǒng)的抽象性和理解難度狐粱。
3.5.簡(jiǎn)單工廠(chǎng) vs 工廠(chǎng)方法 vs 抽象工廠(chǎng)
簡(jiǎn)單工廠(chǎng):產(chǎn)品的工廠(chǎng)
工廠(chǎng)方法:工廠(chǎng)的工廠(chǎng)
抽象工廠(chǎng):復(fù)雜產(chǎn)品的工廠(chǎng)
簡(jiǎn)單工廠(chǎng):工廠(chǎng)是一個(gè)實(shí)體類(lèi),內(nèi)部直接根據(jù)邏輯創(chuàng)建對(duì)應(yīng)的產(chǎn)品胆数。
工廠(chǎng)方法:工廠(chǎng)首先有個(gè)接口定義規(guī)范肌蜻。不同的產(chǎn)品使用不同的實(shí)體類(lèi)工廠(chǎng)根據(jù)規(guī)范和需求創(chuàng)建對(duì)應(yīng)的產(chǎn)品。這就是它們的區(qū)別必尼。
工廠(chǎng)方法是生產(chǎn)一類(lèi)產(chǎn)品蒋搜,抽象工廠(chǎng)是生產(chǎn)一個(gè)產(chǎn)品族
3.6.作業(yè)
1、工廠(chǎng)類(lèi)一定需要將構(gòu)造方法私有化嗎判莉,為什么豆挽?
不一定。抽象工廠(chǎng)類(lèi)就不能券盅,否則父類(lèi)的私有構(gòu)造方法就不能被子類(lèi)調(diào)用帮哈。
2、用工廠(chǎng)模式設(shè)計(jì)支付業(yè)務(wù)場(chǎng)景锰镀,包含跨境支付娘侍,支付寶咖刃、微信、銀聯(lián)支付憾筏,并畫(huà)出類(lèi)圖嚎杨。
/**
* description: 支付接口
*/
public interface IPay {
/**
* 支付方法
*/
void pay();
}
/**
* description: 支付寶支付
*/
public class AliPay implements IPay {
public void pay() {
System.out.println("支付寶支付");
}
}
/**
* description: 微信支付
*/
public class WxPay implements IPay {
public void pay() {
System.out.println("微信支付");
}
}
/**
* description: 銀聯(lián)支付
*/
public class UniPay implements IPay {
public void pay() {
System.out.println("銀聯(lián)支付");
}
}
/**
* description: 蘋(píng)果支付
*/
public class ApplePay implements IPay {
public void pay() {
System.out.println("蘋(píng)果支付");
}
}
/**
* description: 支付抽象工廠(chǎng)
*/
public abstract class AbstractPayFactory {
public void init() {
System.out.println("初始化基礎(chǔ)數(shù)據(jù)");
}
}
/**
* description: 國(guó)內(nèi)支付
*/
public class ChinaPayFactory extends AbstractPayFactory {
protected IPay createAliPay() {
super.init();
return new AliPay();
}
protected IPay createWxPay() {
super.init();
return new WxPay();
}
protected IPay createUniPay() {
super.init();
return new UniPay();
}
}
/**
* description: 國(guó)外支付
*/
public class ForeignPayFactory extends AbstractPayFactory {
protected IPay createApplePay() {
super.init();
return new ApplePay();
}
}
/**
* description: 抽象工廠(chǎng)方法測(cè)試
*/
public class AbstractPayFactoryTest {
public static void main(String[] args) {
ChinaPayFactory chinaPayFactory = new ChinaPayFactory();
chinaPayFactory.createAliPay().pay();
chinaPayFactory.createWxPay().pay();
chinaPayFactory.createUniPay().pay();
ForeignPayFactory foreignPayFactory = new ForeignPayFactory();
foreignPayFactory.createApplePay().pay();
}
}
<img src="http://woshiamiaojiang.gitee.io/image-hosting/FactoryPatternHomework.png" style="zoom:50%;" />