简单工厂讲解
現在我們來學習簡單工廠,簡單工廠的定義和類型,他的定義非常簡單,有一個工廠對象決定創建出哪一種產品類的實例,關鍵字創建產品,他的類型屬于創建型,但是不屬于GOF23種設計模式當中,這里面我也要講一下,因為抽象工廠和工廠方法,里面都是由簡單工廠一步一步演進,那我們講完這個之后呢,也可以形成對比,這樣對大家理解是非常有益處的
我們看一下簡單工廠的應用場景,首先工廠類創建的對象比較少,這種場景適合簡單工廠,還有客戶類只知道傳入工廠類的參數,對于如何創建對象,也就是創建對象的具體邏輯呢,不關心,那簡單工廠非常簡單,一會領著大家來coding的時候,相信小伙伴們一定會理解的
我們來看一下簡單工廠的優點,只需要傳入一個正確的參數,就可以獲取你所需要的對象,而無需知道其創建細節,那還有判斷的邏輯,可以決定在哪個場景創建一個實例,客戶端可以免除直接創建的責任,那這里所說的客戶端,包括客戶端應用層,后續我們都會用Test測試類來描述,這里面只是生產產品,簡單工廠通過這種做法,實現了責任的分割,提供了專門的工廠類,去創建對象,甚至不需要知道產品的類名,只需要知道對應的參數是什么,參數和類名肯定是參數更簡單,第二個是不需要知道其創建細節,我只要告訴你我需要一杯咖啡,簡單工廠直接返回咖啡就行了
我們看一下簡單工廠有什么缺點,工廠類的職責相對過重,增加新的產品的時候,需要修改簡單工廠類的判斷邏輯,違背了開閉原則,前面講原則的時候都有說,這些原則不能全部遵守,也不能不遵守,這是一個平衡,要把握其中的度,根據實際的業務模型,因為工廠類集中了創建邏輯,一旦出現問題,或者出現bug,也會增加系統中類的個數,增加了系統的復雜度,和理解難度,一旦增加新的產品,我們是不得不修改工廠的邏輯的,那在產品類型非常多的時候,整個工廠邏輯就會過于復雜,不利于維護和擴展,那簡單工廠還有一個缺點,那后面抽象工廠中
接下來我們一起來coding,我們也會查看簡單工廠的UML,同時也會對源碼進行解析,包括JDK的源碼,會查看mybatis的源碼,找到我們學習的設計模式,以設計模式的角度來對開源代碼進行一個解析
我們現在來學習工廠的相關模式,我們先來了解簡單工廠,簡單工廠嚴格意義上來說,他并不屬于GOF23種設計模式的一種,這里邊一定要注意,那簡單工廠像是一個編碼風格,和習慣,很多人會把簡單工廠認為是工廠模式,那工廠關鍵字相關的設計模式在GOF當中,是分為工廠方法和抽象工廠的,那我們看一下維基百科對于設計模式的介紹,這里有一個Patterns by Type,按照類型進行分類的一個設計模式,首先看這里Creational,這里是有5個設計模式的,分別是抽象工廠,建造者,工廠方法,原型模式,還有單例模式,而這里是沒有簡單工廠模式的,簡單工廠我們也是要講的,首先從簡單工廠開始,我們學習并了解之后呢,對于工廠方法和抽象工廠,我們理解起來也會更容易一些,那首先我們創建一個包,在這里我們就會學習Creational設計模式,在這里注意一下,簡單工廠不屬于GOF設計模式當中的,但是我們把它單獨創建一個包,為了分類更合理,把它也放到Creational里面,那簡單工廠非常簡單,我們來看一下例子,我們在學習這里面所有的設計模式的時候,首先我們描述一個業務場景,例如網上有JAVA的視頻,Python的視頻,前端的視頻
現在Test里面生成了一個PathonVideo,然后這兩個Video繼承Video,然后我們回到Test里面
這個來看就是非常清晰了,原來這個應用層client客戶端,直接依賴PathonVideo和JavaVideo,現在并不是,應用層Test測試類,他只依賴VideoFactory,而VideoFactory,創建具體的,Java視頻還是Pathon視頻,具體的生產過程都在VideoFactory里面,Test只管直接使用應用層生產視頻,應用層找工廠,我直接用就可以了,我會告訴你我要什么視頻,然后VideoFactory給我生產,那我們回到代碼
package com.learn.design.pattern.creational.simplefactory;/*** 具體的視頻的屬性* 時常* 我們在類中就不描述了* 我們有一個生產視頻的方法* 而這個生產視頻應該是一個抽象方法* 例如JAVA課程的視頻* 和Python課程的視頻* 生產過程會有點不一樣* * * @author Leon.Sun**/
public abstract class Video {/*** 所以我們現在寫一個抽象方法* 這個時候我們的類也要是抽象類才可以* 因為擁有了抽象方法* 這個類必然是抽象類* * */public abstract void produce();}
package com.learn.design.pattern.creational.simplefactory;/*** 緊接著我們創建一個JAVA的視頻實現類* 他繼承Video這個抽象類* 同時實現produce方法* 現在我們填充這個實現* * * @author Leon.Sun**/
public class JavaVideo extends Video {@Overridepublic void produce() {System.out.println("錄制Java課程視頻");}
}
package com.learn.design.pattern.creational.simplefactory;/*** PythonVideo他也實現Vedio* 實現方法* * @author Leon.Sun**/
public class PythonVideo extends Video {@Overridepublic void produce() {System.out.println("錄制Python課程視頻");}
}
package com.learn.design.pattern.creational.simplefactory;/*** * @author Leon.Sun**/
public class VideoFactory {/*** 這里面我們使用Class* * 這個class* 就是包名* 后面是一個類* * * @param c* @return*/public Video getVideo(Class c){/*** 這里聲明Video等于null* 后面會說明為什么要什么Video等于null* * */Video video = null;try {/*** c.getName()獲取類名* 這里面還需要一個強轉* c.getName()是一個全的* 包括包名和類名* 然后獲取他的一個對象* 把它強轉成父類* 即使我們傳JavaVideo或者PathonVideo* 都可以強轉成父類的* 所以這里面Video也就賦值成功了* * 然后調用newInstance方法* 來獲取這個對象* 然后強轉賦值給Video* * */video = (Video) Class.forName(c.getName()).newInstance();} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();}/*** 下邊我們直接返回Video就可以了* */return video;}/*** 首先我們要創建一個方法* 返回值是Video* 我們還要傳一個類型type* 根據type判斷返回什么樣的子類實現* 那這個也比較簡單* 我們直接寫* * 我們看一下工廠的具體實現* 現在我們要加一個算法的課程* 或者前端的課程* 我們要修改這個類的一個實現* 里面創建一個具體的* 前端課程的一個value* 也就是說隨著課程的一個擴展* 我們的類要不斷地修改* 而修改就會帶來風險* 通過修改這個類* 來擴展具體的一個類型* 所以他并不符合開閉原則* 那我們再來看一下UML* 我們可以想象一下我們的目的是為了擴展* 那是為了對擴展開放* 和對修改關閉* 這個時候我們就可以使用工廠方法來演進一下* 我們再理會一下他們之間的差異* 之后我們再像抽象工廠演進* 然后再體會他們之間的不同* 那通過這個我們加深對工廠模式的理解* 也有把這個方法寫成靜態方法的* 通過靜態方法來建一個簡單的工廠* 這個也是比較常見的* 使用靜態方法就直接調用就可以了* 不用創建VideoFactory* 因為這個方法是靜態方法呢* 我們是無法改變這個行為* 如果沒有被繼承或者重寫的* 直接使用VideoFactory是OK的* 剛剛我們對簡單工廠也分析了一下* 對于簡單工廠我們還可以通過反射* 來彌補簡單工廠的擴展性* 利用反射來演示一下簡單工廠* * * * * @param c* @return*/public Video getVideo(String type){/*** 不區分大小寫* 這里使用switch也是OK的* * 這個時候判斷他是一個Java視頻* */if("java".equalsIgnoreCase(type)){/*** 直接返回一個Java視頻* */return new JavaVideo();}else if("python".equalsIgnoreCase(type)){return new PythonVideo();}return null;}}
package com.learn.design.pattern.creational.simplefactory;/*** 對于Test這個類它是屬于應用層的* 我們也可以把它理解成調用類的一個client* 所以我們接下來講應用層代碼* 無論這個類是Test還是Client* 在我們這里面都是一樣的* 我們創建一個Java的視頻* new一個JavaVideo* 調用它的produce方法* 如果我們需要JAVA的視頻* 那現在就是一個父類的引用* 指向了子類的一個實現* 在我們這個客戶端類* Test類里面* 也就是說應用層類* 它是非常依賴JavaVideo這個類的* 例如我現在想生成PythonVideo* 這個類肯定是需要改的* 那現在我們在同一個包下* 所以不需要額外的import* 所以在import這個區域* 體現不出來的* 如果我們是跨包的* 或者是需要import的時候* 就可以看到是需要依賴的類的* 那我們能不能不讓我們的應用層依賴對應的類* 那我們能夠把生產過程放到一個類里面* 是我們的應用層不依賴于對應的具體實現類* 這個就是一個簡單工廠的一個由來* 現在我們就把創建具體的視頻過程移植到簡單工廠里面* 那我們現在來創建這個類* * 簡單工廠有沒有什么缺點* * * * @author Leon.Sun**/
public class Test {public static void main(String[] args) {/*** 現在就不直接new PathonVideo了* 我不需要直接依賴這個類* 那這里面也就不使用匿名對象了* 直接再new一個出來* * 這里面并沒有具體的PathonVideo還是JavaVideo* 如果導包的時候* 不需要導具體的* JavaVideo還是PythonVideo* 我們只導入一個VideoFactory就可以了* 現在他們都是同包的* 不需要import也能夠拿到它* 現在看一下最新的UML類圖* * 首先new了一個VideoFactory* 這個毋庸置疑* */
// VideoFactory videoFactory = new VideoFactory();/*** 通過videoFactory的getVideo方法* 里面傳一個JAVA* * 然后進入getVideo方法* * 這個時候注意Video的一個類型* 具體的指向的是JavaVideo的一個對象* 這個對象的具體類型是JavaVideo類型* 同時它也是Video類型* Video instanceof JavaVideo* 但是他不是PathonVideo類型* Video instanceof PathonVideo* 然后繼續* * */
// Video video = videoFactory.getVideo("java");/*** 如果為空就直接return* */
// if(video == null){
// return;
// }/*** 然后這里直接調用JavaVideo的produce方法* 這個就是簡單工廠* 非常容易理解* * */
// video.produce();VideoFactory videoFactory = new VideoFactory();/*** 這里要傳對應的class* 那我們只需要傳JavaVideo.class就可以了* 這樣有一個擴展性* 從一定程度上滿足開閉原則* 如果我們新增一個前端課程* FEVideo* 那只需要傳FEVideo的類就可以了* 而這個工廠是不需要變化的* 工廠我們不需要再修改他* 我們現在來debug一下* * */Video video = videoFactory.getVideo(JavaVideo.class);if(video == null){return;}/*** 直接執行produce方法* */video.produce();}}
?
總結
- 上一篇: 接口隔离原则原理讲解-coding
- 下一篇: 简单工厂 jdk源码解析