一起學設計模式-02 工廠模式

寫在前面

如果你還沒了解六大軟件設計原則的話,建議先谷歌下,再來學習設計模式。這對理解設計模式和記憶設計模式的經典寫法都會有很大幫助。學習設計模式,光是記寫法沒有意義,要學習的是設計思想,爲什麼要這麼做,這麼做的好處是什麼。懷着這兩個問題來學習設計模式,你會發現六大軟件設計原則在設計模式中的應用,相應的也會加強你對六大軟件設計原則的記憶。

工廠模式的經典疑問

很多人,包括我自己在看工廠模式的時候都會問一個問題,工廠模式的好處到底是什麼?有人說抽象?我覺得那是依賴倒置的好處,工廠模式遵循了依賴倒置原則,自然有抽象的特點,但這並不是根本好處,但凡遵循這一原則的都會具備這個特點。所以在我們在學習某個知識點的時候,要多看幾篇文章,反覆對比斟酌,不要希望說“XXX看這一篇就夠了”(很多博客的標題都會這麼起),包括這篇文章,也僅僅是作爲讀者的一個參考。那麼工廠模式的好處到底是啥,我個人認爲工廠模式最根本的好處是:

工廠模式最直接的好處就是屏蔽了對象的創建細節,將使用者和對象的創建過程隔離開來。特別是對那些創建過程非常複雜的對象,使用者不再需要關心這些對象是如何創建的,這麼做簡化了使用者的操作,降低了調用難度。另外,由於創建的過程被工廠封裝,也避免了創建對象的代碼,散落在各個模塊的情況,同時也減少了代碼開發工作量。當然工廠模式也具備多態的特性,這是因爲工廠模式遵循依賴倒置原則,只是一個附帶的特性。最後,多了這層封裝也爲實例創建的集中管理提供了便捷。

假如我需要一個ThinkPad 的Computer電腦實例來辦公,這個ThinkPad電腦實例是由A Screen顯示屏,B KeyBoard鍵盤,C CPU 處理器, D RAM內存條構成,且有一個非常複雜的組裝過程。好了,作爲一個使用者,我可能需要這麼寫:

Screen aScreen = new AScreen(a);
KeyBoard bKeyborad = new BKeyBoard(b);
CPU ccpu = new CCPU(c);
RAM dRam = new DRAM(d);
Computer computer = new ThinkPad(aScreen, bKeyborad, cCpu, dRam);

我作爲電腦的使用者,我並不關心電腦是由哪些部分構成的,是怎麼組裝的。這些內容對使用者來說,顯然是多餘的。而且假如我在多處需要使用這個ThinkPad的實例,這段構造ThinkPad的代碼會出現在多個地方。某天ThinkPad的構成變了,使用的是fRAM,那我還要把散落在各個模塊的構造代碼挑出來,把參數dRam改爲fRam。

有的人可能會說,那我提供一個無參構造函數,把創建對象的細節都放裏面,這樣不也可以避免當入參發生變化時,不需要到處修改代碼,而且也屏蔽了創建細節。這樣確實是可以,但是實際上有多少個類的是可以無參構造的呢,這種情況就非常極端了。設計模式只是一個解決實際問題的參考,有沒有用要不要用都是結合實際情況決定,當然因爲其廣泛的適用性,所以被總結爲一個模式,類構成非常簡單且產品單一,當然可以不用工廠模式,沒必要鑽牛角尖。

下面我們從簡單工廠模式開始,一步步深入的學習簡單工廠模式,工廠方法模式,抽象工廠模式,看看工廠模式是怎麼樣一步步演變的。

簡單工廠模式

簡單工廠模式通常由三部分構成,經典寫法就是switch語句來決定返回的對象。

  • 抽象的產品接口(遵循依賴倒置原則,所有產品都實現了產品抽象接口)
  • 具象的產品類
  • 具象的工廠類

//抽象接口
public interface Computer {
    public void run();
}

//具象類
public class Mac implements  Computer {
    @Override
    public void run() {
        System.out.println("Mac Run");
    }
}

public class ThinkPad implements Computer {
    @Override
    public void run() {
        System.out.println("ThinkPad Run");
    }
}

//簡單工廠-告訴工廠想要什麼類型的電腦,工廠就給你什麼類型的電腦,新款電腦需要修改工廠類增加case分支
public class SimpleFactory {
    public static Computer createComputer(String Type){
        Computer computer = null;
        switch (Type){
            case "MAC": return new Mac();
            case "ThinPad":return new ThinkPad();
            default: throw new IllegalArgumentException("不存在的Computer類型");
        }
    }
}

//客戶端代碼
public class TestComputer {
    public static void main(String[] args)  {
        SimpleFactory.createComputer("ThinPad").run();
    }
}

在產品不多的情況下,我們可以接受使用簡單工廠模式。但是在產品類型比較多的時候,簡單工廠模式就不再適用。想象下,每增加一個產品,我們都需要修改簡單工廠(違背開閉原則),而且我們不停的往這個簡單工廠裏面添加代碼,越來越多,非常臃腫,可讀性和可維護性會變差。而且,我們需要不停的修改簡單工廠的代碼,違背了開閉原則(只擴展不修改原代碼)。這個時候,工廠方法模式就來了。

注1:順便提一下使用反射來創建實例可以實現不違反開閉原則的簡單工廠

    public static Computer createComputerByReflect(String type) 
            throws IllegalAccessException, 
                    InstantiationException, 
                   ClassNotFoundException {
        Class computerClass = Class.forName(type);
        return (Computer)computerClass.newInstance();
    }

    //客戶端代碼
    public class TestComputer {
    public static void main(String[] args) throws IllegalAccessException, ClassNotFoundException, InstantiationException {
        SimpleFactory.createComputerByReflect("test.open.Mac").run();
    }
}

注2:簡單工廠模式又稱靜態工廠模式,使用靜態方法,無需實例化簡單工廠。至於工廠方法模式和抽象工廠模式爲啥就不用靜態方法,個人理解是因爲這兩個模式對工廠進行了抽象(定了工廠接口),如果用了靜態方法,這些方法無法被重寫。

工廠方法模式

工廠方法模式,可以理解爲簡單工廠模式的加強版,對具象的工廠類做了一層抽象(構成多了一層工廠接口),不同產品的創建不再集中在一個工廠內,而是由各自專門的工廠類負責。新增一個產品,無需改動原有工廠類,只需要新增一個新的工廠類即可,在繼承了簡單工廠模式的優點之餘,也克服了簡單工廠模式違背開閉原則的問題。當然缺點就是類變多了,開發量增加了。

  • 抽象的產品接口
  • 抽象的工廠接口(爲了遵循開閉原則而做的抽象)
  • 具象的產品類
  • 具象的工廠類

//抽象的產品接口
public interface Computer {
    public void run();
}

//具象的產品類
public class Mac implements  Computer {

    @Override
    public void run() {
        System.out.println("Mac Run");
    }
}

public class ThinkPad implements Computer {

    @Override
    public void run() {
        System.out.println("ThinkPad Run");
    }
}

//抽象的工廠接口
public interface AbMethodFactory {
    Computer createComputer();
}

//具象的工廠類
public class MacFactory implements AbMethodFactory{
    @Override
    public Computer createComputer() {
        return new Mac();
    }
}

//具象的工廠類
public class ThinkPadFactory implements AbMethodFactory {

    @Override
    public Computer createComputer() {
        return new ThinkPad();
    }
}

//客戶端代碼
public class TestComputer {
    public static void main(String[] args){
        AbMethodFactory abMethodFactory = new MacFactory();
        Computer mac = abMethodFactory.createComputer();
        mac.run();
    }
}

抽象工廠模式

前面兩種工廠,生產的都是單一產品。實際上,有的時候我們會有生成一系列產品的需求,比如我要生產的是一個電腦套裝(一個產品系列),包含電腦,鼠標,鍵盤。當我們面臨多個產品組合協作的場景時,使用抽象工廠,我們可以保證客戶端使用的產品,都是如我們所希望的出自同一系列(比如 Mac電腦要接Type-C接口的鼠標),我只要切換工廠,就能實現配套措施的全部切換,改變整個系統的行爲。缺點是如果我要在產品族裏面再加一個產品,那麼我需要修改工廠接口,增加一個獲取新產品的方法抽象方法定義,所有具象工廠也要增加對應實現,違背了開閉原則。

抽象工廠的構成和工廠方法模式一致,但是其工廠內會提供多個方法用於獲取同一個系列的不同產品。

  • 抽象的產品接口
  • 抽象的工廠接口(定義多個方法生產同系列不同產品)
  • 具象的產品類
  • 具象的工廠類(提供多個方法生產同系列不同產品)

代碼只貼工廠接口和其中一個工廠吧,不然篇幅太冗長了。

public interface AbMethodFactory {
    //生產電腦
    Computer createComputer();
    //生產鼠標
    Mouse createMouse();
}

public class MacFactory implements AbMethodFactory{
    @Override
    public Computer createComputer() {
        return new Mac();
    }

    @Override
    public Mouse createMouse() {
        return new TypeCMouse();
    }

}

參考文章:

https://blog.csdn.net/fmyzc/article/details/79614944

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章