1. 程式人生 > >Java 設計模式情景分析——工廠方法模式

Java 設計模式情景分析——工廠方法模式

工廠方法模式(Factory Pattern),是建立型設計模式之一。其在我們平時開發中應用很廣泛,如 Android 中的 Activity 裡的各個生命週期方法,以 onCreate 為例,它就可以看作是一個工廠方法,我們在其中可以構造我們的 View 並通過 setContentView 返回 framework 處理等。

定義:定義一個用於建立物件的介面,讓子類決定例項化哪個類。

1.工廠方法模式的使用情景

在任何需要生成複雜物件的地方,都可以使用工廠方法模式,複雜物件適合使用工廠方法模式,用 new 就可以完成建立的物件無需使用工廠方法模式。

2.程式中使用工廠方法模式的優缺點

- 工廠方法模式
優點 降低了物件之間的耦合度,工廠模式依賴於抽象的架構,其將例項化的任務交由子類去完成,有非常好的擴充套件性。
缺點 每次為工廠方法新增新的產品時就要編寫一個新的產品類,同還要引入抽象層,必然會導致程式碼類結構的複雜化。

3.工廠方法模式的UML類圖

工廠方法模式的UML類圖

4.工廠方法模式的實現

對比UML類圖,我們可以寫出工廠方法模式的通用模式程式碼如下:

1.抽象工廠方法(核心),具體生產什麼由子類去實現:

public abstract class Factory {
    public abstract Product createProduct
(); }

2.具體工廠類(實現了具體業務邏輯):

public class ConcreteFactory extends Factory{
    @Override
    public Product createProduct() {
        return new ConcreteProductA(); // 返回具體的產品物件
    }
}

3.抽象產品類,由具體的產品類去實現:

public abstract class Product {
    public abstract void method();
}

4.具體產品類(包含ConcreteProductA、ConcreteProductB等):

public class ConcreteProductA extends Product {
    @Override
    public void method() {
        System.out.println("ConcreteProductA method");
    }
}

測試程式:

@Test
public void factoryTest() throws Exception {
    Factory factory = new ConcreteFactory();
    Product product = factory.createProduct();
    product.method();
}

輸出:ConcreteProductA method

這種方式比較常見,需要哪個就生產哪個,有時候還可以利用反射的方式更加簡潔地來生產具體產品物件,此時,需要在工廠方法的引數列表中傳入一個 Class 類來決定是哪一個產品類:

public abstract class Factory {
    /**
     * @param clz 產品物件類型別
     * @param <T> 具體的產品物件
     * @return
     */
    public abstract <T extends Product> T createProduct(Class<T> clz);
}

對應的具體工廠類則通過反射獲取類的例項即可:

public class ConcreteFactory extends Factory{
    @Override
    public <T extends Product> T createProduct(Class<T> clz) {
        Product p = null;
        try {
            p = (Product) Class.forName(clz.getName()).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return (T) p;
    }
}

再看一下測試程式碼的實現:

@Test
public void factoryTest() throws Exception {
    Factory factory = new ConcreteFactory();
    Product product = factory.createProduct(ConcreteProductA.class);
    product.method();
}

輸出:ConcreteProductA method

需要哪個類的物件傳入哪一個類即可,這種方法比較簡潔、動態。如果不喜歡這一種,也可以嘗試為每一個產品都定義一個具體的工廠,各司其職,像擁有多個工廠的方式我們稱為多工廠方法模式,同樣當我們的工廠類只有一個的時候,我們還可以簡化掉抽象類,只需要將對應的工廠方法給為靜態方法即可:

public class Factory {
    public static Product createProduct() {
        return new ConcreteProductA();
    }
}

像這樣的方式又稱為簡單工廠模式或者靜態工程模式,它是工廠模式的一個弱化版本。

5.Android系統原始碼中的應用情景

工廠方法模式應用很廣泛,開發中使用到的資料結構中就隱藏著對工廠方法模式的應用,例如 List、Set,List、Set 繼承自 Collection 介面,而 Collection 介面繼承於 Iterable 介面:

public interface Iterable<T> {
    Iterator<T> iterator();
}

這意味著 List、Set 介面也會繼承 iterator() 方法,下面以 ArrayList 為例進行分析:

ArrayList 中 iterator() 方法的實現就是構造並返回一個迭代器物件:

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
    public Iterator<E> iterator() {
        return new Itr();
    }
    // 迭代器
    private class Itr implements Iterator<E> {
        protected int limit = java.util.ArrayList.this.size;
        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 < limit;
        }
        @SuppressWarnings("unchecked")
        public E next() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            int i = cursor;
            if (i >= limit)
                throw new NoSuchElementException();
            Object[] elementData = java.util.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();
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            try {
                java.util.ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;
                limit--;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }
        // 程式碼省略
    }
    // 程式碼省略
}

其中的 iterator() 方法其實就相當於一個工廠方法,專為 new 物件而生,構造並返回一個具體的迭代器。