1. 程式人生 > >java靜態工廠方法與工廠模式

java靜態工廠方法與工廠模式

靜態工廠方法和工廠模式很像,但是在java中還是有很大區別的。

(一)靜態工廠方法vs工廠模式

對於一個類來說,為了讓客戶端獲得一個自身的例項,最常用的方法就是提供一個公有的構造器。除了這種使用構造器的方法之外,對於單個類來說,我們可以定義靜態工廠方法來獲取自身的類的一個例項。靜態工廠方法和工廠模式的不同也體現在此,靜態工廠方法是獲取這個類自身的一個例項,他的存在是為了更好的描述和處理這個類。而工廠模式的作用更在於解耦,讓每個類在例項化的時候不再使用new這種耦合度極高的方法。

我們通過靜態工廠方法來代替構造器,我們首先需要知道的是靜態工廠方法只是一個“普通的方法”。的確,我們將具有:返回這個物件的一個例項

這種特點的靜態方法叫做靜態工廠方法,它在理論上與其他方法並沒有什麼不同。但是也正是因為他是一種”普通的方法“,它具有很多構造器不具備的優點。

工廠方法又分為三種模式:簡單工廠模式、工廠模式(工廠方法模式)、抽象工廠模式。這三種是為了一個目標的不同程度的抽象。工廠方法用一種比靜態工廠方法更加系統的理論來對每一個需要使用的物件進行包裝。這種包裝下再也不使用new來建立物件,隨著抽象程度的變高,我們甚至可以將這種建立物件放到xml或者註釋之中——就像是那麼多框架做的一樣。

總而言之,靜態工廠方法在一個類的內部,較小的範圍裡可以讓你建立物件更加方便優雅,而工廠模式在大的範圍中能夠讓你的程式碼重用性更佳,耦合度更低。

(二)靜態工廠方法可以有不同的名字

對於普通的構造器來說,通過引數來對類中的不同屬性賦值,然後返回一個這個類的相應例項。但在有些時候,如果我們想獲得有些差別的類例項,唯一可以採用的方法是通過不同的構造器引數列表來區分不同的例項。但是這並不是一個好主意,因為有的時候,僅僅是構造器方法簽名上的不同,可能會讓客戶端使用者迷惑,只是引數順序的變化讓他們很難去記住到底哪個構造對應的是哪個例項

這個時候可以考慮使用靜態工廠方法,為不同的構造方法來起不同的名字來區分不同功能的例項化,而返回的都是一個this,這樣類似構造器的操作讓不同的例項可以被更加容易的區分。

(三)靜態工廠方法可以返回一個現有的例項

每當我們呼叫構造器,每當我們使用new來初始化一個物件時,都無疑是在堆上建立了一個新的物件。而有些不可變的類、不希望被例項出多個物件的類不用也不必每次都建立一個物件。通過靜態工廠方法,我們可以直接向客戶端返回一個我們早已建立好的物件,對於有些不可變的類,比如基本型別包裝類,這樣做可以極大地節省我們的開銷

對於單例模式的類,只允許每個類中存在一個已經例項化的物件。對於單例模式來說,構造器都會被宣告為private,我們不能也無法構造一個物件,只能通過靜態工廠方法來獲取他的物件。這種手法在很多很多包中都有體現,尤其是那些比較重量級的類(其實我們也恰好總喜歡把這種變數其名為factory),更需要這種手法來操作物件。

(四)靜態工廠方法可以作為檢視返回子型別

檢視這個名詞有很多解釋,在這裡檢視的解釋是java集合框架中的檢視方法——通過返回一個List(或者其他具體型別的祖先類,Set,Map等)物件,讓那些List的子類都可以來呼叫這個方法。

(五)簡單工廠方法

相比較靜態工廠方法可以便利一個類的例項化建立過程,工廠方法在一種模式的角度上,對所有需要new來例項化的類進行封裝解耦。提到過工廠方法分為三種:簡單工廠模式、工廠模式(工廠方法模式)、抽象工廠模式。這三種模式只是一種思維上的不斷抽象。我們先從簡單工廠方法來看,從簡單的入手。

package FactoryEx;

/**
 * 
 * @author QuinnNorris
 * 
 *         簡單工廠方法
 */
public class SimpleFactory {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub

        Apple apple = new Apple();
        Banana banana = new Banana();
    }

}

class Apple {

}

class Banana {

}

class Factory {

}

在沒有工廠方法的時候,我們建立一個類物件是通過這種new的方式,這樣的好處在於簡潔。但是如果我們在做一個很大很大的專案的時候,需要對專案中所有的Apple物件都改成Banana物件,這個時候就炸了,因為要去查詢所有的Apple物件一個個手動修改。我們現在要用工廠方法來取締這種程式碼緊密的結合的情況。

package FactoryEx;

/**
 * 
 * @author QuinnNorris
 * 
 *         簡單工廠方法
 */
public class SimpleFactory {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub

        Factory factory = new Factory();

        factory.FruitFactory("apple");

    }

}

/**
 * 
 * @author QuinnNorris
 * 
 *         各種水果的祖先類,我們要例項化產品的抽象類
 */
abstract class Fruit {

}

/**
 * 
 * @author QuinnNorris
 * 
 *         蘋果,我們要例項化的一種具體產品
 */
class Apple extends Fruit {

}

/**
 * 
 * @author QuinnNorris
 * 
 *         香蕉,我們要例項化的一種具體產品
 */
class Banana extends Fruit {

}

/**
 * 
 * @author QuinnNorris
 * 
 *         工廠類,這個類中決定最後給你一個蘋果還是香蕉
 */
class Factory {
    /**
     * 
     * @param fruit
     *            通過這個引數表達你的需求
     * @return 根據你的需求返回相應的水果
     */
    public Fruit FruitFactory(String fruit) {
        if (fruit.equals("apple"))
            return new Apple();
        if (fruit.equals("banana"))
            return new Banana();
        return null;
    }
}

簡單工廠方法分為這麼幾個部分:

  1. 抽象產品類:所有具體產品的抽象祖先,這個類的存在是為了在工廠類中可以返回這個類的子類提供方便。
  2. 具體產品類:我們需要例項化的不同的產品物件。
  3. 工廠類:通過工廠類,我們在“工廠”中按照要求打包不同的“水果”發貨送到“顧客”手中。
  4. 客戶端類:客戶端表達自己的需求,從工廠中獲取水果。

通過這種機制,我們獲得一個水果物件的具體過程不必再使用new,而是:

Factory factory = new Factory();

factory.FruitFactory("apple");

我們先建立一個工廠物件,如果你覺得這會讓我們建立兩個物件,我建議使用靜態方法。在建立了工廠之後,通過呼叫工廠方法傳入引數獲取我們想要的水果。在第二句中,返回值是一個Apple型別物件,我們可以直接在程式碼中當作apple使用它,如果我們想要將Apple換成Banana,只需要將引數變換成其他的。甚至,我們可以將這個引數提取出來,放到xml文件,或者其他儲存資訊的地方便於資訊的更改。這裡例項化出的類可以隨時改變,這樣不會產生new出來無法改變的侷限性。

這就是簡單工廠方法,用起來很舒服,增加了擴充套件性,又沒有去修改程式碼。但是簡單工廠方法存在的一個問題是:每當多一種水果,工廠類的方法中就要多一種if選擇情況,如果在數量非常多,實現內容非常長的情況下,這無疑讓整個類變得非常臃腫。為了解決這個問題——工廠方法模式出現了。

(六)工廠方法模式

工廠方法模式是簡單工廠模式的一種升級和抽象。工廠方法模式的核心邏輯是:通過將工廠類採用繼承的方式細分減輕在簡單模式中工廠類的壓力。通過不同的工廠來分別管理不同的產品,一種類似樹形的劃分方式,讓程式碼更加有可讀性。

package FactoryEx;

/**
 * 
 * @author QuinnNorris
 * 
 *         工廠方法模式
 */
public class FactoryMethord {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub

        Factory appleFactory = new AppleFactory();
        appleFactory.FruitFactory();

        Factory bananaFactory = new BananaFactory();
        bananaFactory.FruitFactory();
    }

}

/**
 * 
 * @author QuinnNorris
 * 
 *         各種水果的祖先類,我們要例項化產品的抽象類
 */
abstract class Fruit {

}

/**
 * 
 * @author QuinnNorris
 * 
 *         蘋果,我們要例項化的一種具體產品
 */
class Apple extends Fruit {

}

/**
 * 
 * @author QuinnNorris
 * 
 *         香蕉,我們要例項化的一種具體產品
 */
class Banana extends Fruit {

}

/**
 * 
 * @author QuinnNorris
 * 
 *         抽象工廠類,所有具體的工廠類的子類
 */
abstract class Factory {
    public abstract Fruit FruitFactory();
}

/**
 * 
 * @author QuinnNorris
 * 
 *         具體的工廠類,分管蘋果這個產品
 */
class AppleFactory extends Factory {
    @Override
    public Fruit FruitFactory() {
        return new Apple();
    }
}

/**
 * 
 * @author QuinnNorris
 * 
 *         具體的工廠類,分管香蕉這個產品
 */
class BananaFactory extends Factory {
    @Override
    public Fruit FruitFactory() {
        return new Banana();
    }
}

工廠方法模式分為這麼幾個部分:

  1. 抽象產品類:所有具體產品的抽象祖先,這個類的存在是為了在工廠類中可以返回這個類的子類提供方便。
  2. 具體產品類:我們需要例項化的不同的產品物件。
  3. 抽象工廠類:所有具體工廠的抽象祖先,我們在這個祖先中定義工廠方法。
  4. 具體工廠類:通過具體的工廠類來分別獲取不同的水果。
  5. 客戶端類:客戶端表達自己的需求,從工廠中獲取水果。

可能在這個只有兩種水果的例子中使用工廠方法模式非常的蠢,但是這種模式在要例項化的內容很多的時候還是非常方便的。有的時候我們所有的需例項的類太多,或者整個框架中的類是一種樹形框架的時候,這種工廠方法模式能發揮它最大的作用。

(七)抽象工廠模式

抽象工廠模式和實際上是工廠方法的一種更加泛化的情況。在抽象工廠模式中,抽象產品可能是一個或多個,從而構成一個或多個產品族。 在只有一個產品族的情況下,抽象工廠模式就是工廠方法模式。

package FactoryEx;

/**
 * 
 * @author QuinnNorris
 * 
 *         抽象工廠方法
 */
public class AbstractFactory {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub

        Factory aFactory = new AppleEnglishFactory();
        aFactory.FruitFactory();
        aFactory.BookFactory();

        Factory bFactory = new BananaMathFactory();
        bFactory.FruitFactory();
        bFactory.BookFactory();
    }

}

/**
 * 
 * @author QuinnNorris
 * 
 *         各種書籍的祖先類
 */
abstract class Book {

}

/**
 * 
 * @author QuinnNorris
 * 
 *         從書籍抽象類繼承下來的子類,一種具體的產品
 */
class EnglishBook extends Book {

}

/**
 * 
 * @author QuinnNorris
 * 
 *         從書籍抽象類繼承下來的子類,一種具體的產品
 */
class MathBook extends Book {

}

/**
 * 
 * @author QuinnNorris
 * 
 *         各種水果的祖先類,我們要例項化產品的抽象類
 */
abstract class Fruit {

}

/**
 * 
 * @author QuinnNorris
 * 
 *         蘋果,我們要例項化的一種具體產品
 */
class Apple extends Fruit {

}

/**
 * 
 * @author QuinnNorris
 * 
 *         香蕉,我們要例項化的一種具體產品
 */
class Banana extends Fruit {

}

/**
 * 
 * @author QuinnNorris
 * 
 *         抽象工廠類,所有具體的工廠類的子類
 */
abstract class Factory {
    public abstract Fruit FruitFactory();

    public abstract Book BookFactory();
}

/**
 * 
 * @author QuinnNorris
 * 
 *         具體的工廠類,分管蘋果和英語書這兩個產品
 */
class AppleEnglishFactory extends Factory {

    @Override
    public Fruit FruitFactory() {
        return new Apple();
    }

    @Override
    public Book BookFactory() {
        return new EnglishBook();
    }
}

/**
 * 
 * @author QuinnNorris
 * 
 *         具體的工廠類,分管香蕉和數學書這兩個產品
 */
class BananaMathFactory extends Factory {
    @Override
    public Fruit FruitFactory() {
        return new Banana();
    }

    @Override
    public Book BookFactory() {
        return new MathBook();
    }
}

上面的例子和工廠方法模式很相似,但是不同在於在抽象工廠中,每個工廠的工作又多了一些:現在每個工廠不僅僅只負責給顧客(客戶端)打包送水果,而且還要給顧客發教科書。而且水果和書是完全來自兩個抽象類的,那麼我們把這種一個工廠涉及到多個產品組的模式稱作抽象工廠。在只有一個產品族的情況下,抽象工廠模式就是工廠方法模式。