天道不一定酬所有勤
但是,天道只酬勤

英雄联盟之绝对嚣张已疯:你以為工廠模式很簡單,可能是因為你懂的只是冰山的一角

開發十年,就只剩下這套架構體系了??!

英雄联盟充值记录 www.frhrb.icu 很多人認為工廠模式很簡單,只是有一個建造工廠,幫我們進行對象構造而已。那么請嘗試回答下以下問題:

1、工廠模式分為幾類?
2、GOF 23種設計模式中,工廠方法模式和抽象工廠模式有什么區別?
3、不在GOF 23種設計模式中的簡單工廠模式是什么?
4、簡單工廠模式、工廠方法模式和抽象工廠模式各自解決什么問題?有什么不同?

如果以上四個問題,你都可以很好的回答的話,那么這篇文章就沒有繼續讀下去的必要了,否則,建議你好好學習下本文。

三種工廠模式

工廠模式可以分為三類:

  • 1)簡單工廠模式(Simple Factory)

  • 2)工廠方法模式(Factory Method)

  • 3)抽象工廠模式(Abstract Factory)

這三種模式從上到下逐步抽象,并且更具一般性。

GOF在《設計模式》一書中將工廠模式分為兩類:工廠方法模式(Factory Method)與抽象工廠模式(Abstract Factory)。

將簡單工廠模式(Simple Factory)看為工廠方法模式的一種特例,兩者歸為一類。

這三種工廠模式在設計模式的分類中都屬于創建型模式。

創建型模式(Creational Pattern)對類的實例化過程進行了抽象,能夠將軟件??櫓卸韻蟮拇唇ê投韻蟮氖褂梅擲?。為了使軟件的結構更加清晰,外界對于這些對象只需要知道它們共同的接口,而不清楚其具體的實現細節,使整個系統的設計更加符合單一職責原則。

創建型模式在創建什么(What),由誰創建(Who),何時創建(When)等方面都為軟件設計者提供了盡可能大的靈活性。

創建型模式隱藏了類的實例的創建細節,通過隱藏對象如何被創建和組合在一起達到使整個系統獨立的目的。

工廠模式是創建型模式中比較重要的。工廠模式的主要功能就是幫助我們實例化對象的。之所以名字中包含工廠模式四個字,是因為對象的實例化過程是通過工廠實現的,是用工廠代替new操作的。

這樣做的好處是封裝了對象的實例化細節,尤其是對于實例化較復雜或者對象的生命周期應該集中管理的情況?;岣閬低炒錘蟮目衫┱剮院途×可俚男薷牧?。

接下來我們分別介紹下這三種工廠模式。

簡單工廠模式

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

在介紹簡單工廠模式之前,我們嘗試解決以下問題:

現在我們要使用面向對象的形式定義計算器,為了實現各算法之間的解耦。主要的用到的類如下:

// 計算類的基類
public abstract class Operation {

    private double value1 = 0;
    private double value2 = 0;

    public double getValue1() {
        return value1;
    }
    public void setValue1(double value1) {
        this.value1 = value1;
    }
    public double getValue2() {
        return value2;
    }
    public void setValue2(double value2) {
        this.value2 = value2;
    }
    protected abstract double getResule();
}

//加法
public class OperationAdd extends Operation {
    @Override
    protected double getResule() {
        return getValue1() + getValue2();
    }
}
//減法
public class OperationSub extends Operation {
    @Override
    protected double getResule() {
        return getValue1() - getValue2();
    }
}
//乘法
public class OperationMul extends Operation {
    @Override
    protected double getResule() {
        return getValue1() * getValue2();
    }
}
//除法
public class OperationDiv extends Operation {
    @Override
    protected double getResule() {
        if (getValue2() != 0) {
            return getValue1() / getValue2();
        }
        throw new IllegalArgumentException("除數不能為零");
    }
}

當我想要執行加法運算時,可以使用如下代碼:

public class Main {
    public static void main(String[] args) {
        OperationAdd operationAdd = new OperationAdd();
        operationAdd.setValue1(10);
        operationAdd.setValue2(5);
System.out.println(operationAdd.getResule());
    }
}

當我需要執行減法運算時,我就要創建一個OperationSub類。也就是說,我想要使用不同的運算的時候就要創建不同的類,并且要明確知道該類的名字。

那么這種重復的創建類的工作其實可以放到一個統一的工廠類中。簡單工廠模式有以下優點:

1、一個調用者想創建一個對象,只要知道其名稱就可以了。

2、屏蔽產品的具體實現,調用者只關心產品的接口。

簡單工廠模式實現方式

簡單工廠模式其實和他的名字一樣,很簡單。先來看看它的組成:

Factory:這是本模式的核心,含有一定的商業邏輯和判斷邏輯。在java中它往往由 一個具體類實現。(OperationFactory)

Product:它一般是具體產品繼承的父類或者實現的接口。在java中由接口或者抽象類來實現。(Operation)

ConcreteProduct:工廠類所創建的對象就是此角色的實例。在java中由一個具體類實現。 來用類圖來清晰的表示下的它們之間的關系(OperationAdd\OperationSub等)

?

在原有類的基礎上,定義工廠類:

//工廠類
public class OperationFactory {

    public static Operation createOperation(String operation) {
        Operation oper = null;
        switch (operation) {
            case "+":
                oper = new OperationAdd();
                break;
            case "-":
                oper = new OperationSub();
                break;
            case "*":
                oper = new OperationMul();
                break;

            case "/":
                oper = new OperationDiv();
                break;
            default:
                throw new UnsupportedOperationException("不支持該操作");
        }
        return oper;
    }
}

有了工廠類之后,可以使用工廠創建對象:

Operation operationAdd = OperationFactory.createOperation("+");
operationAdd.setValue1(10);
operationAdd.setValue2(5);
System.out.println(operationAdd.getResule());

通過簡單工廠模式,該計算器的使用者不需要關系實現加法邏輯的那個類的具體名字,他只要知道該類對應的參數”+”就可以了。

簡單工廠模式存在的問題

當我們需要增加一種計算時,例如開平方。這個時候我們需要先定義一個類繼承Operation類,其中實現平方的代碼。除此之外我們還要修改OperationFactory類的代碼,增加一個case。這顯然是違背開閉原則的??上攵雜諦虜返募尤?,工廠類是很被動的。

我們舉的例子是最簡單的情況。而在實際應用中,很可能產品是一個多層次的樹狀結構。 簡單工廠可能就不太適用了。

簡單工廠模式總結

工廠類是整個簡單工廠模式的關鍵。包含了必要的邏輯判斷,根據外界給定的信息,決定究竟應該創建哪個具體類的對象。通過使用工廠類,外界可以從直接創建具體產品對象的尷尬局面擺脫出來,僅僅需要負責“消費”對象就可以了。而不必管這些對象究竟如何創建及如何組織的。明確了各自的職責和權利,有利于整個軟件體系結構的優化。

但是由于工廠類集中了所有實例的創建邏輯,違反了高內聚責任分配原則,將全部創建邏輯集中到了一個工廠類中;它所能創建的類只能是事先考慮到的,如果需要添加新的類,則就需要改變工廠類了。

當系統中的具體產品類不斷增多時候,可能會出現要求工廠類根據不同條件創建不同實例的需求.這種對條件的判斷和對具體產品類型的判斷交錯在一起,很難避免??楣δ艿穆?,對系統的維護和擴展非常不利;

這些缺點在工廠方法模式中得到了一定的解決。

工廠方法模式

工廠方法模式(Factory Method Pattern)又稱為工廠模式,也叫虛擬構造器(Virtual Constructor)模式或者多態工廠(Polymorphic Factory)模式,它屬于類創建型模式。

工廠方法模式是一種實現了“工廠”概念的面向對象設計模式。就像其他創建型模式一樣,它也是處理在不指定對象具體類型的情況下創建對象的問題。

工廠方法模式的實質是“定義一個創建對象的接口,但讓實現這個接口的類來決定實例化哪個類。工廠方法讓類的實例化推遲到子類中進行?!?/p>

工廠方法模式用途

工廠方法模式和簡單工廠模式雖然都是通過工廠來創建對象,他們之間最大的不同是——工廠方法模式在設計上完全完全符合“開閉原則”。

在以下情況下可以使用工廠方法模式:

一個類不知道它所需要的對象的類:在工廠方法模式中,客戶端不需要知道具體產品類的類名,只需要知道所對應的工廠即可,具體的產品對象由具體工廠類創建;客戶端需要知道創建具體產品的工廠類。

一個類通過其子類來指定創建哪個對象:在工廠方法模式中,對于抽象工廠類只需要提供一個創建產品的接口,而由其子類來確定具體要創建的對象,利用面向對象的多態性和里氏代換原則,在程序運行時,子類對象將覆蓋父類對象,從而使得系統更容易擴展。

將創建對象的任務委托給多個工廠子類中的某一個,客戶端在使用時可以無須關心是哪一個工廠子類創建產品子類,需要時再動態指定,可將具體工廠類的類名存儲在配置文件或數據庫中。

工廠方法模式實現方式

工廠方法模式包含如下角色:

Product:抽象產品(Operation

ConcreteProduct:具體產品(OperationAdd)

Factory:抽象工廠(IFactory)

ConcreteFactory:具體工廠(AddFactory)

?

這里還用計算器的例子。在保持Operation,OperationAdd,OperationDiv,OperationSub,OperationMul等幾個方法不變的情況下,修改簡單工廠模式中的工廠類(OperationFactory)。替代原有的那個”萬能”的大工廠類,這里使用工廠方法來代替:

//工廠接口
public interface IFactory {
    Operation CreateOption();
}

//加法類工廠
public class AddFactory implements IFactory {
    public Operation CreateOption() {
        return new OperationAdd();
    }
}

//除法類工廠
public class DivFactory implements IFactory {
    public Operation CreateOption() {
        return new OperationDiv();
    }
}

//除法類工廠
public class MulFactory implements IFactory {
    public Operation CreateOption() {
        return new OperationMul();
    }
}

//減法類工廠
public class SubFactory implements IFactory {
    public Operation CreateOption() {
        return new OperationSub();
    }
}

這樣,在客戶端中想要執行加法運算時,需要以下方式:

public class Main {

    public static void main(String[] args) {
        IFactory factory = new AddFactory();
        Operation operationAdd =  factory.CreateOption();
        operationAdd.setValue1(10);
        operationAdd.setValue2(5);
        System.out.println(operationAdd.getResult());
    }
}

到這里,一個工廠方法模式就已經寫好了。

從代碼量上看,這種工廠方法模式比簡單工廠方法模式更加復雜。針對不同的操作(Operation)類都有對應的工廠。很多人會有以下疑問:

貌似工廠方法模式比簡單工廠模式要復雜的多?

工廠方法模式和我自己創建對象沒什么區別?為什么要多搞出一些工廠來?

下面就針對以上兩個問題來深入理解一下工廠方法模式。

為什么要使用工廠來創建對象?

封裝對象的創建過程

在工廠方法模式中,工廠方法用來創建客戶所需要的產品,同時還向客戶隱藏了哪種具體產品類將被實例化這一細節,用戶只需要關心所需產品對應的工廠,無須關心創建細節,甚至無須知道具體產品類的類名。

基于工廠角色和產品角色的多態性設計是工廠方法模式的關鍵。它能夠使工廠可以自主確定創建何種產品對象,而如何創建這個對象的細節則完全封裝在具體工廠內部。工廠方法模式之所以又被稱為多態工廠模式,是因為所有的具體工廠類都具有同一抽象父類。

為什么每種對象要單獨有一個工廠?

符合『開放-封閉原則』

主要目的是為了解耦。在系統中加入新產品時,無須修改抽象工廠和抽象產品提供的接口,無須修改客戶端,也無須修改其他的具體工廠和具體產品,而只要添加一個具體工廠和具體產品就可以了。這樣,系統的可擴展性也就變得非常好,完全符合“開閉原則。

以上就是工廠方法模式的優點。但是,工廠模式也有一些不盡如人意的地方:

在添加新產品時,需要編寫新的具體產品類,而且還要提供與之對應的具體工廠類,系統中類的個數將成對增加,在一定程度上增加了系統的復雜度,有更多的類需要編譯和運行,會給系統帶來一些額外的開銷。

由于考慮到系統的可擴展性,需要引入抽象層,在客戶端代碼中均使用抽象層進行定義,增加了系統的抽象性和理解難度,且在實現時可能需要用到DOM、反射等技術,增加了系統的實現難度。

工廠方法模式總結

工廠方法模式是簡單工廠模式的進一步抽象和推廣。

由于使用了面向對象的多態性,工廠方法模式保持了簡單工廠模式的優點,而且克服了它的缺點。

在工廠方法模式中,核心的工廠類不再負責所有產品的創建,而是將具體創建工作交給子類去做。這個核心類僅僅負責給出具體工廠必須實現的接口,而不負責產品類被實例化這種細節,這使得工廠方法模式可以允許系統在不修改工廠角色的情況下引進新產品。

工廠方法模式的主要優點是增加新的產品類時無須修改現有系統,并封裝了產品對象的創建細節,系統具有良好的靈活性和可擴展性;其缺點在于增加新產品的同時需要增加新的工廠,導致系統類的個數成對增加,在一定程度上增加了系統的復雜性。

抽象工廠模式

抽象工廠模式(Abstract Factory Pattern):提供一個創建一系列相關或相互依賴對象的接口,而無須指定它們具體的類。抽象工廠模式又稱為Kit模式,屬于對象創建型模式。

抽象工廠模式提供了一種方式,可以將同一產品族的單獨的工廠封裝起來。在正常使用中,客戶端程序需要創建抽象工廠的具體實現,然后使用抽象工廠作為接口來創建這一主題的具體對象??突Ф順絳蠆恍枰潰ɑ蜆匭模┧誘廡┠誆康墓こХ椒ㄖ謝竦枚韻蟮木嚀謇嘈?,因為客戶端程序僅使用這些對象的通用接口。抽象工廠模式將一組對象的實現細節與他們的一般使用分離開來。

產品族

來認識下什么是產品族: 位于不同產品等級結構中,功能相關的產品組成的家族。如下面的例子,就有兩個產品族:跑車族和商務車族。

?

抽象工廠模式用途

抽象工廠模式和工廠方法模式一樣,都符合開放-封閉原則。但是不同的是,工廠方法模式在增加一個具體產品的時候,都要增加對應的工廠。但是抽象工廠模式只有在新增一個類型的具體產品時才需要新增工廠。也就是說,工廠方法模式的一個工廠只能創建一個具體產品。而抽象工廠模式的一個工廠可以創建屬于一類類型的多種具體產品。工廠創建產品的個數介于簡單工廠模式和工廠方法模式之間。

在以下情況下可以使用抽象工廠模式:

一個系統不應當依賴于產品類實例如何被創建、組合和表達的細節,這對于所有類型的工廠模式都是重要的。

系統中有多于一個的產品族,而每次只使用其中某一產品族。

屬于同一個產品族的產品將在一起使用,這一約束必須在系統的設計中體現出來。

系統提供一個產品類的庫,所有的產品以同樣的接口出現,從而使客戶端不依賴于具體實現。

抽象工廠模式實現方式

抽象工廠模式包含如下角色:

AbstractFactory(抽象工廠):用于聲明生成抽象產品的方法

ConcreteFactory(具體工廠):實現了抽象工廠聲明的生成抽象產品的方法,生成一組具體產品,這些產品構成了一個產品族,每一個產品都位于某個產品等級結構中;

AbstractProduct(抽象產品):為每種產品聲明接口,在抽象產品中定義了產品的抽象業務方法;

Product(具體產品):定義具體工廠生產的具體產品對象,實現抽象產品接口中定義的業務方法。

本文的例子采用一個汽車代工廠造汽車的例子。假設我們是一家汽車代工廠商,我們負責給奔馳和特斯拉兩家公司制造車子。我們簡單的把奔馳車理解為需要加油的車,特斯拉為需要充電的車。其中奔馳車中包含跑車和商務車兩種,特斯拉同樣也包含奔馳車和商務車。

?

以上場景,我們就可以把跑車和商務車分別對待,對于跑車有單獨的工廠創建,商務車也有單獨的工廠。這樣,以后無論是再幫任何其他廠商造車,只要是跑車或者商務車我們都不需要再引入工廠。同樣,如果我們要增加一種其他類型的車,比如越野車,我們也不需要對跑車或者商務車的任何東西做修改。

下面是抽象產品,奔馳車和特斯拉車:

public interface BenzCar {
    //加汽油
    public void gasUp();

}

public interface TeslaCar {
    //充電
    public void charge();
}

下面是具體產品,奔馳跑車、奔馳商務車、特斯拉跑車、特斯拉商務車:

public class BenzSportCar implements BenzCar {
    public void gasUp() {
        System.out.println("給我的奔馳跑車加最好的汽油");
    }
}

public class BenzBusinessCar implements BenzCar{
    public void gasUp() {
        System.out.println("給我的奔馳商務車加一般的汽油");
    }
}

public class TeslaSportCar implements TeslaCar {
    public void charge() {
        System.out.println("給我特斯拉跑車沖滿電");
    }
}

public class TeslaBusinessCar implements TeslaCar {
    public void charge() {
        System.out.println("不用給我特斯拉商務車沖滿電");
    }
}

下面是抽象工廠:

public interface CarFactory {

    public BenzCar getBenzCar();
    public TeslaCar getTeslaCar();
}

下面是具體工廠:

public class SportCarFactory implements CarFactory {
    public BenzCar getBenzCar() {
        return new BenzSportCar();
    }

    public TeslaCar getTeslaCar() {
        return new TeslaSportCar();
    }
}

public class BusinessCarFactory implements CarFactory {
    public BenzCar getBenzCar() {
        return new BenzBusinessCar();
    }

    public TeslaCar getTeslaCar() {
        return new TeslaBusinessCar();
    }
}

“開閉原則”的傾斜性

“開閉原則”要求系統對擴展開放,對修改封閉,通過擴展達到增強其功能的目的。對于涉及到多個產品族與多個產品等級結構的系統,其功能增強包括兩方面:

增加產品族:對于增加新的產品族,工廠方法模式很好的支持了“開閉原則”,對于新增加的產品族,只需要對應增加一個新的具體工廠即可,對已有代碼無須做任何修改。

增加新的產品等級結構:對于增加新的產品等級結構,需要修改所有的工廠角色,包括抽象工廠類,在所有的工廠類中都需要增加生產新產品的方法,不能很好地支持“開閉原則”。

抽象工廠模式的這種性質稱為“開閉原則”的傾斜性,抽象工廠模式以一種傾斜的方式支持增加新的產品,它為新產品族的增加提供方便,但不能為新的產品等級結構的增加提供這樣的方便。

抽象工廠模式總結

抽象工廠模式提供一個創建一系列相關或相互依賴對象的接口,而無須指定它們具體的類。抽象工廠模式又稱為Kit模式,屬于對象創建型模式。

抽象工廠模式是所有形式的工廠模式中最為抽象和最具一般性的一種形態。

抽象工廠模式的主要優點是隔離了具體類的生成,使得客戶并不需要知道什么被創建,而且每次可以通過具體工廠類創建一個產品族中的多個對象,增加或者替換產品族比較方便,增加新的具體工廠和產品族很方便;主要缺點在于增加新的產品等級結構很復雜,需要修改抽象工廠和所有的具體工廠類,對“開閉原則”的支持呈現傾斜性。

三種工廠模式對比

簡單工廠模式的優缺點

  • 優點:
    • 1、屏蔽產品的具體實現,調用者只關心產品的接口。
    • 2、實現簡單
  • 缺點:
    • 1、增加產品,需要修改工廠類,不符合開放-封閉原則
    • 2、工廠類集中了所有實例的創建邏輯,違反了高內聚責任分配原則

工廠方法模式的優缺點

  • 優點:
    • 1、繼承了簡單工廠模式的優點
    • 2、符合開放-封閉原則
  • 缺點:
    • 1、增加產品,需要增加新的工廠類,導致系統類的個數成對增加,在一定程度上增加了系統的復雜性。

抽象工廠模式的優缺點

  • 優點:
    • 1、隔離了具體類的生成,使得客戶并不需要知道什么被創建
    • 2、每次可以通過具體工廠類創建一個產品族中的多個對象,增加或者替換產品族比較方便,增加新的具體工廠和產品族很方便;
  • 缺點
    • 增加新的產品等級結構很復雜,需要修改抽象工廠和所有的具體工廠類,對“開閉原則”的支持呈現傾斜性。

?

簡單工廠 : 用來生產同一等級結構中的任意產品。(對于增加新的產品,主要是新增產品,就要修改工廠類。符合單一職責原則。不符合開放-封閉原則)

工廠方法 :用來生產同一等級結構中的固定產品。(支持增加任意產品,新增產品時不需要更改已有的工廠,需要增加該產品對應的工廠。符合單一職責原則、符合開放-封閉原則。但是引入了復雜性)

抽象工廠 :用來生產不同產品族的全部產品。(增加新產品時,需要修改工廠,增加產品族時,需要增加工廠。符合單一職責原則,部分符合開放-封閉原則,降低了復雜性)

最后,三種工廠模式各有優缺點,沒有最好的,只有最合適的!

(全文完) 歡迎關注『Java之道』微信公眾號
贊(7)
如未加特殊說明,此網站文章均為原創,轉載必須注明出處。英雄联盟充值记录 » 你以為工廠模式很簡單,可能是因為你懂的只是冰山的一角
分享到: 更多 (0)

評論 1

  • 昵稱 (必填)
  • 郵箱 (必填)
  • 網址
  1. #1

    圖是用什么工具畫的,超贊

    sanjin1個月前 (05-31)回復

HollisChuang's Blog

聯系我關于我