# 工厂方法模式

在学习工厂方法模式之前我们先来了解简单工厂模式。

# 场景

1、小安喜欢吃苹果,于是就自己种苹果树。

image-20210922185242990

2、苹果吃多了,又爱上了吃橘子,于是又自己种橘子树。

image-20210922185909276

3、桔子又吃腻了,然后就种其他各式的水果,结果自己成了一个果农,有大片的果园。这时,小安的基友小金来玩,小安说你想吃什么水果直接摘,因此谁想吃苹果,直接去摘:采摘者自己负责 new Apple()。但是会存在以下问题:

  • 如果采摘的人多了可能会一不小心破坏果树。
  • 比较复杂的果子(板栗),那可能个人是无法采摘的,需要借助工具。

4、根据可能产生的问题,小安于是把果园交给了果园工厂来负责管理,想要啥水果就直接说,然后就能拿到对应的水果,而不需要关心怎么去采摘。

image-20210922190918297

# 简单工厂模式

# 定义

提供一个创建对象实例的功能,而无需关心其具体实现。被创建实例的类型可以是接口,抽象类,也可以是具体的类。

在简单工厂模式中创建实例的方法通常为静态(static)方法,因此简单工厂模式(Simple Factory Pattern)又叫作静态工厂方法模式(Static Factory Method Pattern)。

# 结构

简单工厂模式的主要角色如下:

  • 简单工厂(SimpleFactory):是简单工厂模式的核心,负责实现创建所有实例的内部逻辑。工厂类的创建产品类的方法可以被外界直接调用,创建所需的产品对象,如水果工厂。
  • 抽象产品(Product):是简单工厂创建的所有对象的父类,负责描述所有实例共有的公共接口,如水果。
  • 具体产品(ConcreteProduct):是简单工厂模式的创建目标,如苹果、橘子等。

image-20210923110428870

# 实现

抽象产品(Product)

public interface Fruit {
	//水果
    public void fruit();
}
1
2
3
4

具体产品(ConcreteProduct):水果种类

//苹果
public class Apple implements Fruit {
    @Override
    public void fruit() {
        System.out.println("我是苹果");
    }
}

//香蕉
public class Banana implements Fruit {
    @Override
    public void fruit() {
        System.out.println("我是香蕉");
    }
}

//橘子
public class Orange implements Fruit {
    @Override
    public void fruit() {
        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
23

简单工厂(SimpleFactory)

public class FruitFactory {
    //苹果
    public static final int TYPE_APPLE = 1;
    //桔子
    public static final int TYPE_ORANGE = 2;
    //香蕉
    public static final int TYPE_BANANA = 3;

    /**
     * 根据需求选择对应的产品
     */
    public static Fruit getFruit(int type) {
        Fruit fruit = null;
        if (TYPE_APPLE == type) {
            fruit = new Apple();
        } else if (TYPE_ORANGE == type) {
            fruit = new Orange();
        } else if (TYPE_BANANA == type) {
            fruit = new Banana();
        }
        return fruit;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 优缺点

优点

  1. 工厂类包含必要的逻辑判断,可以决定在什么时候创建哪一个产品的实例。客户端可以免除直接创建产品对象的职责,很方便的创建出相应的产品。工厂和产品的职责区分明确。
  2. 客户端无需知道所创建具体产品的类名,只需知道参数即可,如上面的只需要知道type代表什么即可。

缺点

  1. 简单工厂模式的工厂类单一,负责所有产品的创建,职责过重,一旦异常,整个系统将受影响。且工厂类代码会非常臃肿,违背高聚合原则。
  2. 使用简单工厂模式会增加系统中类的个数(引入新的工厂类),增加系统的复杂度和理解难度。
  3. 系统扩展困难,一旦增加新产品不得不修改工厂逻辑(即if-else判断逻辑),在产品类型较多时,可能造成逻辑过于复杂。
  4. 简单工厂模式使用了 static 工厂方法,造成工厂角色无法形成基于继承的等级结构。

# 使用场景

  • 如果需要完全封装隔离具体实现,让外部只能通过接口来操作封装体,可以选用简单工厂,让客户端通过工厂来获取相应的接口,无需关心具体实现。
  • 如果想要把对外创建对象的职责集中管理和控制,可以选用简单工厂,一个简单工厂可以创建很多的不相关的对象,可以把对外创建对象的职责集中到一个简单工厂来,从而实现集中管理和控制。

# 工厂方法模式

简单工厂模式的主要问题是,类的创建依赖工厂类,也就是说,如果想要拓展程序,必须对工厂类进行修改,这违背了开闭原则及单一职责原则(一个类负责了各类水果的创建)所以,从设计角度考虑,有一定的问题。

image-20210922220948257

如何解决?

我们可以定义一个创建对象的抽象方法并创建多个不同的工厂类实现该抽象方法,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码。这种方法也就是工厂方法模式。

image-20210922221145300

# 定义

定义一个创建对象的工厂接口,将对象的实际创建工作推迟到具体子工厂类当中。这满足创建型模式中所要求的“创建与使用相分离”的特点。

# 结构

工厂方法模式的主要角色如下。

  1. 抽象工厂(Abstract Factory):提供了创建产品的接口(或抽象类),调用者通过它访问具体工厂的工厂方法来创建产品。
  2. 具体工厂(ConcreteFactory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建,如苹果工厂 。
  3. 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能,如父类水果。
  4. 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。如具体的水果种类。

image-20210923110700794

# 实现

抽象工厂(Abstract Factory)

public  interface AbstractFactory {
    /**
     * 获取水果
     */
    Fruit getFruit();
}

1
2
3
4
5
6
7

具体工厂(ConcreteFactory)

/**
 * 苹果工厂
 */
public class AppleFactory implements AbstractFactory {
    @Override
    public Fruit getFruit() {
        return new Apple();
    }
}

/**
 * 香蕉工厂
 */
public class BananaFactory implements AbstractFactory {
    @Override
    public Fruit getFruit() {
        return new Banana();
    }
}

/**
 * 桔子工厂
 */
public class OrangeFactory implements AbstractFactory {
    @Override
    public Fruit getFruit() {
        return new Orange();
    }
}
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

抽象产品(Product)

public interface Fruit {
	//水果
    public void fruit();
}
1
2
3
4

具体产品(ConcreteProduct):水果种类

//苹果
public class Apple implements Fruit {
    @Override
    public void fruit() {
        System.out.println("我是苹果");
    }
}

//香蕉
public class Banana implements Fruit {
    @Override
    public void fruit() {
        System.out.println("我是香蕉");
    }
}

//橘子
public class Orange implements Fruit {
    @Override
    public void fruit() {
        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
23

测试

public static void main(String[] args) {
    //初始化香蕉工厂
    AbstractFactory factory = new BananaFactory();
    Fruit banana = factory.getFruit();
    banana.fruit();
    //初始化苹果工厂
    factory = new AppleFactory();
    Fruit apple = factory.getFruit();
    apple.fruit();
}

输出:
我是香蕉
我是苹果
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 优缺点

优点

  • 用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程
  • 在系统增加新的产品时只需要添加具体产品类和对应的具体工厂类,无须对原工厂进行任何修改,满足开闭原则。

缺点

  • 每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度。
  • 抽象产品只能生产一种产品。

# 使用场景

  • 客户只知道创建产品的工厂名,而不知道具体的产品名。如 TCL 电视工厂、海信电视工厂等。
  • 创建对象的任务由多个具体子工厂中的某一个完成,而抽象工厂只提供创建产品的接口。
  • 客户不关心创建产品的细节,只关心产品的品牌。

# 总结

在大部分情况下,简单工厂模式是使用最多的情况,其本质是选择实现,而工厂方法模式的本质是推迟到子类去实现,其最终的目的都是生产出同一级别的产品,如都可以生产苹果,橘子等。