Java設計模式-工廠模式

1、簡單工廠

定義

簡單工廠其實並不屬於23種GOF設計模式之一,該模式是工廠方法模式的弱化(或者說是工廠方法模式的一種特例),由於簡單,因此稱爲簡單工廠模式(Simple Factory Pattern),也叫作靜態工廠模式。雖然不是"標準"的設計模式(更像是一種編程習慣),但在實際項目中,採用該方法的案例仍是比較多的。java

簡單工廠模式沒有嚴格的定義,咱們姑且使用如下描述:git

提供一個建立對象實例的功能,而無須關心其具體實現。被建立實例的類型能夠是接口、抽象類,也能夠是具體的類編程

關鍵點

  • 具體的工廠類
  • 建立產品的工廠方法
    • 靜態方法(靜態工廠名字的由來)
    • 一般會有一個"類型"參數(還有一種是能夠提供多個靜態工廠方法並經過不一樣方法名區別要建立的產品)
    • 根據參數,利用if或者switch建立產品並返回

實現

抽象產品類(接口或抽象類)

public interface Product {
    void doSomething();
    void doAnything();
}
複製代碼

具體產品類

public class ConcreteProductA implements Product {
    @Override
    public void doSomething() {
        System.out.println("ConcreteProductA doSomething");

    }

    @Override
    public void doAnything() {
        System.out.println("ConcreteProductA doAnything");
    }
}
複製代碼
public class ConcreteProductB implements Product {
    @Override
    public void doSomething() {
        System.out.println("ConcreteProductB doSomething");

    }

    @Override
    public void doAnything() {
        System.out.println("ConcreteProductB doAnything");
    }
}
複製代碼

工廠類

public class Creator {

    public static Product createProduct(String type) {
        Product product = null;
        switch (type) {
            case "A":
                product = new ConcreteProductA();
                break;
            case "B":
                product = new ConcreteProductB();
                break;
        }
        return product;
    }
}
複製代碼

客戶端代碼

public class Client {

    public static void main(String[] args) {
        Product productA = Creator.createProduct("A");
        productA.doSomething();
        productA.doAnything();
      
        Product productB = Creator.createProduct("B");
        productB.doSomething();
        productB.doAnything();
    }

}
複製代碼

優勢

  • 簡單
  • 解耦
    • 客戶端不須要依賴具體的產品類,只依賴工廠類,傳入不一樣的參數就能夠獲得不一樣的對象;
    • 工廠方法能夠根據用戶條件返回同一類型的不一樣子類;
    • 工廠方法中能夠增長額外建立對象的細節,而且對客戶端屏蔽這些細節。

缺點

工廠類的擴展比較困難,每增長一個產品,就要在工廠中添加相應的分支,對擴展開放的同時對修改也開放了,不符合開閉原則。若是有不少產品,那麼工廠方法會顯得特別"臃腫",下降可讀性且不易維護。設計模式

使用場景

  • new的替代品,須要用new建立對象的地方均可以考慮簡單工廠;
  • 產品種類較少,而且基本能夠預見不須要太多擴展的狀況(產品需求基本已經肯定,例如計算器,已知的運算類型大概就那麼多種)

2、工廠方法

定義

Define an interface for creating an object,but let subclasses decide which class toinstantiate.Factory Method lets a class defer instantiation to subclasses.markdown

定義一個用於建立對象的接口,讓子類決定實例化哪個類。工廠方法使一個類的實例化延遲到其子類。框架

實現

抽象產品類

public interface Product {
    void doSomething();
    void doAnything();
}
複製代碼

具體產品類

public class ConcreteProductA implements Product {
    @Override
    public void doSomething() {
        System.out.println("ConcreteProductA doSomething");

    }

    @Override
    public void doAnything() {
        System.out.println("ConcreteProductA doAnything");
    }
}
複製代碼
public class ConcreteProductB implements Product {
    @Override
    public void doSomething() {
        System.out.println("ConcreteProductB doSomething");

    }

    @Override
    public void doAnything() {
        System.out.println("ConcreteProductB doAnything");
    }
}
複製代碼

抽象工廠類

public interface Creator {
    Product createProduct();
}
複製代碼

具體工廠類

public class ConcreteCreatorA implements Creator {
    @Override
    public Product createProduct() {
        return new ConcreteProductA();
    }
}
複製代碼
public class ConcreteCreatorB implements Creator {
    @Override
    public Product createProduct() {
        return new ConcreteProductB();
    }
}
複製代碼

客戶端代碼

public static void main(String[] args) {
        Creator creatorA = new ConcreteCreatorA();
        Product productA = creatorA.createProduct();
        productA.doSomething();
        productA.doAnything();

        Creator creatorB = new ConcreteCreatorB();
        Product productB = creatorB.createProduct();
        productB.doSomething();
        productB.doAnything();
    }
複製代碼

優勢

  • 良好的封裝性,代碼結構清晰編輯器

    一個對象建立是有條件約束的,如一個調用者須要一個具體的產品對象,只要知道這個產品的類名(或約束字符串)就能夠了,不用知道建立對象的艱辛過程,下降模塊間的耦合。ide

  • 工廠方法模式的擴展性很是優秀oop

    在增長產品類的狀況下,只要適當地修改具體的工廠類或擴展一個工廠類,就能夠完成「擁抱變化」。spa

  • 工廠方法模式是典型的解耦框架

  • 高層模塊值須要知道產品的抽象類,其餘的實現類都不用關心,符合迪米特法則,我不須要的就不要去交流;也符合依賴倒置原則,只依賴產品類的抽象;固然也符合里氏替換原則,使用產品子類替換產品父類。

缺點

每增長一個產品類,就須要增長一個對應的工廠類,增長了額外的開發量。

擴展

利用反射機制來解決"每增長一個產品類,就須要增長一個對應的工廠類"的問題

抽象工廠類

public interface Creator {
    <T extends Product> T createProduct(Class<T> clazz);
}
複製代碼

具體工廠類

public class ConcreteCreator implements Creator {
    @Override
    public <T extends Product> T createProduct(Class<T> clazz) {
        Product product= null;
        try {
            product = (Product) Class.forName(clazz.getName()).newInstance();
        } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
            e.printStackTrace();
        }
        return (T) product;
    }
}
複製代碼

客戶端代碼

public class Client {
    public static void main(String[] args) {
        Creator creator = new ConcreteCreator();

        Product productA = creator.createProduct(ConcreteProductA.class);
        productA.doSomething();
        productA.doAnything();

        Product productB = creator.createProduct(ConcreteProductB.class);
        productB.doSomething();
        productB.doAnything();
    }
}
複製代碼

工廠方法 VS 簡單工廠

工廠方法模式是對簡單工廠的進一步抽象和解耦。和簡單工廠比:

  • 工廠方法模式多了一個抽象工廠類
  • 每一個產品都對應一個具體的工廠類
  • 工廠方法模式把簡單工廠的內部邏輯判斷轉移到了客戶端代碼來進行。各個實例對象的建立代碼,沒有耦合在同一個工廠類裏。
  • 工廠方法模式更易於擴展,克服了簡單工廠會違背開閉原則的缺點,又保持了封裝對象建立過程的優勢。

3、抽象工廠

定義

Provide an interface for creating families of related or dependent objects without specifyingtheir concrete classes.

爲建立一組相關或相互依賴的對象提供一個接口,並且無須指定它們的具體類。

實現

抽象產品類

產品A家族

public interface ProductA {
    void doSomething();
    void doAnything();
}
複製代碼

產品B家族

public interface ProductB {
    void doSomething();
    void doAnything();
}
複製代碼

具體產品類

產品A家族,產品等級1

public class ConcreteProductA1 implements ProductA {
    @Override
    public void doSomething() {
        System.out.println("ConcreteProductA1 doSomething");
    }

    @Override
    public void doAnything() {
        System.out.println("ConcreteProductA1 doAnything");
    }
}
複製代碼

產品A家族,產品等級2

public class ConcreteProductA2 implements ProductA {
    @Override
    public void doSomething() {
        System.out.println("ConcreteProductA2 doSomething");
    }

    @Override
    public void doAnything() {
        System.out.println("ConcreteProductA2 doAnything");
    }
}
複製代碼

產品B家族,產品等級2

public class ConcreteProductB1 implements ProductB {
    @Override
    public void doSomething() {
        System.out.println("ConcreteProductB1 doSomething");
    }

    @Override
    public void doAnything() {
        System.out.println("ConcreteProductB1 doAnything");
    }
}
複製代碼

產品B家族,產品等級2

public class ConcreteProductB2 implements ProductB {
    @Override
    public void doSomething() {
        System.out.println("ConcreteProductB2 doSomething");
    }

    @Override
    public void doAnything() {
        System.out.println("ConcreteProductB2 doAnything");
    }
}
複製代碼

抽象工廠類

public interface Creator {
    /** * 建立A產品家族 * @return */
    ProductA createProductA();

    /** * 建立B產品家族 * @return */
    ProductB createProductB();

    // ...
    // 有N個產品族,在抽象工廠類中就應該有N個建立方法
}
複製代碼

有N個產品族,在抽象工廠類中就應該有N個建立方法

具體工廠類

public class ConcreteCreator1 implements Creator {
    @Override
    public ProductA createProductA() {
        return new ConcreteProductA1();
    }

    @Override
    public ProductB createProductB() {
        return new ConcreteProductB1();
    }
}
複製代碼
public class ConcreteCreator2 implements Creator {
    @Override
    public ProductA createProductA() {
        return new ConcreteProductA2();
    }

    @Override
    public ProductB createProductB() {
        return new ConcreteProductB2();
    }
}
複製代碼

有M個產品等級,就應該有M個具體工廠實現

客戶端代碼

public class Client {
    public static void main(String[] args) {
        Creator creator1 = new ConcreteCreator1();

        ProductA productA1 = creator1.createProductA();
        productA1.doSomething();
        productA1.doAnything();

        ProductB productB1 = creator1.createProductB();
        productB1.doSomething();
        productB1.doAnything();

        Creator creator2 = new ConcreteCreator2();

        ProductA productA2 = creator2.createProductA();
        productA2.doSomething();
        productA2.doAnything();

        ProductB productB2 = creator2.createProductB();
        productB2.doSomething();
        productB2.doAnything();
    }
}
複製代碼

優勢

  • 封裝性,高層模塊不須要關心每一個產品的實現類,不關心對象是如何建立出來的,只要知道工廠類是誰,就能建立出一個須要的對象

  • 一個產品族中的多個對象被設計成一塊兒工做時,它能保證客戶端始終只使用同一個產品族中的對象,而且很容易交換產品系列

  • 能夠定義產品族內的約束,而且這樣的約束對高層模塊來講是透明的

缺點

抽象工廠模式的最大缺點就是產品族擴展很是困難,以上面的通用代碼爲例,若是要增長一個產品C,也就是說產品家族由原來的2個增長到3個,抽象工廠類Creator要增長一個方法createProductC(),而後每一個實現類都要修改,違反了開閉原則。

注意是產品族擴展困難,而不是產品等級,若是新增一個產品等級,只需增長一個具體工廠類的實現便可完成擴展

抽象工廠 VS 工廠方法

抽象工廠模式是工廠方法模式的升級版本,在有多個業務品種、業務分類時,經過抽象工廠模式產生須要的對象是一種很是好的解決方式。

工廠方法模式生產一個產品,抽象工廠模式生產多個產品(一系列產品);在編程中,一般表現爲一個接口或者抽象類,也就是說,工廠方法模式提供的全部產品都是衍生自同一個接口或抽象類,而抽象工廠模式所提供的產品則是衍生自不一樣的接口或抽象類。

4、總結

簡單工廠、工廠方法、抽象工廠這三種模式是逐步抽象的,後者適用於更爲通常的場景,而前者是後者的特例。但它們的目標是異曲同工的,目的都是靈活地建立所需的對象而且對客戶端隱藏對象建立細節,三者的擴展性和開發量有所不一樣,能夠根據實際狀況選擇合適的模式來代替以new的方式建立對象的過程:

  • 簡單工廠適用於產品種類較少,且不須要太多擴展的場景

  • 工廠方法模式做爲簡單工廠的進一步抽象和補充,更加適用於有不少擴展需求的場景

  • 若是一個產品族都有相同的約束(在有多個業務品種、業務分類時,即:具備產品族&產品等級結構的概念),則可使用抽象工廠模式

    例如一個文本編輯器和一個圖片處理器,都是軟件實體,可是*nix下的文本編輯器和Windows下的文本編輯器雖然功能和界面都相同,可是代碼實現是不一樣的,圖片處理器也有相似狀況。也就是具備了共同的約束條件:操做系統類型。因而咱們可使用抽象工廠模式,產生不一樣操做系統下的編輯器和圖片處理器。

源碼地址:gitee.com/tianranll/j…

參考文獻:《設計模式之禪》、《大話設計模式》

相關文章
相關標籤/搜索