Java設(shè)計(jì)模式之簡(jiǎn)單工廠 工廠方法 抽象工廠深度總結(jié)
工廠模式介紹
工廠模式也是非常常見(jiàn)的設(shè)計(jì)模式之一,其屬于創(chuàng)建型模式。工廠模式分類:簡(jiǎn)單工廠(Simple Factory)、工廠方法(Factory Method)、抽象工廠(Abstract Factory),嚴(yán)格來(lái)講,簡(jiǎn)單工廠不屬于工廠設(shè)計(jì)模式。
好處
- 解耦:工廠模式主要是為了對(duì)象的創(chuàng)建和使用的分離
- 降低代碼重復(fù):如果對(duì)象創(chuàng)建很復(fù)雜,則每次創(chuàng)建都需要重復(fù)很多代碼,通過(guò)工廠包裝起來(lái)可以減少重復(fù)代碼
- 方便維護(hù):如果創(chuàng)建對(duì)象的邏輯有修改,則只需要修改工廠內(nèi)代碼,而不用修改每個(gè)創(chuàng)建對(duì)象的代碼。
常見(jiàn)的應(yīng)用
- Spring中的BeanFactory.getBean(beanName)
- 日志當(dāng)中的LoggerFactory.getLogger(name)
- Java加密時(shí) KeyGenerator keygen=KeyGenerator.getInstance("AES");
- RabbitMQ的Java客戶端創(chuàng)建連接 :
//創(chuàng)建連接工廠 ConnectionFactory factory = new ConnectionFactory("192.168.74.4"); // 通過(guò)連接工廠獲取連接 Connection connection = factory.newConnection();
簡(jiǎn)單工廠(Simple Factory)
簡(jiǎn)單工廠并不在23種設(shè)計(jì)模式之中,屬于特殊的工廠模式,應(yīng)用的相對(duì)來(lái)說(shuō)少一點(diǎn)。客戶端實(shí)例化對(duì)象的時(shí)候不用通過(guò)new Audi()的方式,而是可以通過(guò)往統(tǒng)一工廠傳入相應(yīng)的條件返回對(duì)應(yīng)的實(shí)例對(duì)象(主要通過(guò)if-else或者switch-case進(jìn)行判斷,違背了開(kāi)閉原則),屏蔽了實(shí)例化對(duì)象的具體邏輯細(xì)節(jié)。
適用場(chǎng)景
- 需要?jiǎng)?chuàng)建的對(duì)象較少。
- 客戶端不關(guān)心對(duì)象的創(chuàng)建過(guò)程。
角色分配:
- 工廠(Factory)角色:簡(jiǎn)單工廠模式的核心,它負(fù)責(zé)實(shí)現(xiàn)創(chuàng)建所有實(shí)例的內(nèi)部邏輯。工廠類可以被外界直接調(diào)用,創(chuàng)建所需的產(chǎn)品對(duì)象。
- 抽象產(chǎn)品(Product)角色 :簡(jiǎn)單工廠模式所創(chuàng)建的所有對(duì)象的父類,它負(fù)責(zé)描述所有實(shí)例所共有的公共接口。
- 具體產(chǎn)品(Concrete Product)角色:簡(jiǎn)單工廠模式的創(chuàng)建目標(biāo),所有創(chuàng)建的對(duì)象都是充當(dāng)這個(gè)角色的某個(gè)具體類的實(shí)例。
其UML類圖如下所示:
應(yīng)用案例:
- Java加密時(shí) KeyGenerator keygen=KeyGenerator.getInstance("AES");
- DateFormat類中的public final static DateFormat getDateInstance(int style)
優(yōu)缺點(diǎn):
- 優(yōu)點(diǎn):實(shí)現(xiàn)簡(jiǎn)單,隱藏了創(chuàng)建對(duì)象的細(xì)節(jié),創(chuàng)建對(duì)象的邏輯修改時(shí),客戶端不用進(jìn)行修改。
- 缺點(diǎn):違背了開(kāi)閉原則,每次新增刪除子類的時(shí)候都要修改工廠的邏輯,而且創(chuàng)建對(duì)象的邏輯都包含在工廠內(nèi),后面子類太多的話這塊代碼會(huì)非常多,難以維護(hù)。
簡(jiǎn)單工廠實(shí)現(xiàn):
我們創(chuàng)建一個(gè)Car接口,里面包含一個(gè)getName方法用以返回具體的品牌名稱,接口有兩個(gè)實(shí)現(xiàn)類Audi和Bmw,分別返回了品牌名稱 "Audi" 和 "Bmw"。
package com.wkp.designpattern.factory; //汽車接口 public interface Car { //返回汽車的品牌 public String getName(); }
package com.wkp.designpattern.factory; public class Audi implements Car { public String getName() { return "Audi"; } }
package com.wkp.designpattern.factory; public class Bmw implements Car { public String getName() { return "Bmw"; } }
下面是簡(jiǎn)單工廠的核心,用于創(chuàng)建對(duì)象
package com.wkp.designpattern.simple.factory; import com.wkp.designpattern.factory.Audi; import com.wkp.designpattern.factory.Bmw; import com.wkp.designpattern.factory.Car; public class SimpleFactory { public Car getCar(String name){ if("Audi".equals(name)){ return new Audi(); }else if("Bmw".equals(name)){ return new Bmw(); }else{ System.out.println("沒(méi)法生產(chǎn)這個(gè)車"); return null; } } }
測(cè)試代碼如下:
package com.wkp.designpattern.simple.factory; import com.wkp.designpattern.factory.Car; public class SimpleFactoryTest { public static void main(String[] args) { SimpleFactory factory = new SimpleFactory(); Car car1 = factory.getCar("Audi"); System.out.println(car1.getName()); Car car2 = factory.getCar("Bmw"); System.out.println(car2.getName()); } }
輸出結(jié)果如下:
Audi
Bmw
這里每增加一個(gè)新的子類,getCar方法就要添加if判斷,刪除了子類這里也要修改,顯然違反了開(kāi)閉原則,而且創(chuàng)建對(duì)象的邏輯全部都在這個(gè)方法中,隨著創(chuàng)建對(duì)象的增加,這里的邏輯會(huì)非常多。當(dāng)然我們可以通過(guò)反射的方式對(duì)上面的工廠進(jìn)行改進(jìn)如下:
//利用反射改進(jìn)的簡(jiǎn)單工廠,添加的時(shí)候不用修改getCar方法 public class ReflectSimpleFactory { //參數(shù)className為完整類名 public Car getCar(String className){ Car obj=null; try { obj=(Car) Class.forName(className).newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } return obj; } }
public class ReflectSimpleFactoryTest { public static void main(String[] args) { ReflectSimpleFactory factory = new ReflectSimpleFactory(); Car car1 = factory.getCar("com.wkp.designpattern.factory.Audi"); System.out.println(car1.getName()); Car car2 = factory.getCar("com.wkp.designpattern.factory.Bmw"); System.out.println(car2.getName()); } }
輸出結(jié)果不變,也符合了開(kāi)閉原則,但是要傳入完整類名也不方便,可以通過(guò)xml或者配置文件的方式進(jìn)行改進(jìn)。
工廠方法(Factory Method)
工廠方法的應(yīng)用是最多的,工廠方法中不再提供統(tǒng)一的工廠創(chuàng)建對(duì)象,而是針對(duì)不同的對(duì)象提供不同的工廠。定義一個(gè)創(chuàng)建對(duì)象的接口,讓其子類自己決定實(shí)例化哪一個(gè)工廠類,創(chuàng)建過(guò)程延遲到子類進(jìn)行。
適用場(chǎng)景
- 當(dāng)一個(gè)類不知道它所必須創(chuàng)建的對(duì)象的類的時(shí)候:工廠方法模式中客戶端不需要知道對(duì)象的名稱,只需要知道要?jiǎng)?chuàng)建的對(duì)象對(duì)應(yīng)的工廠即可。
- 當(dāng)一個(gè)類希望由它的子類來(lái)指定它所創(chuàng)建的對(duì)象的時(shí)候:工廠方法模式中定義了一個(gè)Factory接口,該接口包含一個(gè)創(chuàng)建對(duì)象的抽象方法,具體的創(chuàng)建對(duì)象的邏輯由其實(shí)現(xiàn)類完成。
- 當(dāng)類將創(chuàng)建對(duì)象的職責(zé)委托給多個(gè)幫助子類中的某一個(gè),并且你希望將哪一個(gè)幫助子類是代理者這一信息局部化的時(shí)候。
角色分配:
- 抽象工廠(Abstract Factory)角色:是工廠方法模式的核心,提供了創(chuàng)建對(duì)象的接口,任何在模式中創(chuàng)建的對(duì)象的工廠類必須實(shí)現(xiàn)這個(gè)接口。
- 具體工廠(Concrete Factory)角色:這是實(shí)現(xiàn)抽象工廠接口的具體工廠類,包含與應(yīng)用程序密切相關(guān)的邏輯,并且受到應(yīng)用程序調(diào)用以創(chuàng)建某一種產(chǎn)品對(duì)象。
- 抽象產(chǎn)品(AbstractProduct)角色 :工廠方法模式所創(chuàng)建的對(duì)象的超類型,也就是產(chǎn)品對(duì)象的共同父類或共同擁有的接口。
- 具體產(chǎn)品(Concrete Product)角色 :這個(gè)角色實(shí)現(xiàn)了抽象產(chǎn)品角色所定義的接口。某具體產(chǎn)品有專門的具體工廠創(chuàng)建,它們之間往往一一對(duì)應(yīng)
其UML類圖如下所示:
應(yīng)用案例:
- spring-data-redis中創(chuàng)建redis連接的地方,RedisConnectionFactory接口提供了創(chuàng)建連接的方法RedisConnection getConnection(),該工廠的兩個(gè)實(shí)現(xiàn)類JedisConnectionFactory ,LettuceConnectionFactory分別用于創(chuàng)建jedis和lettuce連接
優(yōu)缺點(diǎn):
- 優(yōu)點(diǎn):符合開(kāi)閉原則,添加子類時(shí)只需要添加對(duì)應(yīng)的工廠類即可,而不用修改原有的工廠類,每個(gè)對(duì)象的創(chuàng)建邏輯都在對(duì)應(yīng)的工廠類中,代碼邏輯清晰,易于維護(hù)。
- 缺點(diǎn):每次增加一個(gè)產(chǎn)品時(shí),都需要增加一個(gè)具體類和對(duì)象實(shí)現(xiàn)工廠,使得系統(tǒng)中類的個(gè)數(shù)成倍增加,在一定程度上增加了系統(tǒng)的復(fù)雜度,同時(shí)也增加了系統(tǒng)具體類的依賴。
工廠方法實(shí)現(xiàn):
定義一個(gè)Factory接口,里面提供了一個(gè)getCar()方法,具體的創(chuàng)建邏輯由其實(shí)現(xiàn)類去完成。
public interface Factory { public Car getCar(); }
下面是Factory接口的具體實(shí)現(xiàn)類,用于創(chuàng)建對(duì)應(yīng)的對(duì)象
public class AudiFactory implements Factory { public Car getCar() { return new Audi(); } }
public class BmwFactory implements Factory { public Car getCar() { return new Bmw(); } }
測(cè)試類如下:
public class FuncFactoryTest { public static void main(String[] args) { Car car1 = new AudiFactory().getCar(); System.out.println(car1.getName()); Car car2 = new BmwFactory().getCar(); System.out.println(car2.getName()); } }
抽象工廠(Abstract Factory)
上面的工廠模式生產(chǎn)的都是一類產(chǎn)品,而抽象工廠模式可以生產(chǎn)產(chǎn)品族。什么是產(chǎn)品族呢?其實(shí)就是一組具有關(guān)聯(lián)關(guān)系的產(chǎn)品集合,舉幾個(gè)例子:
- 比如生產(chǎn)電腦,用Intel系列、AMD系列的零件(CPU、主板。。。。。),每個(gè)系列的零件就是一個(gè)產(chǎn)品族。
- 比如我們開(kāi)發(fā)過(guò)程中會(huì)用到MySQL、Oracle數(shù)據(jù)庫(kù),而不同的數(shù)據(jù)庫(kù)的操作會(huì)有不同,比如有User,Order兩個(gè)類,兩個(gè)類都有添加、修改操作,那User、Order的操作就要隨著數(shù)據(jù)庫(kù)的切換而切換,MySQL和Oracle下不同的類就組成了兩個(gè)產(chǎn)品族。
- 我們用QQ空間的時(shí)候會(huì)有換皮膚的功能,而每套皮膚下的背景、按鈕、導(dǎo)航、菜單。。。。。。這些也構(gòu)成了產(chǎn)品族
- 我們上面用的生產(chǎn)汽車的例子,比如奧迪,寶馬不同品牌車的零部件(輪胎、發(fā)動(dòng)機(jī)、軸承。。。。。。)
適用場(chǎng)景
- 和工廠方法一樣客戶端不需要知道它所創(chuàng)建的對(duì)象的類。
- 需要一組對(duì)象共同完成某種功能時(shí),并且可能存在多組對(duì)象完成不同功能的情況。(同屬于同一個(gè)產(chǎn)品族的產(chǎn)品)
- 系統(tǒng)結(jié)構(gòu)穩(wěn)定,不會(huì)頻繁的增加對(duì)象。(因?yàn)橐坏┰黾泳托枰薷脑写a,不符合開(kāi)閉原則)
角色分配
- 抽象工廠(AbstractFactory)角色 :是工廠方法模式的核心,與應(yīng)用程序無(wú)關(guān)。任何在模式中創(chuàng)建的對(duì)象的工廠類必須實(shí)現(xiàn)這個(gè)接口。
- 具體工廠類(ConcreteFactory)角色 :這是實(shí)現(xiàn)抽象工廠接口的具體工廠類,包含與應(yīng)用程序密切相關(guān)的邏輯,并且受到應(yīng)用程序調(diào)用以創(chuàng)建某一種產(chǎn)品對(duì)象。
- 抽象產(chǎn)品(Abstract Product)角色 :工廠方法模式所創(chuàng)建的對(duì)象的超類型,也就是產(chǎn)品對(duì)象的共同父類或共同擁有的接口。
- 具體產(chǎn)品(Concrete Product)角色 :抽象工廠模式所創(chuàng)建的任何產(chǎn)品對(duì)象都是某一個(gè)具體產(chǎn)品類的實(shí)例。在抽象工廠中創(chuàng)建的產(chǎn)品屬于同一產(chǎn)品族,這不同于工廠模式中的工廠只創(chuàng)建單一產(chǎn)品。
其UML類圖如下所示:
應(yīng)用案例:
QQ空間換膚、更換數(shù)據(jù)庫(kù)等
優(yōu)缺點(diǎn):
- 優(yōu)點(diǎn):可以創(chuàng)建系列產(chǎn)品,方便切換使用的產(chǎn)品系列。
- 缺點(diǎn):擴(kuò)展產(chǎn)品族較為麻煩,不光要添加所有的抽象產(chǎn)品實(shí)現(xiàn),還要添加產(chǎn)品族對(duì)應(yīng)的工廠;另外添加產(chǎn)品也不簡(jiǎn)單,要添加抽象產(chǎn)品,所有的產(chǎn)品族實(shí)現(xiàn),還要對(duì)原先的工廠實(shí)現(xiàn)添加工廠方法以生產(chǎn)新產(chǎn)品。
抽象工廠實(shí)現(xiàn)
這個(gè)案例我們就以生產(chǎn)電腦為例,眾所周知目前電腦的CPU有兩大品牌:Intel和AMD。我們就以此為例,如果電腦選用Intel系列,就要用Intel的CPU和主板等,如果用AMD系列,就用AMD系列的零部件。
下面的兩個(gè)接口就是我們上面提到的抽象產(chǎn)品角色。
//CPU public interface CPU { public String getName(); }
//主板 public interface MainBoard { public String getName(); }
下面的四個(gè)類就是我們上面提到的具體產(chǎn)品角色。首先是Intel系列產(chǎn)品
public class IntelCPU implements CPU { public String getName() { return "IntelCPU"; } }
public class IntelMainBoard implements MainBoard{ public String getName() { return "IntelMainBoard"; } }
然后是AMD系列產(chǎn)品:
public class AMDCPU implements CPU { public String getName() { return "AMDCPU"; } }
public class AMDMainBoard implements MainBoard{ public String getName() { return "AMDMainBoard"; } }
下面的Factory就是抽象工廠角色,提供了生產(chǎn)CPU和主板的抽象方法。
public interface Factory { //生產(chǎn)CPU public CPU createCPU(); //生產(chǎn)主板 public MainBoard createMainBoard(); }
然后是具體的工廠類角色,分別生產(chǎn)不同系列的產(chǎn)品。
//Intel系列產(chǎn)品工廠 public class IntelFactory implements Factory { public CPU createCPU() { return new IntelCPU(); } public MainBoard createMainBoard() { return new IntelMainBoard(); } }
//AMD系列產(chǎn)品工廠 public class AMDFactory implements Factory { public CPU createCPU() { return new AMDCPU(); } public MainBoard createMainBoard() { return new AMDMainBoard(); } }
測(cè)試類如下:
public class FactoryTest { public static void main(String[] args) { Factory intel = new IntelFactory(); System.out.println(intel.createCPU().getName()); System.out.println(intel.createMainBoard().getName()); Factory amd = new AMDFactory(); System.out.println(amd.createCPU().getName()); System.out.println(amd.createMainBoard().getName()); } }
運(yùn)行結(jié)果為:
IntelCPU
IntelMainBoard
AMDCPU
AMDMainBoard
抽象工廠終極改進(jìn)(反射+配置文件+簡(jiǎn)單工廠)
上面也說(shuō)過(guò)抽象工廠的缺點(diǎn)是擴(kuò)展產(chǎn)品族比較麻煩,我們對(duì)上面的抽象工廠做個(gè)改進(jìn),使其在添加產(chǎn)品族的時(shí)候更簡(jiǎn)單一些。我們引入簡(jiǎn)單工廠,但是簡(jiǎn)單工廠擴(kuò)展的時(shí)候要添加判斷條件,所以我們可以通過(guò)反射+配置文件去解決這個(gè)問(wèn)題。改動(dòng)后的UML圖如下所示:
改進(jìn)后的代碼如下所示:Configuration類用于讀取配置文件(沒(méi)有具體去實(shí)現(xiàn))
type=Intel packageName=com.wkp.design.pattern.factory.abst
public class SimpleFactory { static class Configuration{ public static String get(String key){ String value="";//TODO 讀取配置文件得到value return value; } } //通過(guò)配置文件讀取產(chǎn)品族類型及包名 private static final String type=Configuration.get("type"); private static final String packageName=Configuration.get("packageName"); //生產(chǎn)CPU public CPU createCPU() throws Exception{ return (CPU)Class.forName(packageName+"."+type+"CPU").newInstance(); } //生產(chǎn)主板 public MainBoard createMainBoard() throws Exception{ return (MainBoard)Class.forName(packageName+"."+type+"MainBoard").newInstance(); } }
測(cè)試代碼如下
public class SimpleFactoryTest { public static void main(String[] args) throws Exception { SimpleFactory factory = new SimpleFactory(); System.out.println(factory.createCPU().getName()); System.out.println(factory.createMainBoard().getName()); } }
我們看到,在調(diào)用的時(shí)候客戶端完全不用管用的是Intel還是AMD,這樣如果想切換產(chǎn)品族的話,只需要修改配置文件即可,非常的方便。
到此這篇關(guān)于Java設(shè)計(jì)模式之簡(jiǎn)單工廠 工廠方法 抽象工廠深度總結(jié)的文章就介紹到這了,更多相關(guān)Java 設(shè)計(jì)模式內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Java創(chuàng)建型設(shè)計(jì)模式之抽象工廠模式(Abstract?Factory)
- Java設(shè)計(jì)模式之抽象工廠模式淺析講解
- Java設(shè)計(jì)模式之抽象工廠模式(Abstract?Factory)
- Java?深入理解創(chuàng)建型設(shè)計(jì)模式之抽象工廠模式
- 深入理解Java設(shè)計(jì)模式之抽象工廠模式
- Java設(shè)計(jì)模式之抽象工廠模式詳解
- Java設(shè)計(jì)模式之工廠模式分析【簡(jiǎn)單工廠、工廠方法、抽象工廠】
- Java設(shè)計(jì)模式之抽象工廠模式
- Java設(shè)計(jì)模式筆記之抽象工廠代碼示例
- Java設(shè)計(jì)模式之抽象工廠模式實(shí)例詳解
- Java設(shè)計(jì)模式編程中簡(jiǎn)單工廠與抽象工廠模式的使用實(shí)例
- Java設(shè)計(jì)模式編程中的工廠方法模式和抽象工廠模式
- Java設(shè)計(jì)模式之工廠方法和抽象工廠
相關(guān)文章
使用java數(shù)組 封裝自己的數(shù)組操作示例
這篇文章主要介紹了使用java數(shù)組 封裝自己的數(shù)組操作,結(jié)合實(shí)例形式分析了java數(shù)組索引、遍歷等相關(guān)封裝操作技巧與注意事項(xiàng),需要的朋友可以參考下2020-03-03mybatisplus?@Select注解中拼寫動(dòng)態(tài)sql異常問(wèn)題的解決
這篇文章主要介紹了mybatisplus?@Select注解中拼寫動(dòng)態(tài)sql異常問(wèn)題的解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12新手場(chǎng)景Java線程相關(guān)問(wèn)題及解決方案
這篇文章主要介紹了新手場(chǎng)景Java線程相關(guān)問(wèn)題及解決方案,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07java必學(xué)必會(huì)之this關(guān)鍵字
java必學(xué)必會(huì)之this關(guān)鍵字,java中this的用法進(jìn)行了詳細(xì)的分析介紹,感興趣的小伙伴們可以參考一下2015-12-12java根據(jù)網(wǎng)絡(luò)地址保存圖片的方法
這篇文章主要為大家詳細(xì)介紹了java根據(jù)網(wǎng)絡(luò)地址保存圖片的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-07-07java9新特性Collection集合類的增強(qiáng)與優(yōu)化方法示例
這篇文章主要為大家介紹了java9新特性Collection集合類的增強(qiáng)與優(yōu)化方法示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2022-03-03Java實(shí)現(xiàn)對(duì)比兩個(gè)實(shí)體類字段變化的示例詳解
我們?cè)诠ぷ髦?可能會(huì)在日志中記錄數(shù)據(jù)的變化情況或者在公共處理的數(shù)據(jù)增加一個(gè)日志頁(yè)面,我們可以根據(jù)CompareUtils工具類比較數(shù)據(jù)前后發(fā)生了怎樣的變化,下面我們就來(lái)看看具體實(shí)現(xiàn)吧2024-03-03