工厂模式的定义
程序设计中的工厂类往往是对象创建、初始化过程的封装,而工厂方法 (Factory Method) 则可以升华为一种设计模式,它对工厂制造方法进行接口规范化,以允许子类工厂决定具体制造哪类产品的实例,最终降低系统耦合,使系统的可维护性、可扩展性等得到提升。
一般情况下,工厂模式分为三种更加细分的类型:简单工厂、工厂方法和抽象工厂
应用工厂模式
下面以一个实际案例:空战游戏的角色建模,来介绍他们的用法
如图所示,游戏中的敌人有飞机和坦克,虽然种类不同但它们总会有一些共同属性,比如描述位置状态的坐标,以及一个绘制方法(show)将它绘制到图层上,下面是对敌人的父类设计:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public abstract class Enemy {
//x,y 轴坐标
protected int x;
protected int y;
public Enemy(int x, int y) {
this.x = x;
this.y = y;
}
/**
* 绘制方法
*/
public abstract void show();
}
下面是具体的子类设计:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* 飞机类
*/
public class Airplane extends Enemy{
public Airplane(int x, int y) {
super(x, y);
}
@Override
public void show() {
System.out.printf("坐标(%d,%d),出现敌机\n", x, y);
System.out.println("对方发动攻击");
}
}
/**
* 坦克类
*/
public class Tank extends Enemy{
public Tank(int x, int y) {
super(x, y);
}
@Override
public void show() {
System.out.printf("坐标(%d,%d),出现敌方坦克\n", x, y);
System.out.println("对方发动攻击");
}
}
然后是我们的客户端程序:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Client {
public static void main(String[] args) {
int screenWidth = 100;
System.out.println("游戏开始");
Random random = new Random();
int x = random.nextInt(screenWidth);
Enemy airplane = new Airplane(x, 0);
airplane.show();
x = random.nextInt(screenWidth);
Enemy tank = new Tank(x, 0);
tank.show();
}
}
/*输出结果
游戏开始
坐标(85,0),出现敌机
对方发动攻击
坐标(9,0),出现敌方坦克
对方发动攻击
*/
我们发现游戏正常运行起来了,但是有个问题,没有 Boss 啊!这时我们需要添加一个 Boss 类,然后又写一段重复的创建敌人的代码,于是一旦增加敌人我们就要修改客户端程序,显然敌人的创建、初始化逻辑耦合在了客户端当中,这时简单工厂就发挥作用了:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class SimpleFactory {
private int screenWidth;
private Random random;
public SimpleFactory(int screenWidth, Random random) {
this.screenWidth = screenWidth;
this.random = random;
}
public Enemy create(String type){
int x = random.nextInt(screenWidth);
Enemy enemy = null;
switch (type){
case "airplane":
enemy = new Airplane(x, 0);
break;
case "tank":
enemy = new Tank(x, 0);
break;
case "boss":
enemy = new Boss(screenWidth / 2, 0);
break;
default:
throw new RuntimeException("不允许的敌人类型");
}
return enemy;
}
}
这时如果新增其他类别的敌人,只需要在 switch 语句里加个 case 分支就可以了,客户端的代码也会更加简单:
但是如果业务不断的变化,敌人的种类越来越多,每次都需要修改简单工厂,使得它变得越来越臃肿,从设计上缺乏灵活性和可扩展性,同时不符合开闭原则
这时便可使用工厂方法对生产模式进行抽象,为每类敌人专门提供一个工厂,这样每次敌人种类增加,只需要新增对应的工厂类就可以了,符合开闭原则。首先定义一个工厂接口,以确立统一的生产标准:
1
2
3
public interface EnemyFactory {
Enemy create(int screenWidth);
}
下面是将简单工厂拆分后的类设计:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class AirplaneFactory implements EnemyFactory{
@Override
public Enemy create(int screenWidth) {
Random random = new Random();
int x = random.nextInt(screenWidth);
return new Airplane(x, 0);
}
}
public class TankFactory implements EnemyFactory{
@Override
public Enemy create(int screenWidth) {
Random random = new Random();
int x = random.nextInt(screenWidth);
return new Tank(x, 0);
}
}
public class BossFactory implements EnemyFactory{
@Override
public Enemy create(int screenWidth) {
return new Boss(screenWidth / 2, 0);
}
}
最后就是客户端程序:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Client {
public static void main(String[] args) {
int screenWidth = 100;
System.out.println("游戏开始");
EnemyFactory factory;
factory = new AirplaneFactory();
Enemy airplane = factory.create(screenWidth);
factory = new TankFactory();
Enemy tank = factory.create(screenWidth);
factory = new BossFactory();
Enemy boss = factory.create(screenWidth);
airplane.show();
tank.show();
boss.show();
}
}
通过工厂方法的改造,不仅敌人需要分类,工厂也同样需要分类,这样一旦敌人种类增加,只需要按照生产规范新增一类工厂,而不需要修改其他工厂方法,相比工厂方法,抽象工厂应用的场景如下:
简单来说,每个工厂不再单纯生产一种对象,经接口规范生产方法后,每种工厂将会生产多种对象。