摘要:我認(rèn)為按書上的順序比較好理解因?yàn)楹唵戊o態(tài)工廠模式是在工廠方法模式上縮減,抽象工廠模式是在工廠方法模式上再增強(qiáng)。所以我就先講工廠方法模式了。抽象工廠模式抽象工廠模式就比較復(fù)雜了,我們一般的應(yīng)用都寫不到。
前言
只有光頭才能變強(qiáng)
回顧前面:
給女朋友講解什么是代理模式
包裝模式就是這么簡單啦
單例模式你會(huì)幾種寫法?
昨天寫了單例模式了,今天是時(shí)候?qū)懝S模式啦~
工廠模式我個(gè)人認(rèn)為其實(shí)比較難理解的,如果有接觸過|聽過|見過該模式的同學(xué)很可能就會(huì)想:我自己new一個(gè)對象出來就好了,簡單快捷。用得著你這個(gè)工廠模式嗎?搞一個(gè)工廠出來還要寫一大堆的代碼呢~
網(wǎng)上的很多資料都是在闡述著:工廠模式的好處就是解耦。相信大家對解耦這個(gè)詞也不陌生,那解耦究竟有什么好處呢?
本文章試圖去解釋為什么要用工廠模式,用了工廠模式的好處是什么,以及工廠模式衍生出的三種形式究竟有什么區(qū)別~~
那么接下來就開始吧,如果有錯(cuò)的地方希望能多多包涵,并不吝在評論區(qū)指正!
一、工廠模式概述在《設(shè)計(jì)模式之禪》這本書中分了兩章節(jié)講工廠模式:
工廠方法模式
(ps:其中里面講到了簡單工廠模式)
抽象工廠模式
網(wǎng)上的大部分資料都是將工廠模式分成三種:
簡單/靜態(tài)工廠模式
工廠方法模式
抽象工廠模式
看完上面的敘述是不是想打死我,什么鳥玩意?不急哈,下面我會(huì)一一講到~~
1.1為什么要用工廠模式?想想我們?yōu)槭裁匆霉S模式?下面我就簡單舉例子:
文件IO的操作我們會(huì)經(jīng)常用得到吧,所以BufferedReader對象經(jīng)常要?jiǎng)?chuàng)建的:
// 創(chuàng)建一個(gè)BufferedReader對象 BufferedReader bf = new BufferedReader(new FileReader(new File("aa.txt")));
你說麻煩嗎?其實(shí)也不麻煩,就一行代碼嘛,哪里麻煩了~如果不太熟悉IO流的同學(xué)就沒有那么機(jī)靈了,創(chuàng)建一個(gè)BufferedReader可能就是以下的代碼了:
File file = new File("aa.txt"); FileReader fileReader = new FileReader(file); BufferedReader bufferedReader = new BufferedReader(fileReader);
你說麻煩嗎?其實(shí)也不麻煩,不就是三行代碼嘛,哪里麻煩了~如果這個(gè)應(yīng)用很多的類上都用到了BufferedReader對象的話,那每個(gè)類都寫上這三行代碼了。那你說麻煩嗎?那肯定麻煩啊,還用想啊....
可以看出來,創(chuàng)建一個(gè)BufferReader對象里面需要一個(gè)FileReader對象,而FileReader對象又要File對象。那創(chuàng)建這個(gè)BufferReader對象還是比較麻煩的(代碼上看不麻煩,從構(gòu)造上看還是挺麻煩的)!
雖然比較麻煩,但我們還能用,能用就行!于是乎,我們就去寫代碼了,現(xiàn)在有三個(gè)類都要進(jìn)行文件的讀寫操作,于是他們就有這樣的代碼:
public class FileOperateA { public static void main(String[] args) throws FileNotFoundException { File file = new File("aa.txt"); FileReader fileReader = new FileReader(file); BufferedReader bufferedReader = new BufferedReader(fileReader); // 讀寫文件.... } }
此時(shí):上頭說,我要換成LineNumberReader來讀寫,有這個(gè)需求!那我們作為一個(gè)寫代碼的,能怎么辦?很絕望也需要去完成呀。
不熟悉IDE的小伙子就一個(gè)一個(gè)將BufferedReader改成LineNumberReader,現(xiàn)在就3個(gè)類用到了BufferedReader,也就改6次而已。(ps:那如果很多地方都用到了呢?)
熟悉IDE的小伙子就全局替換重構(gòu),妥妥的!
哎,寫個(gè)代碼屁事真多...那有沒有一種方法能夠讓創(chuàng)建對象變得簡單而且修改對象時(shí)能很方便呢?
哎,工廠模式就行了。
再說從面向?qū)ο蟮慕嵌葋砜?/strong>:我一個(gè)操作文件的類還要我會(huì)創(chuàng)建BufferReader是不是有點(diǎn)過分了?(職責(zé)沒有分工好)
交給工廠來創(chuàng)建對象這就很面向?qū)ο罅耍?/p> 1.2體驗(yàn)工廠模式
何為工廠?將我們的產(chǎn)品都交由工廠來生產(chǎn)!我現(xiàn)在用的iphone5s,從哪來?從富士康組裝而來,富士康是工廠。我用得著知道iphone5s在富士康是怎么組裝起來的嗎?不需要。
來,我們來改造一下上面的例子。首先我們創(chuàng)建一個(gè)工廠類,它可以生產(chǎn)Reader對象!
// 創(chuàng)建Reader對象的工廠 public class ReaderFactory { public static Reader getReader() throws FileNotFoundException { File file = new File("aa.txt"); FileReader fileReader = new FileReader(file); BufferedReader reader = new BufferedReader(fileReader); return reader; } }
那么我們要得到BufferReader對象就賊簡單了:
public class FileOperateA { public static void main(String[] args) throws FileNotFoundException { //-------我有工廠了,還用自己搞嗎?不用了! //File file = new File("aa.txt"); //FileReader fileReader = new FileReader(file); //BufferedReader bufferedReader = new BufferedReader(fileReader); //-------我有工廠了,還用自己搞嗎?不用了! // 用工廠來創(chuàng)建出對象 Reader reader = ReaderFactory.getReader(); // 讀寫文件.... } }
工廠將我們創(chuàng)建的對象過程給屏蔽了!
此時(shí)我要改成LineNumberReader怎么玩?在工廠上改一下就好了:
我們的調(diào)用方FileOperateA|FileOperateB|FileOperateC這些類完全就不用變!
1.3使用工廠方法的好處從上面的工廠模式體驗(yàn)我們就可以看到:
我們修改了具體的實(shí)現(xiàn)類,對客戶端(調(diào)用方)而言是完全不用修改的。
如果我們使用new的方式來創(chuàng)建對象的話,那么我們就說:new出來的這個(gè)對象和當(dāng)前客戶端(調(diào)用方)耦合了!
也就是,當(dāng)前客戶端(調(diào)用方)依賴著這個(gè)new出來的對象!
這就是解耦的好處!
我再放下我之前練習(xí)的時(shí)候?qū)戇^的代碼吧:
我有一個(gè)DaoFactory,邏輯很簡單就是專門創(chuàng)建Dao對象的~
那么在Service層就可以使用工廠將想要的Dao對象初始化了~
此時(shí)我們的Service與Dao的對象低耦合的~
大家可能看不出有什么好處,還弄了一大堆的字符串啥的~~
在Service與Controller層我也弄了一個(gè)ServiceFactory,根據(jù)當(dāng)時(shí)業(yè)務(wù)的需要(添加權(quán)限),我創(chuàng)建Service時(shí)就非常靈活了:
二、如何使用工廠模式在一開始我就說了,工廠模式可以分成三類:
簡單/靜態(tài)工廠模式
工廠方法模式
抽象工廠模式
下面我就逐一來介紹一下每一種工廠模式有什么不一樣~
三種模式都以:Java3y要買寵物的例子來講解~
2.1工廠方法模式很多博客都是以簡單/靜態(tài)工廠模式,工廠方法模式,抽象工廠模式這個(gè)順序來講解工廠模式的。我認(rèn)為按書上的順序比較好理解~因?yàn)楹唵?靜態(tài)工廠模式是在工廠方法模式上縮減,抽象工廠模式是在工廠方法模式上再增強(qiáng)。
所以我就先講工廠方法模式了。
Java3y每天寫代碼很無聊,想要買只寵物來陪陪自己。于是乎就去寵物店看寵物啦~~~
作為一間寵物店,號(hào)稱什么寵物都有!于是乎,店主宣傳的時(shí)候就說:我的寵物店什么寵物都有!
于是構(gòu)建寵物的工廠就誕生了~
// 號(hào)稱什么寵物都有 public interface AnimalFactory { // 可以獲取任何的寵物 Animal createAnimal(); }
當(dāng)然了,主流的寵物得進(jìn)貨一些先放在店里充充門面,一些特殊的寵物就告訴顧客要時(shí)間進(jìn)貨~
所以,我們就有了構(gòu)建貓和狗的工廠(繼承著所有寵物的工廠)
貓工廠:
// 繼承著寵物工廠 public class CatFactory implements AnimalFactory { @Override // 創(chuàng)建貓 public Animal createAnimal() { return new Cat(); } }
狗工廠也是一樣的:
// 繼承著寵物工廠 public class DogFactory implements AnimalFactory { // 創(chuàng)建狗 @Override public Animal createAnimal() { return new Dog(); } }
嗯,還有我們的實(shí)體類:貓、狗、動(dòng)物(多態(tài):貓和狗都是動(dòng)物,可以直接用動(dòng)物來表示了)
動(dòng)物實(shí)體類:
public abstract class Animal { // 所有的動(dòng)物都會(huì)吃東西 public abstract void eat(); }
貓實(shí)體類:
public class Cat extends Animal { // 貓喜歡吃魚 @Override public void eat() { System.out.println("貓吃魚"); } }
狗實(shí)體類:
public class Dog extends Animal { // 狗喜歡吃肉 @Override public void eat() { System.out.println("狗吃肉"); } }
那么現(xiàn)在Java3y想要一只狗,跟了寵物店老板說,寵物店老板就去找狗回來了:
// 去找狗工廠拿一只狗過來 AnimalFactory f = new DogFactory(); // 店主就拿到了一只狗給Java3y Animal a = f.createAnimal(); a.eat(); System.out.println("關(guān)注公眾號(hào):Java3y");
那么現(xiàn)在Java3y想要一只貓,跟了寵物店老板說,寵物店老板就去找貓回來了:
// 去找貓工廠拿一只貓過來 AnimalFactory ff = new CatFactory(); // 店主就拿到了一只貓給Java3y Animal aa = ff.createAnimal(); aa.eat(); System.out.println("關(guān)注公眾號(hào):Java3y");
如果這個(gè)時(shí)候Java3y說想要一只蜥蜴怎么辦???沒問題啊,店主搞個(gè)蜥蜴工廠就好了~~
// 要買蜥蜴.. AnimalFactory fff = new LizardFactory(); Animal aaa = ff.createAnimal(); aaa.eat();
優(yōu)點(diǎn):
1:客戶端不需要在負(fù)責(zé)對象的創(chuàng)建,明確了各個(gè)類的職責(zé)
2:如果有新的對象增加,只需要增加一個(gè)具體的類和具體的工廠類即可
3:不會(huì)影響已有的代碼,后期維護(hù)容易,增強(qiáng)系統(tǒng)的擴(kuò)展性
缺點(diǎn):
1:需要額外的編寫代碼,增加了工作量
工廠方法類圖:
2.2簡單/靜態(tài)工廠模式現(xiàn)在寵物店生意不好做啊,號(hào)稱“什么寵物都有",這吹過頭了~~于是店主只賣兩種常見的寵物了。
既然就只有兩種寵物的話,那就沒必要有”貓廠“、”狗廠“了,一個(gè)貓狗廠就行了!
所以我們的工廠是這樣子的:
public class AnimalFactory { public static Dog createDog() { return new Dog(); } public static Cat createCat() { return new Cat(); } // 外界想要貓要狗,這里創(chuàng)建就好了 public static Animal createAnimal(String type) { if ("dog".equals(type)) { return new Dog(); } else if ("cat".equals(type)) { return new Cat(); } else { return null; } } }
三個(gè)實(shí)體還是沒變(動(dòng)物、貓、狗)....
那么Java3y去寵物店買貓狗的時(shí)候,告訴老板我要貓、我要狗:
// 拿到狗 Animal A = AnimalFactory.createAnimal("dog"); A.eat(); // 拿到貓 Animal C = AnimalFactory.createAnimal("cat"); C.eat();
現(xiàn)在問題來了:
1:我想要一個(gè)豬,可是我的工廠類沒有豬
2:我就去改代碼,寫可以創(chuàng)建豬對象的
3:接著,我又要其他的動(dòng)物
4:我還是得改代碼
5...................
6:這就是簡單工廠類的缺點(diǎn):當(dāng)需求改變了,我就要改代碼.
簡單工廠類的優(yōu)點(diǎn)也很明顯:我就一個(gè)具體的工廠來創(chuàng)建對象,代碼量少。
2.3抽象工廠模式抽象工廠模式就比較復(fù)雜了,我們一般的應(yīng)用都寫不到。我首先來簡述一下需求吧:
現(xiàn)在非常流行在貓狗屆也吹起了一股“性別風(fēng)”
有的喜歡公的
有的喜歡母的
那我們的貓和狗都是有性別的,不是公的就是母的~~
我們之前在工廠方法模式下是每個(gè)動(dòng)物都開一個(gè)工廠,如果動(dòng)物過多的話,那么就有很多的工廠~
那現(xiàn)在我們可以抽取出來:每個(gè)動(dòng)物不是公的就是母的~
所以我們有兩個(gè)工廠就足夠了!
具體的代碼是這樣的:
我們的最大工廠還是定義了創(chuàng)建什么動(dòng)物
public interface AnimalFactory { Animal createDog(); Animal createCat(); }
創(chuàng)建母貓和母狗的工廠:
public class FemaleAnimalFactory implements AnimalFactory { // 生產(chǎn)母狗和母貓 @Override public Animal createDog() { return new FemaleDog(); } @Override public Animal createCat() { return new FemaleCat(); } }
創(chuàng)建公貓和公狗的工廠:
public class MaleAnimalFactory implements AnimalFactory { // 生產(chǎn)公狗和公貓 @Override public Animal createDog() { return new MaleDog(); } @Override public Animal createCat() { return new MaleCat(); } }
這是所有動(dòng)物都擁有的普遍行為:
public abstract class Animal { // 所有的動(dòng)物都會(huì)吃東西 public abstract void eat(); // 所有的動(dòng)物都有性別 public abstract void gender(); }
這是貓都擁有的普遍行為:
public abstract class Cat extends Animal { // 貓喜歡吃魚 @Override public void eat() { System.out.println("貓吃魚"); } }
這是狗都擁有的普遍行為:
public abstract class Dog extends Animal { // 狗喜歡吃肉 @Override public void eat() { System.out.println("狗吃肉"); } }
貓分為公貓、母貓。狗分為公狗和母狗:
public class FemaleCat extends Cat { public void gender() { System.out.println("I am a female Cat"); } }
.....
簡單來說:工廠方法模式的工廠是創(chuàng)建出一種產(chǎn)品,而抽象工廠是創(chuàng)建出一類產(chǎn)品。
一類的產(chǎn)品我們稱之為產(chǎn)品族。
貓是一類的,狗也是一類的。所以AnimalFactory定義了兩類產(chǎn)品--->Animal createDog();和Animal createCat();
產(chǎn)品的繼承結(jié)構(gòu)稱之為產(chǎn)品等級。
所有的動(dòng)物都是會(huì)吃東西的,它們都是有性別的,這是最普遍的。所以Animal定義了兩個(gè)抽象方法:public abstract void eat();和public abstract void gender();
所有的狗都是會(huì)吃肉的,所以Dog實(shí)現(xiàn)了eat()方法
- 狗又分成了公狗和母狗,所以定義了兩個(gè)類FemaleDog和MaleDog繼承了Dog,實(shí)現(xiàn)了`gender()`方法
所有的貓都是會(huì)吃魚的,所以Cat實(shí)現(xiàn)了eat()方法
- 貓又分成了公貓和母貓,所以定義了兩個(gè)類FemaleCat和MaleCat繼承了Cat,實(shí)現(xiàn)了`gender()`方法
具體的工廠是面向多個(gè)產(chǎn)品等級結(jié)構(gòu)進(jìn)行生產(chǎn)。
所以FemaleAnimalFactory定義了createDog()和createCat()生產(chǎn)母狗和母貓
所以MaleAnimalFactory定義了createDog()和createCat()生產(chǎn)公狗和共貓
找到母工廠就可以創(chuàng)建母貓和母狗,找到公工廠就可以創(chuàng)建公貓和公狗
public static void main(String[] args) { // 需要性別為母的就去找母工廠 AnimalFactory af = new FemaleAnimalFactory(); // 需要一只母貓 af.createCat().gender(); // 需要一只母狗 af.createDog().gender(); System.out.println("-------------關(guān)注公眾號(hào):Java3y-------------------------"); // 需要性別為公的就去找公工廠 AnimalFactory aff = new MaleAnimalFactory(); // 需要一只公狗 aff.createDog().gender(); // 需要一只公貓 aff.createCat().gender(); }
效果:
這是抽象工廠模式的類圖:
抽象工廠模式說到底就是多了一層抽象,減少了工廠的數(shù)量。
抽象工廠缺點(diǎn)也很明顯:
難以擴(kuò)展產(chǎn)品族--->如果我再要寵物豬的話
那我要修改AnimalFactory、FemaleAnimalFactory、MaleAnimalFactory這些類了~
三、總結(jié)總的來說我們用簡單工廠模式比較多,工廠方式模式的話代碼量會(huì)比較大,抽象工廠模式的話需要業(yè)務(wù)比較大的情況下才會(huì)用到(如果有更好的理解方式不妨在評論區(qū)留言,一起交流交流漲漲見識(shí)~~)
工廠模式配合反射來使用也是極好的~
參考資料:
《設(shè)計(jì)模式之禪》
https://wangjingxin.top/2016/10/27/abstract/--【原創(chuàng)】設(shè)計(jì)模式系列(九)——抽象工廠模式
https://www.zhihu.com/question/24843188?sort=created--工廠設(shè)計(jì)模式有什么用?
https://blog.csdn.net/lemon_tree12138/article/details/46225213--Java設(shè)計(jì)模式——工廠模式
https://www.cnblogs.com/toutou/p/4899388.html--詳解設(shè)計(jì)模式之工廠模式(簡單工廠+工廠方法+抽象工廠)
http://www.cnblogs.com/poissonnotes/archive/2010/12/01/1893871.html--工廠模式
如果文章有錯(cuò)的地方歡迎指正,大家互相交流。習(xí)慣在微信看技術(shù)文章,想要獲取更多的Java資源的同學(xué),可以關(guān)注微信公眾號(hào):Java3y。為了大家方便,剛新建了一下qq群:742919422,大家也可以去交流交流。謝謝支持了!希望能多介紹給其他有需要的朋友
文章的目錄導(dǎo)航:
https://zhongfucheng.bitcron.com/post/shou-ji/wen-zhang-dao-hang
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/69469.html
摘要:設(shè)計(jì)模式工廠模式最近閱讀了幾本設(shè)計(jì)模式方面的書籍學(xué)習(xí)之余整理下來方便以后的歸納和梳理設(shè)計(jì)模式工廠模式創(chuàng)造工廠模式是一種創(chuàng)建性模式也就是一種創(chuàng)建對象的最佳實(shí)踐首先我們需要理解為什么我們需要工廠模式想象一個(gè)場景如果你要求去買一些東西板燒雞腿 Javascript設(shè)計(jì)模式-工廠模式 最近閱讀了幾本設(shè)計(jì)模式方面的書籍,學(xué)習(xí)之余整理下來,方便以后的歸納和梳理 設(shè)計(jì)模式-工廠模式 創(chuàng)造工廠模式是一...
摘要:基于工廠角色和產(chǎn)品角色的多態(tài)性設(shè)計(jì)是工廠方法模式的關(guān)鍵。工廠方法模式之所以又被稱為多態(tài)工廠模式,是因?yàn)樗械木唧w工廠類都具有同一抽象父類。工廠方法模式總結(jié)工廠方法模式是簡單工廠模式的進(jìn)一步抽象和推廣。 JavaScript工廠模式 首先需要說一下工廠模式。工廠模式根據(jù)抽象程度的不同分為三種 簡單工廠模式 工廠方法模式 抽象工廠模式 1.簡單工廠模式 簡單工廠模式:又稱為靜態(tài)工廠方法...
摘要:本文一大半內(nèi)容都是通過舉例來讓讀者去理解什么是控制反轉(zhuǎn)和依賴注入,通過理解這些概念,來更加深入。這種由外部負(fù)責(zé)其依賴需求的行為,我們可以稱其為控制反轉(zhuǎn)。工廠模式,依賴轉(zhuǎn)移當(dāng)然,實(shí)現(xiàn)控制反轉(zhuǎn)的方法有幾種。 容器,字面上理解就是裝東西的東西。常見的變量、對象屬性等都可以算是容器。一個(gè)容器能夠裝什么,全部取決于你對該容器的定義。當(dāng)然,有這樣一種容器,它存放的不是文本、數(shù)值,而是對象、對象的描...
摘要:抽象工廠模式可以向客戶端提供一個(gè)接口,使客戶端在不必指定產(chǎn)品的具體的情況下,創(chuàng)建多個(gè)產(chǎn)品族中的產(chǎn)品對象。前邊的兩個(gè)都是直接生產(chǎn)實(shí)例的,開始以為抽象工廠也是但是后來發(fā)現(xiàn)抽象工廠更像是生產(chǎn)工廠,其實(shí)抽象工廠其實(shí)是實(shí)現(xiàn)子類繼承父類的方法。 創(chuàng)建型模式 創(chuàng)建型模式是對一個(gè)類的實(shí)例化過程進(jìn)行了抽象,把對象的創(chuàng)建和對象的使用進(jìn)行了分離 上一篇介紹了下單例模式,這一篇介紹一下工廠模式和抽象工廠模式,...
摘要:在標(biāo)準(zhǔn)的種設(shè)計(jì)模式中,與工廠相關(guān)的模式有種工廠方法模式和抽象工廠模式。在這里,要區(qū)分清楚的是工廠方法模式強(qiáng)調(diào)的是方法,而抽象工廠模式強(qiáng)調(diào)的是工廠,這是兩個(gè)相關(guān)但又不相同的概念,就像做飯和廚房的區(qū)別,一個(gè)是談動(dòng)作,一個(gè)是談空間。 在標(biāo)準(zhǔn)的23種設(shè)計(jì)模式中,與工廠相關(guān)的模式有2種:工廠方法模式(Factory method pattern)和抽象工廠模式(Abstract factory ...
閱讀 3600·2021-11-15 11:36
閱讀 1091·2021-11-11 16:55
閱讀 739·2021-10-20 13:47
閱讀 3058·2021-09-29 09:35
閱讀 3521·2021-09-08 10:45
閱讀 2576·2019-08-30 15:44
閱讀 878·2019-08-30 11:10
閱讀 1456·2019-08-29 13:43