1. 程式人生 > >設計模式-工廠方法模式

設計模式-工廠方法模式

集合類 tof col 進行 bool down urn DC 角色

在簡單工廠模式中,我們發現存在很多問題:

  • 由於工廠類集中了所有產品創建邏輯,一旦不能正常工作,整個系統都要受到影響。
  • 要新增產品類的時候,就要修改工廠類的代碼,違反了開放封閉原則(對擴展的開放,對修改的關閉)。
  • 簡單工廠模式由於使用了靜態工廠方法,造成工廠角色無法形成基於繼承的等級結構。

為了解決上述的問題,我們學習一種新的設計模式:工廠方法模式。

模式定義

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

設計原則

依賴倒置原則:要依賴抽象,不要依賴具體類。

聽起來像是針對接口編程,不針對實現編程,但是這個原則說明了:不能讓高層組件依賴底層組件,而且,不管高層或底層組件,兩者都應該依賴於抽象。例如,下圖中 Pizza 是抽象類,PizzaStore 和 Pizza 子類都依賴於 Pizza 這個抽象類。

技術分享圖片

模式類圖

技術分享圖片

工廠方法模式實例

問題描述:

每個地區的 PizzaStore 賣的 Pizza 雖然種類相同,但是都有自己的風味。一個客戶點了紐約的 cheese 種類的 Pizza 和在芝加哥點的相同種類的 Pizza 是不同的。要求設計出滿足條件的 PizzaStore。

問題的解決方案類圖

PizzaStore 有 orderPizza() 方法,顧客可以用它來下單。下單之後需要先使用 createPizza() 來制作 Pizza,這裏的 createPizza() 就是 factoryMethod(),不同的 PizzaStore 子類實現了不同的 createPizza()。
技術分享圖片

首先定義產品接口-披薩

package com.wpx.factorymethod;

/**
 * 定義產品接口-披薩
 */
public interface Pizza {
    public void make();
}

再定義具體的產品類-紐約風味奶酪披薩、紐約風味蔬菜披薩、芝加哥風味奶酪披薩、芝加哥風味蔬菜披薩

package com.wpx.factorymethod;

/**
 * 具體的產品類,實現產品接口-紐約風味奶酪披薩
 */
public class NYStyleCheesePizza implements Pizza{
    @Override
    public
void make() { System.out.println("制作紐約風味奶酪披薩"); } }
package com.wpx.factorymethod;

/**
 * 具體的產品類,實現產品接口-紐約風味蔬菜披薩
 */
public class NYStyleVeggiePizza implements Pizza {
    @Override
    public void make() {
        System.out.println("制作紐約風味蔬菜披薩");
    }
}
package com.wpx.factorymethod;

/**
 * 具體的產品類,實現產品接口-芝加哥風味奶酪披薩
 */
public class ChicagoStyleCheesePizza implements Pizza{
    @Override
    public void make() {
        System.out.println("制作芝加哥風味奶酪披薩");
    }
}
package com.wpx.factorymethod;

/**
 * 具體的產品類,實現產品接口-芝加哥風味蔬菜披薩
 */
public class ChicagoStyleVeggiePizza implements Pizza{
    @Override
    public void make() {
        System.out.println("制作芝加哥風味蔬菜披薩");
    }
}

定義工廠接口

package com.wpx.factorymethod;

/**
 * 定義工廠接口
 */
public interface PizzaStore {
    public Pizza orderPizza(String item);
}

接著定義兩個具體的工廠類,實現工廠接口

package com.wpx.factorymethod;

/**
 * 具體的工廠類,實現工廠接口-紐約工廠
 */
public class NYPizzaStore implements PizzaStore {
    @Override
    public Pizza orderPizza(String item) {
        Pizza pizza = null;
        if (item.equals("乳酪比薩")) {
            pizza = new NYStyleCheesePizza();
        } else if (item.equals("蔬菜披薩")) {
            pizza = new NYStyleVeggiePizza();
        } else {
            throw new UnsupportedOperationException();
        }
        pizza.make();
        return pizza;
    }
}
package com.wpx.factorymethod;

/**
 * 具體的工廠類,實現工廠接口-芝加哥工廠
 */
public class ChicagoPizzaStore implements PizzaStore {
    @Override
    public Pizza orderPizza(String item) {
        Pizza pizza = null;
        if (item.equals("乳酪比薩")) {
            pizza = new ChicagoStyleCheesePizza();
        } else if (item.equals("蔬菜披薩")) {
            pizza = new ChicagoStyleVeggiePizza();
        } else {
            throw new UnsupportedOperationException();
        }
        pizza.make();
        return pizza;
    }
}

最後對工廠方法模式進行測試,先從紐約工廠那裏來一份乳酪披薩,再從芝加哥工廠訂一份乳酪披薩。

package com.wpx.factorymethod;

/**
 * 測試工廠方法模式
 */
public class PizzaDemo {
    public static void main(String[] args) {
        PizzaStore nyStore = new NYPizzaStore();
        nyStore.orderPizza("乳酪比薩");
        PizzaStore chicagoStore = new ChicagoPizzaStore();
        chicagoStore.orderPizza("乳酪比薩");
    }
}

運行結果

制作紐約風味奶酪披薩
制作芝加哥風味奶酪披薩

Process finished with exit code 0

JDK中的工廠方法模式

Collection接口中的一段代碼:

Iterator<E> iterator();  

繼承Collction的List、Set等中有:

Iterator<E> iterator();  

接下來再看ArrayList中的代碼:

    public Iterator<E> iterator() {
        return new Itr();
    }

    private class Itr implements Iterator<E> {
        int cursor;       // index of next element to return
        int lastRet = -1; // index of last element returned; -1 if no such
        int expectedModCount = modCount;

        public boolean hasNext() {
            return cursor != size;
        }

        @SuppressWarnings("unchecked")
        public E next() {
            checkForComodification();
            int i = cursor;
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
        }

        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }

Itr類實現了Interator接口

public interface Iterator<E> {

    boolean hasNext();

    E next();

    void remove();
}

通過查看JDK源碼,我們就會明白這裏是怎麽應用的工廠方法模式了其中Iterator是抽象產品角色,Itr是Iterator下面的一個具體產品角色,List類應該是抽象工廠角色,ArrayList應該是具體工廠角色。如果我們需要在List下自定義一個集合類並給出相應的叠代方式,那麽我們只需要添加一個實現List接口的集合類,然後在增加一個叠代類實現Iterator接口就可以了。並不需要去修改JDK源碼的內容,滿足了開-閉原則。

總結

優點:

  • 更符合開-閉原則:新增一種產品時,只需要增加相應的具體產品類和相應的工廠子類即可
  • 符合單一職責原則:每個具體工廠類只負責創建對應的產品
  • 不使用靜態工廠方法,可以形成基於繼承的等級結構

缺點:

  • 添加新產品時,除了增加新產品類外,還要提供與之對應的具體工廠類,系統類的個數將成對增加,在一定程度上增加了系統的復雜度;同時,有更多的類需要編譯和運行,會給系統帶來一些額外的開銷
  • 雖然保證了工廠方法內的對修改關閉,但對於使用工廠方法的類,如果要更換另外一種產品,仍然需要修改實例化的具體工廠類
  • 一個具體工廠只能創建一種具體產品

設計模式-工廠方法模式