工厂模式是一种非常常用的**创建型设计模式**,其提供了创建对象的最佳方式。在创建对象时,不会对客户端暴露对象的创建逻辑,而是通过使用共同的接口来创建对象。通过使用工厂模式,在业务代码中可以灵活的操控生成的实例对象。
工厂模式主要包含以下三种实现:**简单工厂、工厂方法及抽象工厂**。下面我们来逐一了解这三种工厂方法的实现与异同。
简单工厂
工厂模式中,最简单易懂的就是简单工厂方法。通俗点来说,简单工厂的核心思想就是:“你告诉我你需要什么,我就为你生产什么”。这里举一个游戏的简单例子。一个游戏中角色分别有战士、法师、精灵。而我们需要设计一个相应的铁匠铺。根据不同的角色,来锻造不同的武器。(假设战士是剑、法师是法杖,而精灵是弓)
那么编程思路其实很简单,首先我们需要设计一个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方法中定义相应的逻辑,然后执行得到如下结果。
总的来说,抽象工厂主要是针对多个类型对象的时候使用的方法。但其缺点也很明显,首先是需要增加较多的类来实现相应的逻辑。其次是该方式也不符合开闭原则,如果需要修改的时候,是需要对工厂和对象都进行相应的代码修改的。例如如果需要再增加一个头盔,就可能影响到各个套装都需要增加对头盔的锻造逻辑的实现。
总结
本文介绍了工厂模式对应的三种不同实现方法,包括**简单工厂**、**工厂方法**以及**抽象工厂**。三种实现方法也各有优劣。
-
简单工厂,逻辑简单,代码逻辑易懂,但是不符合开闭原则,增加工厂需要改动相应的判断逻辑。
-
工厂方法,对于简单工厂做了进一步的抽象,新增工厂只需要新增加相应的工厂类即可,不涉及到工厂判断的逻辑。但是会存在多个工厂的情况下,类的数目增多的情况。
-
抽象工厂,是对于工厂方法的进一步抽象,其支持同时生生成多个对象。通过新增加工厂类可以新增加需要生成的组合对象。但是问题在于其也违背了开闭原则,当需要生成的对象数量增多时,相应的逻辑需要进行修改和增加。