抽象工厂讲解
我們來學(xué)一下抽象工廠,抽象工廠模式提供一個(gè)創(chuàng)建一系列相關(guān)或者相互依賴的對象的接口,關(guān)鍵字一系列相關(guān),或相互依賴相關(guān)的接口,那根據(jù)這個(gè)一系列呢,我們會(huì)引出產(chǎn)品等級,產(chǎn)品族這兩個(gè)概念,無須指定他們具體的類,類型是創(chuàng)建型,同一個(gè)主題單獨(dú)的工廠封裝起來,在正常使用中,客戶端需要?jiǎng)?chuàng)建抽象工廠的具體實(shí)現(xiàn),然后使用抽象工廠,作為接口,來創(chuàng)建這一主題的具體對象,那我們在使用的時(shí)候呢,是不需要知道或關(guān)心,他從這些內(nèi)部的工廠中,獲得對象的具體類型,因?yàn)槲覀兊目蛻舳顺绦蚰?僅使用這些接口的通用接口,那抽象工廠模式呢,一組對象的實(shí)現(xiàn)細(xì)節(jié),和他們的使用呢,分離開來
我們接著來看一下適用場景,首先客戶端不依賴于產(chǎn)品類實(shí)例如何被創(chuàng)建,實(shí)現(xiàn)的細(xì)節(jié),這個(gè)呢很好理解,第二個(gè)強(qiáng)調(diào)一系列相關(guān)產(chǎn)品對象,屬于同一產(chǎn)品族,一起使用創(chuàng)建對象時(shí),需要大量的重復(fù)代碼,這里面我們就可以使用抽象工廠,同時(shí)呢提供了一個(gè)產(chǎn)品類的庫,所有的產(chǎn)品以同樣的接口出現(xiàn),從而使客戶端不依賴于具體的實(shí)現(xiàn),那使用抽象工廠模式,能夠在工廠具體變化的時(shí)候,不要修改使用工廠的
接著我們來看一下抽象工廠的優(yōu)點(diǎn),首先具體產(chǎn)品在應(yīng)用層代碼隔離,無須關(guān)心創(chuàng)建細(xì)節(jié),那工廠相關(guān)的這幾個(gè)呢,都有這個(gè)優(yōu)點(diǎn),第二個(gè)優(yōu)點(diǎn)是抽象工廠比較重要的,講一個(gè)系列的產(chǎn)品族統(tǒng)一到一起創(chuàng)建,那后面我們會(huì)講產(chǎn)品族,我們繼續(xù)來看
缺點(diǎn)也比較明顯,規(guī)定了所有可能被創(chuàng)建的產(chǎn)品集合,產(chǎn)品族中擴(kuò)展新的產(chǎn)品比較困難,需要修改抽象工廠的接口,增加了系統(tǒng)的抽象性和理解難度
我們說一下產(chǎn)品等級結(jié)構(gòu)和產(chǎn)品族,我們首先看一下這個(gè)圖
這個(gè)圖詮釋的非常好,而且非常的恰當(dāng),首先呢我們理解一個(gè)概念,可以看到圖中有橢圓形,圓形和正方形,這三個(gè)圖形相同顏色的,就代表一個(gè)產(chǎn)品族,這兒呢是一個(gè)產(chǎn)品族
打個(gè)比方,產(chǎn)品族都是由同一個(gè)工廠生產(chǎn)的,位于不同產(chǎn)品等級結(jié)構(gòu),對于豎著的我們看一下,相同形狀,但是呢,顏色深淺不一樣的,他們是同一個(gè)產(chǎn)品等級結(jié)構(gòu)
比如圓形,橢圓形,和正方形,這里面我們打個(gè)比方,產(chǎn)品族比如美的電器,美的還生產(chǎn)洗衣機(jī),圓形就是洗衣機(jī),美的這個(gè)工廠還生產(chǎn)空調(diào),橢圓形就是空調(diào),他們都是美的品牌,他們都是屬于美的這個(gè)產(chǎn)品族,豎著來看,橢圓形都是空調(diào),第一排是美的空調(diào),第二排那個(gè)橢圓形可以理解成海爾的空調(diào),第三排的橢圓形呢,可以理解成海信的空調(diào),他們都是空調(diào),他們屬于同一個(gè)產(chǎn)品等級結(jié)構(gòu),打個(gè)比方,在產(chǎn)品等級結(jié)構(gòu)中,假設(shè)有一個(gè)抽象類,空調(diào),美的空調(diào),海爾空調(diào),格力空調(diào),海信空調(diào),都是空調(diào),而橫著來看,美的電冰箱,美的洗衣機(jī),美的空調(diào),都是美的,這樣的話就比較好理解了,我們學(xué)習(xí)了工廠方法,那工廠方法模式呢,針對的就是產(chǎn)品等級結(jié)構(gòu),而抽象工廠模式呢,針對的就是產(chǎn)品族,我們只要在美的工廠里面取空調(diào),取出來的肯定是美的的空調(diào),我們只要在美的工廠里面去電冰箱,取出來的肯定也是美的的電冰箱,所以我們只要指出一個(gè)產(chǎn)品所處的產(chǎn)品族,以及他所處的等級結(jié)構(gòu),就可以唯一確定這個(gè)產(chǎn)品,這里面一定要注意,抽象工廠和工廠方法,最大的一個(gè)區(qū)別,然后我們繼續(xù)來看
左側(cè)呢具體的小房子,我們就認(rèn)為他是一個(gè)具體的工廠,剛剛又說美的生產(chǎn)出來的電冰箱空調(diào)洗衣機(jī),全部是美的品牌,只要從左側(cè)具體工廠取出來的,肯定是屬于同一個(gè)產(chǎn)品族
那抽象工廠解決的正式這個(gè)問題,所以具體使用工廠方法還是抽象工廠呢,還要看我們實(shí)際的應(yīng)用場景,而這兩個(gè)模式呢,怎么選擇呢,剛剛也說了,看業(yè)務(wù)場景,那從理論上來說,當(dāng)一個(gè)工廠生產(chǎn)不同等級結(jié)構(gòu)的,一個(gè)產(chǎn)品族中的,那這個(gè)時(shí)候抽象工廠模式,比工廠方法模式呢,更為簡單,更有效率,那在后面的coding當(dāng)中,也會(huì)體會(huì)他們之間的區(qū)別,還有類的個(gè)數(shù)一個(gè)比較,我們會(huì)引入一個(gè)新的應(yīng)用場景,然后在擴(kuò)展的時(shí)候呢,用工廠方法思考一下,用抽象工廠也思考一下,很明顯的就能夠?qū)Ρ瘸鰜?他們的區(qū)別,這樣也會(huì)加深我們對抽象工廠的使用,那整體工廠方面呢,是簡單工廠,工廠方法,還有抽象工廠,我們通過一系列的業(yè)務(wù)引入,場景改變,需求變更,就是希望能夠理解透,牢牢的掌握住這個(gè)知識點(diǎn),那接著回到這個(gè)圖中,這個(gè)工廠創(chuàng)建美的洗衣機(jī),這個(gè)工廠又創(chuàng)建美的電冰箱,這個(gè)工廠又創(chuàng)建了美的的空調(diào)
現(xiàn)在提出了一個(gè)要求,每個(gè)課程不僅僅要有視頻,還要有相應(yīng)的手記,那如果按照工廠方法的一個(gè)方式,來擴(kuò)展的話,想想一下,Java手記,不同的手記,他們之間也是各具特色,那如果按照工廠方法擴(kuò)展要求來說,我們要?jiǎng)?chuàng)建一個(gè)前端的手記類,Java的手記類,Pathon的手記類,前端手記的工廠,Java手記的工廠,還有Pathon手記的工廠,同時(shí)呢我們還要?jiǎng)?chuàng)建手記的抽象類,還有手記的一個(gè)工廠,那在工廠方法中,如果我們的業(yè)務(wù)場景發(fā)生了比較大擴(kuò)展,很容易產(chǎn)生類爆炸這么一種情況,也就是說我們要建很多很多的類,而原來一個(gè)視頻就是一個(gè)課程,而現(xiàn)在一個(gè)視頻加一個(gè)手記是一個(gè)課程,那我們來分析一下,Java視頻,Pathon視頻,前端視頻,他們處于同一個(gè)視頻等級,都是視頻,前端手記,Java手記,Pathon手記,他們處于同一個(gè)產(chǎn)品等級,都是手記,Java視頻和Java手記他們屬于同一產(chǎn)品族,都是Java相關(guān)的,同一產(chǎn)品族,我們就看Java,同一產(chǎn)品等級我們就看他是視頻還是手記即可,那我們現(xiàn)在就來實(shí)現(xiàn)他,那我們先創(chuàng)建一個(gè)包,那我們先來寫一個(gè)課程,在寫課程之前呢,我們先創(chuàng)建課程工廠,這里我們使用interface來聲明
首先從上往下看,上面就是產(chǎn)品族的工廠,課程的工廠,里面有article,里面有video,在IDEA這個(gè)插件里面,它會(huì)自動(dòng)認(rèn)為它是一個(gè)屬性,我們可以試一下,假如我們現(xiàn)在不以get開頭,這個(gè)時(shí)候他就變成一個(gè)方法,因?yàn)槲覀冞@個(gè)是get開頭的,他認(rèn)為他是一個(gè)property
在jackson序列號和反序列化的時(shí)候,對于get開頭和set開頭的方法,也可以通過一些配置,是set和get開頭的方法變成一個(gè)屬性,那這個(gè)不是我們的重點(diǎn),現(xiàn)在他認(rèn)為是一個(gè)屬性或者方法不重要
我們先把他去掉,從上往下看,產(chǎn)品族的工廠,下面有兩個(gè)具體的產(chǎn)品族工廠
分別是Java課程和Python課程的工廠,然后往下看,Java課程工廠生產(chǎn)JavaVideo
生產(chǎn)PathonVideo,生產(chǎn)PathonArticle,而Video和Article呢,分別作為整個(gè)產(chǎn)品族的一個(gè)返回值,他們?nèi)且蕾囉诰唧w的產(chǎn)品,也就是他們?nèi)蕾嚦橄螽a(chǎn)品Video,依賴抽象產(chǎn)品Article,而Article兩個(gè)具體的子類,就是JavaArticle和PathonArticle,他們兩個(gè)就是具體的產(chǎn)品了,那我們往上看一下,那每個(gè)設(shè)計(jì)模式呢,都是結(jié)合實(shí)際的業(yè)務(wù)場景,那在某些業(yè)務(wù)場景呢,可能這種設(shè)計(jì)模式需要改進(jìn)或者優(yōu)化,甚至可能帶來更多的麻煩,例如我們現(xiàn)在增加了算法課程工廠,那對于現(xiàn)在抽象工廠這種模式,非常容易擴(kuò)展,對原來的類還不需要變化,也就是我們在這里創(chuàng)建一個(gè)算法的工廠,他來實(shí)現(xiàn)課程工廠,然后下面我再創(chuàng)建算法的Video,算法手記,兩個(gè)實(shí)際的產(chǎn)品,就可以了,其他這個(gè)類呢,是不需要?jiǎng)拥?因?yàn)樽裱S,實(shí)際產(chǎn)品,他們之間定義的一個(gè)契約,包含抽象方法和接口,那這個(gè)呢就是抽象工廠的一個(gè)好處,那當(dāng)然還有一個(gè)好處
就那現(xiàn)在的類圖來說,我們應(yīng)用層比如Test客戶端,調(diào)用這些類的一個(gè)客戶端代碼,一會(huì)我會(huì)領(lǐng)著大家來寫Test,他在使用的時(shí)候,他并不關(guān)心產(chǎn)品族創(chuàng)建的一個(gè)過程,我們應(yīng)用層代碼只需要關(guān)心值哪個(gè)產(chǎn)品族工廠就可以了,因?yàn)槲覀儜?yīng)用層代碼不會(huì)直接創(chuàng)建Java手記和Pathon手記,Java視頻和Pathon視頻,因?yàn)檎麄€(gè)創(chuàng)建過程是由創(chuàng)建工廠來創(chuàng)建的,這里創(chuàng)建的是一個(gè)產(chǎn)品族產(chǎn)品,這里面創(chuàng)建的是Java肯定不會(huì)錯(cuò),如果我們把代碼放在應(yīng)用層,進(jìn)行耦合的話,如果應(yīng)用層的代碼對Java視頻,Java手記依賴過大的話,那么我們在應(yīng)用層代碼可能會(huì)寫錯(cuò),就是這個(gè)課程是有Java的視頻和Python的手記組成,這樣的話就鬧笑話了,而現(xiàn)在我們是從Java課程的工廠里面取出來的,一定是Java視頻和Java手記,都是屬于Java產(chǎn)品族的,那這個(gè)也是抽象工廠的一大優(yōu)點(diǎn),那我們現(xiàn)在來寫一下應(yīng)用層Test類
和整個(gè)抽象工廠是怎么協(xié)作的,通過這個(gè)圖我們就能夠看出來,我們應(yīng)用層Test代碼根本就不關(guān)心什么具體的視頻或者手記之類的,我只關(guān)心從哪個(gè)工廠拿哪個(gè)產(chǎn)品就是了,我從Java課程工廠里面拿Java的產(chǎn)品,我從Pathon課程工廠里面拿Pathon的手記,肯定不會(huì)拿錯(cuò),也有可能應(yīng)用層的代碼非常復(fù)雜,我從Java課程里面取了一個(gè)手記,從Pathon工廠里面取了一個(gè)視頻,然后展示給頁面,這個(gè)時(shí)候設(shè)計(jì)模式會(huì)跳出來說,怪我了,這個(gè)時(shí)候不能怪設(shè)計(jì)模式,應(yīng)用層的代碼和具體的視頻,是解耦的,和具體的手記也是解耦的,例如說我們現(xiàn)在看到的這個(gè)類,他們現(xiàn)在都屬于同一個(gè)包,如果不在同一個(gè)包,我這里面也不需要導(dǎo)入Java視頻的一個(gè)類,Java手記的一個(gè)類,Pathon視頻的類,Pathon手記的類,這就達(dá)到了一定程度的解耦,我們接著看類圖,那我們在使用抽象工廠的時(shí)候,盡量找固定程度比較高的,比如課程里面肯定是有視頻的,手記,這兩個(gè)幾乎是不會(huì)刪除,他們固定程度是比較高的,那剛剛總結(jié)的就是抽象工廠的優(yōu)點(diǎn),那抽象工廠也是有缺點(diǎn)的,我得快速總結(jié)一下,首先應(yīng)用代碼不和具體的產(chǎn)品發(fā)生依賴,他只和具體的產(chǎn)品族工廠發(fā)生以來關(guān)系,第二點(diǎn)從具體的產(chǎn)品族工廠取出來的,肯定屬于同一產(chǎn)品族,第三點(diǎn)擴(kuò)展性好,例如新增前端算法的產(chǎn)品族,只需要增加前端的課程工廠,還有具體的產(chǎn)品,就可以了,很方便,無須修改原有系統(tǒng),符合開閉原則,那整個(gè)就是根據(jù)實(shí)際的應(yīng)用場景,進(jìn)行取舍,所以接下來我就說一下缺點(diǎn),缺點(diǎn)最主要的是什么呢,新增產(chǎn)品等級,會(huì)比較麻煩,要對原有的系統(tǒng)進(jìn)行比較大的修改,那在這種業(yè)務(wù)場景下,他就違背了開閉原則,所以某些原則在某些場景符合的,比如說,我們擴(kuò)展新的產(chǎn)品族,這個(gè)是符合開閉原則的,但是在抽象工廠里邊,如果為現(xiàn)有的產(chǎn)品族增加新的產(chǎn)品等級,那就不符合開閉原則了,那這個(gè)時(shí)候我們引入一個(gè)新的業(yè)務(wù)場景,這個(gè)課程除了視頻和手記之外呢,還要把源碼放到里面,也就是說視頻手記和源碼,才能構(gòu)成這個(gè)課程,那么我們對原有的修改就比較大了,首先我們要把這個(gè)課程工廠,新增一個(gè)源碼,然后實(shí)現(xiàn)這個(gè)課程工廠的具體工廠,也都要增加源碼,下面我們再創(chuàng)建一個(gè)源碼的抽象類,然后再寫具體的JAVA源碼,Pathon源碼的,實(shí)際產(chǎn)品,那在這種情況下,是不符合開閉原則的,因?yàn)槲覀円薷脑瓉砝锩婢唧w的實(shí)現(xiàn)了,所以對于產(chǎn)品族里面擴(kuò)展,新的產(chǎn)品等級,還是比較麻煩的,所以平時(shí)在我們工廠的應(yīng)用場景,例如產(chǎn)品等級結(jié)構(gòu),產(chǎn)品固定的,并且還需要多個(gè)產(chǎn)品來組合到一起,形成產(chǎn)品族的,每個(gè)業(yè)務(wù)場景,但是呢如果碰到頻繁改動(dòng),那也不適用于抽象工廠,比如今天課程里面再加一個(gè)工廠,明天工廠里面還要加一個(gè)東西,后天我還要加個(gè)東西,那這個(gè)就真的太頻繁了,這個(gè)類要經(jīng)常經(jīng)常改,這些抽象相關(guān)的類,所以我們平時(shí)找相對固定的,即使你們過了好久才改一次呢,也沒有關(guān)系的,比如我們新增了一個(gè)源碼,可能這種工廠的設(shè)計(jì)模式呢可以固定一年兩年三年的,這個(gè)呢時(shí)間就很長了,這種即使我們?nèi)ジ?也是OK的,只不過我們測試的時(shí)候,仔細(xì)一些,就可以了,比如說隨著網(wǎng)站的業(yè)務(wù)不斷擴(kuò)大,一年或者兩年呢,添加一個(gè)新的產(chǎn)品等級,這都是沒有問題的,半年也是OK的,對比工廠方法,一起來看一下,我們把property去掉就OK了,這樣就變成一個(gè)正常的方法演示
我們可以發(fā)現(xiàn)抽象工廠里面的方法啊,工廠方法關(guān)注等級結(jié)構(gòu),抽象工廠關(guān)注產(chǎn)品族,這個(gè)是兩個(gè)設(shè)計(jì)模式最大的區(qū)別,那抽象工廠還是非常有威力的,他能幫助我們針對抽象編程,而不是針對具體的類來編程,抽象工廠我們就講完了,希望通過對比的方式,能融會(huì)貫通,使用這兩種工廠相關(guān)的模式
package com.learn.design.pattern.creational.abstractfactory;/*** 再創(chuàng)建一個(gè)手記的class* 那這兩個(gè)類呢也都是抽象類* * * @author Leon.Sun**/
public abstract class Article {public abstract void produce();
}
package com.learn.design.pattern.creational.abstractfactory;/*** 創(chuàng)建這個(gè)Video class* * * @author Leon.Sun**/
public abstract class Video {public abstract void produce();}
package com.learn.design.pattern.creational.abstractfactory;/*** 創(chuàng)建抽象類和接口的一個(gè)業(yè)務(wù)場景的選擇* 那在我們這里面呢* 我們會(huì)聲明兩個(gè)方法* 一個(gè)是獲取視頻* 一個(gè)是獲取手記* 而這兩個(gè)都是抽象方法* 使用抽象類也是OK的* 只不過這兩種我們都來體會(huì)一下* 我們寫兩個(gè)方法* * 課程是視頻加手記* 才可以稱之為內(nèi)容* 那么我們就聲明一個(gè)產(chǎn)品族的一個(gè)工廠* 我們聲明Java課程的一個(gè)工廠* * * @author Leon.Sun**/
public interface CourseFactory {Video getVideo();Article getArticle();}
package com.learn.design.pattern.creational.abstractfactory;/*** 他呢繼承Video* * * @author Leon.Sun**/
public class JavaVideo extends Video {@Overridepublic void produce() {System.out.println("錄制Java課程視頻");}
}
package com.learn.design.pattern.creational.abstractfactory;/*** 他呢繼承Article這個(gè)類* * * @author Leon.Sun**/
public class JavaArticle extends Article {/*** 然后實(shí)現(xiàn)produce方法* */@Overridepublic void produce() {System.out.println("編寫Java課程手記");}
}
package com.learn.design.pattern.creational.abstractfactory;/*** 他來實(shí)現(xiàn)CourseFactory* 那現(xiàn)在我們要找到具體的Java視頻和Java手記* 那我們繼續(xù)聲明類* * 我們使用抽象工廠的時(shí)候* 并沒有聲明視頻的工廠和手記的工廠* 而是聲明一個(gè)組合到一起的課程的工廠* 但是具體的產(chǎn)品是有的* Java手記和Java視頻* Java課程作為產(chǎn)品族* 他包含視頻和手記* 那我們接著寫一套Python的* * @author Leon.Sun**/
public class JavaCourseFactory implements CourseFactory {@Overridepublic Video getVideo() {/*** 這里直接返回JavaVideo* */return new JavaVideo();}@Overridepublic Article getArticle() {/*** 這里就直接返回JavaArticle* */return new JavaArticle();}
}
package com.learn.design.pattern.creational.abstractfactory;/*** PythonVideo繼承Video* 然后再寫一個(gè)Pathon的手記* * @author Leon.Sun**/
public class PythonVideo extends Video {@Overridepublic void produce() {System.out.println("錄制Python課程視頻");}
}
package com.learn.design.pattern.creational.abstractfactory;/*** 繼承Article* * * @author Leon.Sun**/
public class PythonArticle extends Article {@Overridepublic void produce() {System.out.println("編寫Python課程手記");}
}
package com.learn.design.pattern.creational.abstractfactory;/*** 我們再創(chuàng)建一個(gè)Pathon的課程工廠* 他呢實(shí)現(xiàn)課程工廠CourseFactory* 然后把里面的具體實(shí)現(xiàn)* 寫一下* 當(dāng)我們要擴(kuò)展課程的時(shí)候* 只需要?jiǎng)?chuàng)建產(chǎn)品族的一個(gè)工廠* 還有具體的產(chǎn)品就可以* 現(xiàn)在我們對整體的抽象工廠我們看一下* 我們看一下類圖* * * @author Leon.Sun**/
public class PythonCourseFactory implements CourseFactory {@Overridepublic Video getVideo() {/*** 這里直接new一個(gè)PythonVideo*/return new PythonVideo();}@Overridepublic Article getArticle() {/*** 這里直接new一個(gè)PythonArticle* */return new PythonArticle();}
}
package com.learn.design.pattern.creational.abstractfactory;/*** 都是一個(gè)產(chǎn)品族* 注意一定要強(qiáng)調(diào)產(chǎn)品族的概念* 在抽象工廠這個(gè)設(shè)計(jì)模式當(dāng)中* 那我們來看一下UML* * @author Leon.Sun**/
public class Test {public static void main(String[] args) {/*** 我們創(chuàng)建一個(gè)CourseFactory* 我們new一個(gè)JavaCourseFactory* * */CourseFactory courseFactory = new JavaCourseFactory();/*** 這個(gè)Video我們直接從courseFactory里面去取* * */Video video = courseFactory.getVideo();/*** 這個(gè)手記我也是直接從courseFactory去取* 只要從這個(gè)工廠里面取的* 肯定都是Java產(chǎn)品族的* * */Article article = courseFactory.getArticle();video.produce();article.produce();}
}
?
總結(jié)
- 上一篇: 工厂方法源码解析(jdk+logback
- 下一篇: 抽象工厂源码解析