首页 工厂模式
文章
取消

工厂模式

工厂模式的定义

程序设计中的工厂类往往是对象创建、初始化过程的封装,而工厂方法 (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();
    }
}

通过工厂方法的改造,不仅敌人需要分类,工厂也同样需要分类,这样一旦敌人种类增加,只需要按照生产规范新增一类工厂,而不需要修改其他工厂方法,相比工厂方法,抽象工厂应用的场景如下:

简单来说,每个工厂不再单纯生产一种对象,经接口规范生产方法后,每种工厂将会生产多种对象。

本文由作者按照 CC BY 4.0 进行授权

原型模式

建造者模式