工廠模式:優(yōu)雅地生成多種類型對(duì)象

工廠模式是一種非常常用的**創(chuàng)建型設(shè)計(jì)模式**,其提供了創(chuàng)建對(duì)象的最佳方式。在創(chuàng)建對(duì)象時(shí),不會(huì)對(duì)客戶端暴露對(duì)象的創(chuàng)建邏輯,而是通過(guò)使用共同的接口來(lái)創(chuàng)建對(duì)象。通過(guò)使用工廠模式,在業(yè)務(wù)代碼中可以靈活的操控生成的實(shí)例對(duì)象。

工廠模式主要包含以下三種實(shí)現(xiàn):**簡(jiǎn)單工廠、工廠方法及抽象工廠**。下面我們來(lái)逐一了解這三種工廠方法的實(shí)現(xiàn)與異同。

簡(jiǎn)單工廠

工廠模式中,最簡(jiǎn)單易懂的就是簡(jiǎn)單工廠方法。通俗點(diǎn)來(lái)說(shuō),簡(jiǎn)單工廠的核心思想就是:“你告訴我你需要什么,我就為你生產(chǎn)什么”。這里舉一個(gè)游戲的簡(jiǎn)單例子。一個(gè)游戲中角色分別有戰(zhàn)士、法師、精靈。而我們需要設(shè)計(jì)一個(gè)相應(yīng)的鐵匠鋪。根據(jù)不同的角色,來(lái)鍛造不同的武器。(假設(shè)戰(zhàn)士是劍、法師是法杖,而精靈是弓)

那么編程思路其實(shí)很簡(jiǎn)單,首先我們需要設(shè)計(jì)一個(gè)Player的基類對(duì)象,并定義一個(gè)基本的玩家類型Type字段,用于判斷當(dāng)前玩家是什么角色。然后分別定義出戰(zhàn)士、法師和精靈的對(duì)象。并對(duì)getType的方法進(jìn)行覆蓋。簡(jiǎn)單類圖如下:
具體類方法如下:
//玩家類
public class Player {
    String name;
    int type;

    public Player(String name) {
        this.name = name;
    }
}
public class Warrior extends Player {

    @Override
    public int getType() {
        return PlayerEnum.WARRIOR.getCode();
    }
}
在定義完玩家類以后,我們就可以設(shè)計(jì)咱們的鐵匠鋪類了。簡(jiǎn)單來(lái)說(shuō),鐵匠鋪只需要調(diào)用getType方法判斷當(dāng)前玩家的角色,并鍛造武器即可。
@Slf4j
public class BlackSmithShopSimpleFactory {

    public static Weapon createWeapon(Player player) {
        //簡(jiǎn)單工廠中設(shè)計(jì)判斷即可
        Weapon weapon = null;
        if (player.getType() == WARRIOR.getCode()) {
            weapon = new Sword();
        } else if (player.getType() == ELF.getCode()) {
            weapon = new Bow();
        } else if (player.getType() == MAGE.getCode()) {
            weapon = new Staff();
        }
        return weapon;
    }

    public static void main(String[] args) {
        Player player1 = new Warrior();
        Weapon weapon1 = createWeapon(player1);
        System.out.println("player1從鐵匠鋪獲取到的武器是:"+weapon1.getDesc());

        Player player2 = new Mage();
        Weapon weapon2 = createWeapon(player2);
        System.out.println("player2從鐵匠鋪獲取到的武器是:"+ weapon2.getDesc());

        Player player3 = new Elf();
        Weapon weapon3 = createWeapon(player3);
        System.out.println("player3從鐵匠鋪獲取到的武器是:"+ weapon3.getDesc());
    }
}
最終的輸出結(jié)果如下:
可以看到,簡(jiǎn)單工廠在對(duì)應(yīng)不同玩家的時(shí)候都能生成出相應(yīng)的對(duì)象,實(shí)現(xiàn)了靈活的機(jī)制。但是需要注意的是,簡(jiǎn)單工廠其實(shí)是違背了**開(kāi)閉原則**的。例如我們可能需要新增玩家角色 - 刺客,其使用的武器是飛鏢,那么此時(shí)我們就需要修改鐵匠鋪的代碼,新增上飛鏢這種類型。

簡(jiǎn)單工廠代碼雖然違反了開(kāi)閉原則且可能需要頻繁增加生成代碼,但是勝在簡(jiǎn)單易實(shí)現(xiàn),且可讀性較好。大多數(shù)時(shí)候都能勝任問(wèn)題。

工廠方法

工廠方法模式是對(duì)簡(jiǎn)單工廠模式的進(jìn)一步深化,其不像簡(jiǎn)單工廠模式通過(guò)一個(gè)工廠來(lái)完成所有對(duì)象的創(chuàng)建,而是**通過(guò)不同的工廠來(lái)創(chuàng)建不同的對(duì)象**,每個(gè)對(duì)象有對(duì)應(yīng)的工廠創(chuàng)建。

依舊是以上面的游戲?yàn)槔?,假設(shè)游戲當(dāng)前不再針對(duì)職業(yè)進(jìn)行武器的限制了。玩家可以任意地選擇合適的武器,那么此時(shí)就可以采用工廠方法進(jìn)行改造。
首先,定義一個(gè)抽象類或接口,其中規(guī)定咱們的創(chuàng)建武器的方法。然后,分別定義生產(chǎn)法杖、生產(chǎn)弓和生產(chǎn)劍的鐵匠鋪。并實(shí)現(xiàn)對(duì)應(yīng)的方法。緊接著,根據(jù)玩家的具體需要去生成相應(yīng)的武器即可。選擇工廠并生產(chǎn)的代碼如下: 
public class BlackSmithShopFactoryPattern {

    public static void main(String[] args) {
        Player player = new Player();

        //生產(chǎn)劍的鐵匠鋪
        WeaponShop swordShop = new SwordShop();
        Weapon weapon = swordShop.createWeapon();
        System.out.println("玩家選擇的武器為:"+weapon.getDesc());

        //生產(chǎn)弓的鐵匠鋪
        WeaponShop staffShop = new StaffShop();
        Weapon weapon1 = staffShop.createWeapon();
        System.out.println("玩家選擇的武器為:"+weapon1.getDesc());

        //生產(chǎn)法杖的鐵匠鋪
        BowShop bowShop = new BowShop();
        Weapon weapon2 = bowShop.createWeapon();
        System.out.println("玩家選擇的武器為:"+weapon2.getDesc());
    }
}
最終的結(jié)果如下:
工廠方法的優(yōu)勢(shì)在于擴(kuò)展性相對(duì)比較好,當(dāng)需要新增工廠的時(shí)候,只需要進(jìn)行相應(yīng)的拓展即可實(shí)現(xiàn)。例如我們?nèi)绻略鑫淦?,只需要?xiě)一個(gè)類再實(shí)現(xiàn)相應(yīng)的生產(chǎn)武器的方法即可。

但是,問(wèn)題在于過(guò)多的類很可能會(huì)影響整個(gè)系統(tǒng)的可讀性,增大系統(tǒng)的復(fù)雜度。

抽象工廠

抽象工廠,其實(shí)是工廠方法的拓展。上述無(wú)論是工廠方法還是簡(jiǎn)單工廠,都是針對(duì)一個(gè)對(duì)象進(jìn)行設(shè)計(jì)和封裝,但是實(shí)際情況中往往會(huì)存在一些連帶的情況。還是以上述游戲的情況為例子。假設(shè)當(dāng)前我們要打造的不再是單一的武器,而是需要打造對(duì)應(yīng)的武器和盔甲。同時(shí),武器和盔甲必須成套打造才有相應(yīng)的屬性加成。那么這個(gè)時(shí)候,工廠方法和簡(jiǎn)單工廠就比較難滿足我們的需求了。

針對(duì)這個(gè)情況,我們可以設(shè)計(jì)相應(yīng)的抽象工廠解決。這里我們首先假定套裝有兩套,黑龍?zhí)籽b和紅龍?zhí)籽b。

首先咱們先設(shè)計(jì)一個(gè)抽象的套裝工廠,其兩個(gè)方法分別是生產(chǎn)武器和盔甲。
public abstract class SuitShop {
    public abstract Weapon createWeapon();

    public abstract Armor createArmor();
}
隨后根據(jù)職業(yè)和套裝指定相應(yīng)的工廠實(shí)現(xiàn)子類。(這里僅僅實(shí)現(xiàn)了戰(zhàn)士的工廠實(shí)現(xiàn)子類,其余角色的子類實(shí)現(xiàn)類似)
public class RedDragonWarriorSuitShop extends SuitShop{
    @Override
    public Weapon createWeapon() {
        return new RedDragonSword();
    }

    @Override
    public Armor createArmor() {
        return new RedDragonPlate();
    }
}
public class BlackDragonWarriorSuitShop extends SuitShop{

    @Override
    public Weapon createWeapon() {
        return new BlackDragonSword();
    }

    @Override
    public Armor createArmor() {
        return new BlackDragonPlate();
    }
}
除了工廠,我們還要定義對(duì)應(yīng)的黑龍?zhí)籽b和紅龍?zhí)籽b對(duì)應(yīng)的武器和盔甲的子類。
public class BlackDragonSword extends Sword{
    @Override
    public String getDesc() {
        return "黑龍劍";
    }
}
public class BlackDragonPlate extends Plate{
    @Override
    public String getDesc() {
        return "黑龍板甲";
    }
}
public class RedDragonPlate extends Plate{
    @Override
    public String getDesc() {
        return "紅龍板甲";
    }
}
public class RedDragonSword extends Sword{
    @Override
    public String getDesc() {
        return "紅龍劍";
    }
}
最終定義的整體類圖如下所示:
public class BlackSmithShopAbstractFactory {

    public static void main(String[] args) {
        SuitShop suitShop = new BlackDragonWarriorSuitShop();
        Weapon weapon = suitShop.createWeapon();
        Armor armor = suitShop.createArmor();

        System.out.println("玩家打造的套裝是:"+weapon.getDesc()+"和"+armor.getDesc());

        SuitShop redDragonWarriorSuitShop = new RedDragonWarriorSuitShop();
        Weapon weapon1 = redDragonWarriorSuitShop.createWeapon();
        Armor armor1 = redDragonWarriorSuitShop.createArmor();
        System.out.println("玩家打造的套裝是:"+weapon1.getDesc()+"和"+armor1.getDesc());
    }
}
在main方法中定義相應(yīng)的邏輯,然后執(zhí)行得到如下結(jié)果。
總的來(lái)說(shuō),抽象工廠主要是針對(duì)多個(gè)類型對(duì)象的時(shí)候使用的方法。但其缺點(diǎn)也很明顯,首先是需要增加較多的類來(lái)實(shí)現(xiàn)相應(yīng)的邏輯。其次是該方式也不符合開(kāi)閉原則,如果需要修改的時(shí)候,是需要對(duì)工廠和對(duì)象都進(jìn)行相應(yīng)的代碼修改的。例如如果需要再增加一個(gè)頭盔,就可能影響到各個(gè)套裝都需要增加對(duì)頭盔的鍛造邏輯的實(shí)現(xiàn)。

總結(jié)

本文介紹了工廠模式對(duì)應(yīng)的三種不同實(shí)現(xiàn)方法,包括**簡(jiǎn)單工廠**、**工廠方法**以及**抽象工廠**。三種實(shí)現(xiàn)方法也各有優(yōu)劣。
  1. 簡(jiǎn)單工廠,邏輯簡(jiǎn)單,代碼邏輯易懂,但是不符合開(kāi)閉原則,增加工廠需要改動(dòng)相應(yīng)的判斷邏輯。
  2. 工廠方法,對(duì)于簡(jiǎn)單工廠做了進(jìn)一步的抽象,新增工廠只需要新增加相應(yīng)的工廠類即可,不涉及到工廠判斷的邏輯。但是會(huì)存在多個(gè)工廠的情況下,類的數(shù)目增多的情況。
  3. 抽象工廠,是對(duì)于工廠方法的進(jìn)一步抽象,其支持同時(shí)生生成多個(gè)對(duì)象。通過(guò)新增加工廠類可以新增加需要生成的組合對(duì)象。但是問(wèn)題在于其也違背了開(kāi)閉原則,當(dāng)需要生成的對(duì)象數(shù)量增多時(shí),相應(yīng)的邏輯需要進(jìn)行修改和增加。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容