设计模式(五)--工厂模式汇总
LZ想把簡單工廠模式、工廠方法模式和抽象工廠模式整理到一篇博文當(dāng)中,由淺入深,應(yīng)該能方便理解和記憶,話不多說,進入正題。
一、簡單工廠模式
定義:從設(shè)計模式的類型上來說,簡單工廠模式是屬于創(chuàng)建型模式,又叫做靜態(tài)工廠方法(Static Factory Method)模式,但不屬于23種GOF設(shè)計模式之一。簡單工廠模式是由一個工廠對象決定創(chuàng)建出哪一種產(chǎn)品類的實例。簡單工廠模式是工廠模式家族中最簡單實用的模式,可以理解為是不同工廠模式的一個特殊實現(xiàn)。
總結(jié)成一句話就是,由一個工廠對象決定創(chuàng)建出哪一種產(chǎn)品類的實例。下面是百度百科中簡單工廠模式的類圖。
可以看出,上面總共有三種類,一個是工廠類Creator,一個是產(chǎn)品接口IProduct,一個是具體的產(chǎn)品類,例如產(chǎn)品A和產(chǎn)品B,這之中,工廠類負(fù)責(zé)整個創(chuàng)建產(chǎn)品的邏輯判斷,所以為了使工廠類能夠知道我們需要哪一種產(chǎn)品,我們需要在創(chuàng)建產(chǎn)品時傳遞一個參數(shù)給工廠類,去表明我們想要創(chuàng)建哪種產(chǎn)品。下面我們將類圖翻譯成java代碼。
首先是產(chǎn)品接口。
public interface IProduct {public void method(); }接下來是具體的產(chǎn)品類。
public class ProductA implements IProduct{public void method() {System.out.println("產(chǎn)品A方法");}} public class ProductB implements IProduct{public void method() {System.out.println("產(chǎn)品B方法");}}最后是工廠類。
public class Creator {private Creator(){}public static IProduct createProduct(String productName){if (productName == null) {return null;}if (productName.equals("A")) {return new ProductA();}else if (productName.equals("B")) {return new ProductB();}else {return null;}} }我們測試一下。
public class Client {public static void main(String[] args) {IProduct product1 = Creator.createProduct("A");product1.method();IProduct product2 = Creator.createProduct("B");product2.method();} }? 測試結(jié)果。
以上就是簡單工廠模式的樣子,這個模式有個缺點,如果新加一種產(chǎn)品類,則Creator類的代碼要相應(yīng)改動,而工廠方法模式就很好的遵守了開閉原則,即對修改關(guān)閉,對擴展開放。
?
二、工廠方法模式
定義:工廠方法(Factory Method)模式的意義是定義一個創(chuàng)建產(chǎn)品對象的工廠接口,將實際創(chuàng)建工作推遲到子類當(dāng)中。核心工廠類不再負(fù)責(zé)產(chǎn)品的創(chuàng)建,這樣核心類成為一個抽象工廠角色,僅負(fù)責(zé)具體工廠子類必須實現(xiàn)的接口,這樣進一步抽象化的好處是使得工廠方法模式可以使系統(tǒng)在不修改具體工廠角色的情況下引進新的產(chǎn)品。
可以看到工廠方法模式中定義了一個工廠接口,而具體的創(chuàng)建工作推遲到具體的工廠類,它是對簡單工廠模式中的工廠類進一步抽象化,從而產(chǎn)生一個工廠類的抽象和實現(xiàn)體系,從而彌補簡單工廠模式對修改開放的詬病。
下面是百度百科中給出的該模式的類圖。
可以看到,上面右半部分是產(chǎn)品抽象和實現(xiàn)體系,左半部分是工廠抽象和實現(xiàn)體系,其中工廠體系依賴于產(chǎn)品體系,每一個工廠負(fù)責(zé)創(chuàng)造一種產(chǎn)品,這就省去了簡單工廠中的elseif判斷,又客戶端決定實例化一個特定的工廠去創(chuàng)建相應(yīng)的產(chǎn)品。
下面我們將類圖翻譯成代碼,首先是抽象產(chǎn)品接口。
public interface Light {public void turnOn();public void turnOff();}? 下面是具體的產(chǎn)品。
public class BuldLight implements Light{public void turnOn() {System.out.println("BuldLight On"); }public void turnOff() {System.out.println("BuldLight Off"); }} public class TubeLight implements Light{public void turnOn() {System.out.println("TubeLight On"); }public void turnOff() {System.out.println("TubeLight Off"); }}下面是抽象的工廠接口。
public interface Creator {public Light createLight(); }下面是具體的工廠類。
public class BuldCreator implements Creator{public Light createLight() {return new BuldLight();}} public class TubeCreator implements Creator{public Light createLight() {return new TubeLight();}}? 測試一下。
public class Client {public static void main(String[] args) {Creator creator = new BuldCreator();Light light = creator.createLight();light.turnOn();light.turnOff();creator = new TubeCreator();light = creator.createLight();light.turnOn();light.turnOff();} }? 測試結(jié)果。
可以看到,如果新增產(chǎn)品,只需要添加一個產(chǎn)品類,一個該產(chǎn)品的工廠類即可,不需要修改任何代碼。LZ在看這個模式的時候看到別人給出的例子是JDBC API的設(shè)計,覺得非常貼切。LZ也把這個例子記錄在這里。
眾所周知,為了統(tǒng)一各個數(shù)據(jù)庫操作的標(biāo)準(zhǔn),于是有了JDBC的API,它提供了一系列統(tǒng)一的,標(biāo)準(zhǔn)化的操作數(shù)據(jù)庫的接口,我們平時操作數(shù)據(jù)庫,依賴的就是這些抽象,而不是具體的數(shù)據(jù)庫的實現(xiàn),那sun公司是怎么做到的呢?用的就是工廠設(shè)計模式。
JDBC是如何統(tǒng)一了數(shù)據(jù)庫世界的呢?其實最主要的就是靠兩個接口。第一個接口是Driver,我們大體看下源碼。
package java.sql;import java.sql.DriverPropertyInfo; import java.sql.SQLException;/*** The interface that every driver class must implement.*/ public interface Driver {Connection connect(String url, java.util.Properties info)throws SQLException; }connect方法即創(chuàng)造一個數(shù)據(jù)庫連接,也就是說,Driver對象就是工廠模式中的Creator接口,即工廠類的抽象。
這個類除了connect方法以外,還有很多其他方法,篇幅原因,就不一一展開了,我們只關(guān)心核心方法,接口上有一句注釋,翻譯過來是這是一個任何驅(qū)動類都必須實現(xiàn)的接口。也就是說,sun公司明確規(guī)定,所有數(shù)據(jù)庫廠商都必須實現(xiàn)這個接口來提供JDBC服務(wù),即java數(shù)據(jù)庫連接服務(wù)。
? 第二個接口是connect方法的返回抽象Connection對象,我們看一下源碼,仍然只關(guān)心核心方法就可以。
package java.sql;import java.sql.PreparedStatement; import java.sql.SQLException;/*** <P>A connection (session) with a specific* database. SQL statements are executed and results are returned* within the context of a connection.* <P>*/ public interface Connection extends Wrapper {Statement createStatement() throws SQLException;PreparedStatement prepareStatement(String sql) throws SQLException;}以上兩個接口作為JDBC API的一部分,它們相當(dāng)于告訴了數(shù)據(jù)庫生產(chǎn)廠商兩個要求。
???????第一,數(shù)據(jù)庫廠商要提供一個數(shù)據(jù)庫驅(qū)動類,它的作用可以是可以創(chuàng)造數(shù)據(jù)庫連接,而這個數(shù)據(jù)庫連接向上轉(zhuǎn)型為我們JDBC的Connection。
? ? ? ?第二,數(shù)據(jù)庫廠商要提供一個數(shù)據(jù)庫連接的實現(xiàn)類,這個實現(xiàn)類可以執(zhí)行具體數(shù)據(jù)庫的各個操作,比如幫我們執(zhí)行SQL,返回執(zhí)行結(jié)果,關(guān)閉連接等等。
LZ把類圖畫了一下,UML類圖對設(shè)計模式這塊非常重要,我個人的經(jīng)驗是,永遠(yuǎn)不要記代碼,要記設(shè)計思想,記UML類圖,記應(yīng)用場景,所謂用抽象構(gòu)建框架,用細(xì)節(jié)擴展實現(xiàn)。
多標(biāo)準(zhǔn)的工廠方法設(shè)計模式啊,sun公司正是用這個模式統(tǒng)一了數(shù)據(jù)庫世界。工廠方法模式就是提供一個抽象的工廠,一個抽象的產(chǎn)品,在上述當(dāng)中相當(dāng)于Driver(數(shù)據(jù)庫連接工廠)和Connection(抽象產(chǎn)品),實現(xiàn)的一方需要提供一個具體的工廠類(比如mysql驅(qū)動)和一個具體的產(chǎn)品(比如mysql數(shù)據(jù)庫連接)。
客戶端調(diào)用時不依賴于具體工廠和產(chǎn)品(即到底是mysql驅(qū)動,mysql數(shù)據(jù)庫連接還是oracle驅(qū)動,oracle連接,我們程序猿不需要管的,我們只管使用抽象的driver和connection,對吧?),而是依賴于抽象工廠和抽象產(chǎn)品完成工作。
類圖里還有個DriverMananger,DriverMananger在這個設(shè)計當(dāng)中扮演者一個管理者的角色,它幫我們管理數(shù)據(jù)庫驅(qū)動,讓我們不需要直接接觸驅(qū)動接口,我們獲取連接只需要和DriverManager打交道就可以,也就是說客戶端依賴于DriverManager和Connection就可以完成工作,不再需要與Driver關(guān)聯(lián),所以上述說我們依賴于Driver和Connection,現(xiàn)在DriverManager幫我們管理Driver,那我們只需要依賴于DriverManager和Connection就可以了。回想我們剛開始學(xué)習(xí)JDBC的時候,是不是只要讓數(shù)據(jù)庫廠商提供的具體數(shù)據(jù)庫連接類加載,就可以直接從DriverManager里取連接了,所以這是sun公司為了方便編碼給我們提供的一個管理類。
?
?三、抽象工廠模式
抽象工廠模式算是工廠相關(guān)模式的終極形,基于上面的理解,我們不難理解抽象工廠模式,它與工廠方法唯一的區(qū)別就是工廠的接口里是一系列創(chuàng)造抽象產(chǎn)品的方法,而不再是一個,而相應(yīng)的,抽象產(chǎn)品也不再是一個了,而是一系列相關(guān)的產(chǎn)品。這其實是工廠方法模式的一種擴展不是嗎?
定義:為創(chuàng)建一組相關(guān)或相互依賴的對象提供一個接口,而且無需指定他們的具體類。
我們看下百度百科給出的類圖。
?
我們把類圖翻譯成代碼看一下。首先是產(chǎn)品族,也就是類圖右邊部分。
package net;interface ProductA {void methodA(); }interface ProductB {void methodB(); }class ProductA1 implements ProductA{public void methodA() {System.out.println("產(chǎn)品A系列中1型號產(chǎn)品的方法");}}class ProductA2 implements ProductA{public void methodA() {System.out.println("產(chǎn)品A系列中2型號產(chǎn)品的方法");}}class ProductB1 implements ProductB{public void methodB() {System.out.println("產(chǎn)品B系列中1型號產(chǎn)品的方法");}}class ProductB2 implements ProductB{public void methodB() {System.out.println("產(chǎn)品B系列中2型號產(chǎn)品的方法");}}? 左半部分。
package net;public interface Creator {ProductA createProductA();ProductB createProductB();} package net;public class ConcreteCreator1 implements Creator{public ProductA createProductA() {return new ProductA1();}public ProductB createProductB() {return new ProductB1();}} package net;public class ConcreteCreator2 implements Creator{public ProductA createProductA() {return new ProductA2();}public ProductB createProductB() {return new ProductB2();}}測試一下。
package net;public class Client {public static void main(String[] args) throws Exception {Creator creator = new ConcreteCreator1();ProductA productA = creator.createProductA();ProductB productB = creator.createProductB();productA.methodA();productB.methodB();creator = new ConcreteCreator2();productA = creator.createProductA();productB = creator.createProductB();productA.methodA();productB.methodB();} }? 綜上所述,簡單工廠→工廠方法→抽象工廠,是一步步進化的過程。
1,首先從簡單工廠進化到工廠方法,是因為工廠方法彌補了簡單工廠對修改開放的弊端,即簡單工廠違背了開閉原則。
2,從工廠方法進化到抽象工廠,是因為抽象工廠彌補了工廠方法只能創(chuàng)造一個的產(chǎn)品的弊端。
工廠設(shè)計模式可能對像LZ這樣平時只針對業(yè)務(wù)編碼的程序猿來說用到的機會少一點,但是我們在看源碼的過程中一定會看到這個模式,比如前面提到的JDBC,現(xiàn)在相信再回頭看JDBC的源碼,就能看懂當(dāng)年sun公司為什么要這么去設(shè)計代碼,大牛們牛X的地方,我們才能真的體會到。
轉(zhuǎn)載于:https://www.cnblogs.com/peterxiao/p/10207598.html
總結(jié)
以上是生活随笔為你收集整理的设计模式(五)--工厂模式汇总的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SQL UPDATE with INNE
- 下一篇: ASP.NET获取客户端、服务器端基础信