​ 工厂模式是一种非常常用的创建型设计模式,其提供了创建对象的最佳方式。在创建对象时,不会对客户端暴露对象的创建逻辑,而是通过使用共同的接口来创建对象。通过使用工厂模式,在业务代码中可以灵活的操控生成的实例对象。

​ 工厂模式主要包含以下三种实现:简单工厂、工厂方法及抽象工厂。下面我们来逐一了解这三种工厂方法的实现与异同。

简单工厂

​ 工厂模式中,最简单易懂的就是简单工厂方法。通俗点来说,简单工厂的核心思想就是:“你告诉我你需要什么,我就为你生产什么”。这里举一个游戏的简单例子。一个游戏中角色分别有战士、法师、精灵。而我们需要设计一个相应的铁匠铺。根据不同的角色,来锻造不同的武器。(假设战士是剑、法师是法杖,而精灵是弓)

​ 那么编程思路其实很简单,首先我们需要设计一个Player的基类对象,并定义一个基本的玩家类型Type字段,用于判断当前玩家是什么角色。然后分别定义出战士、法师和精灵的对象。并对getType的方法进行覆盖。简单类图如下:
在这里插入图片描述

​ 具体类方法如下:

//玩家类
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();
    }
}

​ 在定义完玩家类以后,我们就可以设计咱们的铁匠铺类了。简单来说,铁匠铺只需要调用getType方法判断当前玩家的角色,并锻造武器即可。

@Slf4j
public class BlackSmithShopSimpleFactory {

    public static Weapon createWeapon(Player player) {
        //简单工厂中设计判断即可
        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());
    }
}

​ 最终的输出结果如下:

在这里插入图片描述

​ 可以看到,简单工厂在对应不同玩家的时候都能生成出相应的对象,实现了灵活的机制。但是需要注意的是,简单工厂其实是违背了开闭原则的。例如我们可能需要新增玩家角色 - 刺客,其使用的武器是飞镖,那么此时我们就需要修改铁匠铺的代码,新增上飞镖这种类型。

​ 简单工厂代码虽然违反了开闭原则且可能需要频繁增加生成代码,但是胜在简单易实现,且可读性较好。大多数时候都能胜任问题。

工厂方法

​ 工厂方法模式是对简单工厂模式的进一步深化,其不像简单工厂模式通过一个工厂来完成所有对象的创建,而是通过不同的工厂来创建不同的对象,每个对象有对应的工厂创建。

​ 依旧是以上面的游戏为例子,假设游戏当前不再针对职业进行武器的限制了。玩家可以任意地选择合适的武器,那么此时就可以采用工厂方法进行改造。

在这里插入图片描述

​ 首先,定义一个抽象类或接口,其中规定咱们的创建武器的方法。然后,分别定义生产法杖、生产弓和生产剑的铁匠铺。并实现对应的方法。紧接着,根据玩家的具体需要去生成相应的武器即可。选择工厂并生产的代码如下:

public class BlackSmithShopFactoryPattern {

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

        //生产剑的铁匠铺
        WeaponShop swordShop = new SwordShop();
        Weapon weapon = swordShop.createWeapon();
        System.out.println("玩家选择的武器为:"+weapon.getDesc());

        //生产弓的铁匠铺
        WeaponShop staffShop = new StaffShop();
        Weapon weapon1 = staffShop.createWeapon();
        System.out.println("玩家选择的武器为:"+weapon1.getDesc());

        //生产法杖的铁匠铺
        BowShop bowShop = new BowShop();
        Weapon weapon2 = bowShop.createWeapon();
        System.out.println("玩家选择的武器为:"+weapon2.getDesc());
    }
}

​ 最终的结果如下:

在这里插入图片描述

​ 工厂方法的优势在于扩展性相对比较好,当需要新增工厂的时候,只需要进行相应的拓展即可实现。例如我们如果要新增武器,只需要写一个类再实现相应的生产武器的方法即可。

​ 但是,问题在于过多的类很可能会影响整个系统的可读性,增大系统的复杂度。

抽象工厂

​ 抽象工厂,其实是工厂方法的拓展。上述无论是工厂方法还是简单工厂,都是针对一个对象进行设计和封装,但是实际情况中往往会存在一些连带的情况。还是以上述游戏的情况为例子。假设当前我们要打造的不再是单一的武器,而是需要打造对应的武器和盔甲。同时,武器和盔甲必须成套打造才有相应的属性加成。那么这个时候,工厂方法和简单工厂就比较难满足我们的需求了。

​ 针对这个情况,我们可以设计相应的抽象工厂解决。这里我们首先假定套装有两套,黑龙套装和红龙套装。

​ 首先咱们先设计一个抽象的套装工厂,其两个方法分别是生产武器和盔甲。

public abstract class SuitShop {
    public abstract Weapon createWeapon();

    public abstract Armor createArmor();
}

​ 随后根据职业和套装指定相应的工厂实现子类。(这里仅仅实现了战士的工厂实现子类,其余角色的子类实现类似)

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();
    }
}

​ 除了工厂,我们还要定义对应的黑龙套装和红龙套装对应的武器和盔甲的子类。

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方法中定义相应的逻辑,然后执行得到如下结果。

在这里插入图片描述

​ 总的来说,抽象工厂主要是针对多个类型对象的时候使用的方法。但其缺点也很明显,首先是需要增加较多的类来实现相应的逻辑。其次是该方式也不符合开闭原则,如果需要修改的时候,是需要对工厂和对象都进行相应的代码修改的。例如如果需要再增加一个头盔,就可能影响到各个套装都需要增加对头盔的锻造逻辑的实现。

总结

​ 本文介绍了工厂模式对应的三种不同实现方法,包括简单工厂工厂方法以及抽象工厂。三种实现方法也各有优劣。

  1. 简单工厂,逻辑简单,代码逻辑易懂,但是不符合开闭原则,增加工厂需要改动相应的判断逻辑。
  2. 工厂方法,对于简单工厂做了进一步的抽象,新增工厂只需要新增加相应的工厂类即可,不涉及到工厂判断的逻辑。但是会存在多个工厂的情况下,类的数目增多的情况。
  3. 抽象工厂,是对于工厂方法的进一步抽象,其支持同时生生成多个对象。通过新增加工厂类可以新增加需要生成的组合对象。但是问题在于其也违背了开闭原则,当需要生成的对象数量增多时,相应的逻辑需要进行修改和增加。