1. 程式人生 > >24種設計模式-抽象工廠模式(6)

24種設計模式-抽象工廠模式(6)

好了,我們繼續上一節課,上一節講到女媧造人,人是造出來了,世界時熱鬧了,可是低頭一看,都是清一色的型別,缺少關愛、仇恨、喜怒哀樂等情緒,人類的生命太平淡了,女媧一想,猛然一拍腦袋,Shit!忘記給人類定義性別了,那怎麼辦?抹掉重來,然後就把人類重新洗牌,準備重新開始製造人類。

由於先前的工作已經花費了很大的精力做為鋪墊,也不想從頭開始了,那先說人類(Product 產品類)怎麼改吧,好,有了,給每個人類都加一個性別,然後再重新制造,這個問題解決了,那八卦爐怎麼辦?只有一個呀,要麼生產出全都是男性,要不都是女性,那不行呀,有了,把已經有了一條生產線——八卦爐(工廠模式中的 Concrete Factory)拆開,於是女媧就使用了“八卦拷貝術”,把原先的八卦爐一個變兩個,並且略加修改,就成了女性八卦爐(只生產女性,一個具體工廠的實現類)和男性八卦爐(只生產男性,又一個具體工廠的實現類),這個過程的類圖如下:

先看人類(也就是產品)的類圖:


這個類圖也比較簡單,Java 的典型類圖,一個介面,幾個抽象類,然後是幾個實現類,沒啥多說的,其中三個抽象類在抽象工廠模式中是叫做產品等級,六個實現類是叫做產品族,這個也比較好理解,實現類嘛是真實的產品,一個叫產品,多了就叫產品族,然後再看工廠類:

其中抽象工廠只實現了一個 createHuman 的方法,目的是簡化實現類的程式碼工作量,這個在講程式碼的時候會說。這裡還使用了 Jdk 1.5 的一個新特性 Enum 型別,其實這個完全可以類的靜態變數來實現,但我想既然是學習就應該學有所獲得,即使你對這個模式非常瞭解,也可能沒用過 Enum 型別,也算是一個不同的知識點吧,我希望給大家講解,每次都有新的技術點提出來,每個人都會有一點的收穫就足夠了,然後在具體的專案中使用時,知道有這個技術點,然後上 baidu 狗狗一下就能解決問題。話題扯遠了,我們繼續類圖,完整的類圖如下,這個看不大清楚,其實就是上面那兩個類圖加起來,

然後類圖講解完畢,我們來看程式實現:

package com.cbf4life;
/**
*定義一個人類的統稱,問題出來了,剛剛定義的時候忘記定義性別了
*這個重要的問題非修改不可,否則這個世界上太多太多的東西不存在了
*/

public interface Human {
    //首先定義什麼是人類
    //人是愉快的,會笑的,本來是想用smile表示,想了一下laugh更合適,好長時間沒有大笑了;
    public	void laugh();
    
    //人類還會哭,代表痛苦
    public void cry();

    //人類會說話
    public void talk();

    //定義性別
    public void sex();
}

人類的介面定義好,然後根據介面建立三個抽象類,也就是三個產品等級,實現 laugh()、cry()、talk()三個方法,以 AbstractYellowHuman 為例:

package com.cbf4life.yellowHuman;
import com.cbf4life.Human;
/**
*為什麼要修改成抽象類呢?要定義性別呀
*/
public abstract class AbstractYellowHuman implements Human {

    public void cry() {
        System.out.println("黃色人種會哭");
    }

    public void laugh() { 
       System.out.println("黃色人種會大笑,幸福呀!");
    }

    public void talk() {
        System.out.println("黃色人種會說話,一般說的都是雙位元組");
    }
}

其他的兩個抽象類 AbstractWhiteHuman 和 AbstractgBlackHuman 與此類似的事項方法,不再通篇拷貝程式碼

       三個抽象類都實現完畢了,然後就是些實現類了。其實,你說抽象類放這裡有什麼意義嗎?就是不允許你 new 出來一個抽象的物件唄,使用非抽象類完全就可以代替,呵呵,殺豬殺尾巴,各有各的殺法,不過既然進了 Java 這個門就要遵守 Java 這個規矩,我們看實現類:

        女性黃種人的實現類:

package com.cbf4life.yellowHuman;
/**
*女性黃種人
*/
public class YellowFemaleHuman extends AbstractYellowHuman {
    public void sex() {
        System.out.println("該黃種人的性別為女...");
    }
}

男性黃種人的實現類:

package com.cbf4life.yellowHuman;

/**
*男性黃種人
*/
public class YellowMaleHuman extends AbstractYellowHuman {
    public void sex() {
        System.out.println("該黃種人的性別為男....");
    }
}

同理可知,女性白種人,男性白種人,女性黑種人,男性黑種人都是對性別的實現。

抽象工廠模式下的產品等級和產品族都已經完成,也就是人類以及產生出的人類是什麼樣子的都已經定義好了,下一步就等著工廠開工建立了,那我們來看工廠類。在看工廠類之前我們先看那個列舉型別,這個是很有意思的

package com.cbf4life;
/**
*世界上有哪些型別的人,列出來
*JDK 1.5開始引入enum型別也是目的的,吸引C程式設計師轉過來
*/

public enum HumanEnum {

   //把世界上所有人型別都定義出來
    YelloMaleHuman("com.cbf4life.yellowHuman.YellowMaleHuman"),
    YelloFemaleHuman("com.cbf4life.yellowHuman.YellowFemaleHuman"),
    WhiteFemaleHuman("com.cbf4life.whiteHuman.WhiteFemaleHuman"),
    WhiteMaleHuman("com.cbf4life.whiteHuman.WhiteMaleHuman"),
    BlackFemaleHuman("com.cbf4life.blackHuman.BlackFemaleHuman"),
    BlackMaleHuman("com.cbf4life.blackHuman.BlackMaleHuman");
    private String value = "";

    //定義建構函式,目的是Data(value)型別的相匹配
    private HumanEnum(String value){
        this.value = value;
    }

    public String getValue(){ 
        return this.value;
    }
}

然後,我們看我們的工廠類,先看介面:

package com.cbf4life;

/**
*這次定一個介面,應該要造不同性別的人,需要不同的生產線
*那這個八卦爐必須可以製造男人和女人
*/
public interface HumanFactory {

    //製造黃色人種
    public Human createYellowHuman();

    //製造一個白色人種
    public Human createWhiteHuman();

    //製造一個黑色人種
    public Human createBlackHuman();

}

然後看抽象類:

package com.cbf4life.humanFactory;

import com.cbf4life.Human;
import com.cbf4life.HumanEnum;
import com.cbf4life.HumanFactory;

public abstract class AbstractHumanFactory implements HumanFactory {
/*
*給定一個性別人種,建立一個人類出來 專業術語是產生產品等級
*/
    protected Human createHuman(HumanEnum humanEnum) { Human human = null;
        //如果傳遞進來不是一個Enum中具體的一個Element的話,則不處理
        if (!humanEnum.getValue().equals("")) {
            try {
                //直接產生一個例項
                human = (Human)
                Class.forName(humanEnum.getValue()).newInstance();
            } catch (Exception e) {
                //因為使用了enum,這個種異常情況不會產生了,除非你的enum有問題;
                e.printStackTrace();
              }
        }//if結束
    return human;
    }
}
看到沒,這就是引入 enum 的好處,createHuman(HumanEnum humanEnum)這個方法定義了輸入引數必須是 HumanEnum 型別,然後直接使用 humanEnum.getValue()方法就能獲得具體傳遞進來的值,這個不多說了,
大家自己看程式領會,沒多大難度,這個抽象類的目的就是減少下邊實現類的程式碼量,我們看實現類:
男性工廠,只建立男性:
package com.cbf4life.humanFactory;

import com.cbf4life.Human;
import com.cbf4life.HumanEnum;

/**
*男性建立工廠
*/
    public class MaleHumanFactory extends AbstractHumanFactory {
        //建立一個男性黑種人
        public Human createBlackHuman() {
                return super.createHuman(HumanEnum.BlackMaleHuman);
        }

        //建立一個男性白種人
        public Human createWhiteHuman() {
                return super.createHuman(HumanEnum.WhiteMaleHuman);
        }

        //建立一個男性黃種人
        public Human createYellowHuman() {
                return super.createHuman(HumanEnum.YelloMaleHuman);
        }

}

女性工廠,只建立女性:

package com.cbf4life.humanFactory;

import com.cbf4life.Human;
import com.cbf4life.HumanEnum;

/**
*女性建立工廠
*/
public class FemaleHumanFactory extends AbstractHumanFactory {

    //建立一個女性黑種人
    public Human createBlackHuman() {
        return super.createHuman(HumanEnum.BlackFemaleHuman);
    }

    //建立一個女性白種人
    public Human createWhiteHuman() {
        return super.createHuman(HumanEnum.WhiteFemaleHuman);
    }

    //建立一個女性黃種人
    public Human createYellowHuman() {
        return super.createHuman(HumanEnum.YelloFemaleHuman);
    }
}

產品定義好了,工廠也定義好了,萬事俱備只欠東風,那咱就開始造吧,哦,不對,女媧開始造人了

public class NvWa {
    public static void main(String[] args) {
        //第一條生產線,男性生產線
        HumanFactory maleHumanFactory = new MaleHumanFactory();

        //第二條生產線,女性生產線
        HumanFactory femaleHumanFactory = new FemaleHumanFactory();

        //生產線建立完畢,開始生產人了:
        Human maleYellowHuman = maleHumanFactory.createYellowHuman();
        Human femaleYellowHuman = femaleHumanFactory.createYellowHuman();
        maleYellowHuman.cry();
        maleYellowHuman.laugh();
        femaleYellowHuman.sex();
        /*
        *.....
        *後面你可以續了
        */
    }
}

兩個八卦爐,一個造女的,一個造男的,開足馬力,一直造到這個世界到現在這個模式為止。

抽象工廠模式講完了,那我們再思考一些問題:工廠模式有哪些優缺點?先說優點,我這人一般先看人優點,非常重要的有點就是,工廠模式符合 OCP 原則,也就是開閉原則,怎麼說呢,比如就性別的問題,

這個世界上還存在雙性人,是男也是女的人,那這個就是要在我們的產品族中增加一類產品,同時再增加一個工廠就可以解決這個問題,不需要我再來實現了吧,很簡單的大家自己畫下類圖,然後實現下。

那還有沒有其他好處呢?抽象工廠模式,還有一個非常大的有點,高內聚,低耦合,在一個較大的專案組,產品是由一批人定義開發的,但是提供其他成員訪問的時候,只有工廠方法和產品的介面,也就是說只需要提供 Product Interface 和 Concrete Factory 就可以產生自己需要的物件和方法,Java 的高內聚低耦合的特性表現的一覽無遺,