1. 程式人生 > >工廠模式匯總

工廠模式匯總

系統 jdb mysql驅動 簡單工廠模式 gof設計模式 ecif 抽象工廠模式 lex client

  LZ想把簡單工廠模式、工廠方法模式和抽象工廠模式整理到一篇博文當中,由淺入深,應該能方便理解和記憶,話不多說,進入正題。

一、簡單工廠模式

  定義:從設計模式的類型上來說,簡單工廠模式是屬於創建型模式,又叫做靜態工廠方法(Static Factory Method)模式,但不屬於23種GOF設計模式之一。簡單工廠模式是由一個工廠對象決定創建出哪一種產品類的實例。簡單工廠模式是工廠模式家族中最簡單實用的模式,可以理解為是不同工廠模式的一個特殊實現。

  總結成一句話就是,由一個工廠對象決定創建出哪一種產品類的實例。下面是百度百科中簡單工廠模式的類圖。

技術分享圖片

  可以看出,上面總共有三種類,一個是工廠類Creator,一個是產品接口IProduct,一個是具體的產品類,例如產品A和產品B,這之中,工廠類負責整個創建產品的邏輯判斷,所以為了使工廠類能夠知道我們需要哪一種產品,我們需要在創建產品時傳遞一個參數給工廠類,去表明我們想要創建哪種產品。下面我們將類圖翻譯成java代碼。

  首先是產品接口。

public interface IProduct {
    public void method();
}

  接下來是具體的產品類。

public class ProductA implements IProduct{

    public void method() {
        System.out.println("產品A方法");
    }

}
public class ProductB implements IProduct{

    public void method() {
        System.out.println(
"產品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();
    }
}

  測試結果。

技術分享圖片

  以上就是簡單工廠模式的樣子,這個模式有個缺點,如果新加一種產品類,則Creator類的代碼要響應改動,而工廠方法模式就很好的遵守了開閉原則,即對修改關閉,對擴展開放。

二、工廠方法模式

  定義:工廠方法(Factory Method)模式的意義是定義一個創建產品對象的工廠接口,將實際創建工作推遲到子類當中。核心工廠類不再負責產品的創建,這樣核心類成為一個抽象工廠角色,僅負責具體工廠子類必須實現的接口,這樣進一步抽象化的好處是使得工廠方法模式可以使系統在不修改具體工廠角色的情況下引進新的產品。

  可以看到工廠方法模式中定義了一個工廠接口,而具體的創建工作推遲到具體的工廠類,它是對簡單工廠模式中的工廠類進一步抽象化,從而產生一個工廠類的抽象和實現體系,從而彌補簡單工廠模式對修改開放的詬病。

  下面是百度百科中給出的該模式的類圖。

技術分享圖片

  可以看到,上面右半部分是產品抽象和實現體系,左半部分是工廠抽象和實現體系,其中工廠體系依賴於產品體系,每一個工廠負責創造一種產品,這就省去了簡單工廠中的elseif判斷,又客戶端決定實例化一個特定的工廠去創建相應的產品。

  下面我們將類圖翻譯成代碼,首先是抽象產品接口。

public interface Light {

    public void turnOn();

    public void turnOff();
    
}

  下面是具體的產品。

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();
    }
}

  測試結果。

技術分享圖片

  可以看到,如果新增產品,只需要添加一個產品類,一個該產品的工廠類即可,不需要修改任何代碼。LZ在看這個模式的時候看到別人給出的例子是JDBC API的設計,覺得非常貼切。LZ也把這個例子記錄在這裏。

  眾所周知,為了統一各個數據庫操作的標準,於是有了JDBC的API,它提供了一系列統一的,標準化的操作數據庫的接口,我們平時操作數據庫,依賴的就是這些抽象,而不是具體的數據庫的實現,那sun公司是怎麽做到的呢?用的就是工廠設計模式。

  JDBC是如何統一了數據庫世界的呢?其實最主要的就是靠兩個接口。第一個接口是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方法即創造一個數據庫連接,也就是說,Driver對象就是工廠模式中的Creator接口,即工廠類的抽象。

  這個類除了connect方法以外,還有很多其他方法,篇幅原因,就不一一展開了,我們只關心核心方法,接口上有一句註釋,翻譯過來是這是一個任何驅動類都必須實現的接口。也就是說,sun公司明確規定,所有數據庫廠商都必須實現這個接口來提供JDBC服務,即java數據庫連接服務。

  第二個接口是connect方法的返回抽象Connection對象,我們看一下源碼,仍然只關心核心方法就可以。

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的一部分,它們相當於告訴了數據庫生產廠商兩個要求。

第一,數據庫廠商要提供一個數據庫驅動類,它的作用可以是可以創造數據庫連接,而這個數據庫連接向上轉型為我們JDBC的Connection。

第二,數據庫廠商要提供一個數據庫連接的實現類,這個實現類可以執行具體數據庫的各個操作,比如幫我們執行SQL,返回執行結果,關閉連接等等。

  LZ把類圖畫了一下,UML類圖對設計模式這塊非常重要,我個人的經驗是,永遠不要記代碼,要記設計思想,記UML類圖,記應用場景,所謂用抽象構建框架,用細節擴展實現

技術分享圖片

  多標準的工廠方法設計模式啊,sun公司正是用這個模式統一了數據庫世界。工廠方法模式就是提供一個抽象的工廠,一個抽象的產品,在上述當中相當於Driver(數據庫連接工廠)和Connection(抽象產品),實現的一方需要提供一個具體的工廠類(比如mysql驅動)和一個具體的產品(比如mysql數據庫連接)。

  客戶端調用時不依賴於具體工廠和產品(即到底是mysql驅動,mysql數據庫連接還是oracle驅動,oracle連接,我們程序猿不需要管的,我們只管使用抽象的driver和connection,對吧?),而是依賴於抽象工廠和抽象產品完成工作。

  類圖裏還有個DriverMananger,DriverMananger在這個設計當中扮演者一個管理者的角色,它幫我們管理數據庫驅動,讓我們不需要直接接觸驅動接口,我們獲取連接只需要和DriverManager打交道就可以,也就是說客戶端依賴於DriverManager和Connection就可以完成工作,不再需要與Driver關聯,所以上述說我們依賴於Driver和Connection,現在DriverManager幫我們管理Driver,那我們只需要依賴於DriverManager和Connection就可以了。回想我們剛開始學習JDBC的時候,是不是只要讓數據庫廠商提供的具體數據庫連接類加載,就可以直接從DriverManager裏取連接了,所以這是sun公司為了方便編碼給我們提供的一個管理類。

三、抽象工廠模式

  抽象工廠模式算是工廠相關模式的終極形,基於上面的理解,我們不難理解抽象工廠模式,它與工廠方法唯一的區別就是工廠的接口裏是一系列創造抽象產品的方法,而不再是一個,而相應的,抽象產品也不再是一個了,而是一系列相關的產品。這其實是工廠方法模式的一種擴展不是嗎?

  定義:為創建一組相關或相互依賴的對象提供一個接口,而且無需指定他們的具體類。

  我們看下百度百科給出的類圖。

技術分享圖片

  我們把類圖翻譯成代碼看一下。首先是產品族,也就是類圖右邊部分。

package net;

interface ProductA {

    void methodA();
}

interface ProductB {
    
    void methodB();
}

class ProductA1 implements ProductA{

    public void methodA() {
        System.out.println("產品A系列中1型號產品的方法");
    }
    
}

class ProductA2 implements ProductA{

    public void methodA() {
        System.out.println("產品A系列中2型號產品的方法");
    }
    
}

class ProductB1 implements ProductB{

    public void methodB() {
        System.out.println("產品B系列中1型號產品的方法");
    }
    
}

class ProductB2 implements ProductB{

    public void methodB() {
        System.out.println("產品B系列中2型號產品的方法");
    }
    
}

  左半部分。

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,從工廠方法進化到抽象工廠,是因為抽象工廠彌補了工廠方法只能創造一個的產品的弊端。

  工廠設計模式可能對像LZ這樣平時只針對業務編碼的程序猿來說用到的機會少一點,但是我們在看源碼的過程中一定會看到這個模式,比如前面提到的JDBC,現在相信再回頭看JDBC的源碼,就能看懂當年sun公司為什麽要這麽去設計代碼,大牛們牛X的地方,我們才能真的體會到。

工廠模式匯總