工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。一般来说,xxxxxAdapter和xxxxx

工厂模式有分为三种:

  • 简单工厂
  • 工厂方法
  • 抽象工厂

简单工厂

拿手机为例,手机有很多品牌,比如华为、小米、苹果等。

首先定义一个手机的接口

1
2
3
4
public interface Phone {
//手机品牌
void brand();
}

再定义两种品牌的手机,分别实现手机的接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class HuaWeiPhone implements Phone {
@Override
public void brand() {
System.out.println("华为手机...");
}
}


public class XiaoMiPhone implements Phone {
@Override
public void brand() {
System.out.println("小米手机...");
}
}

再定义一个专门生产手机的工厂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class PhoneFactory {
public Phone createPhone(String phoneType){
Phone phone = null;
switch (phoneType){
case "xm" :
phone = new HuaWeiPhone();
break;
case "hw" :
phone = new XiaoMiPhone();
break;
}
return phone;
}
}

客户端测试代码

1
2
3
4
5
6
7
8
9
public class test {
public static void main(String[] args) {
PhoneFactory factory = new PhoneFactory();
Phone xm = factory.createPhone("xm");
Phone hw = factory.createPhone("hw");
xm.brand();
hw.brand();
}
}

输出打印

image-20220107172236304

可以看到,当我们给工厂不同的参数的时候,我们就能获得不同品牌的手机,但是有发现了一个问题,如果我们的工厂还想能生成苹果手机,那么我们就必须要改动工厂类的代码逻辑,这就违背了开闭原则

工厂方法

在上面的简单工厂中,如果要创建的手机类型较多,且各个手机创建的过程不尽相同,则一个工厂类职责会变得越来越多,不符合单一职责原则。另外简单工厂也不符合开闭原则。要新增一种手机需要修改原来的工厂类。因此,工厂方法模式中,将生产各种类型的产品的工厂也做了抽象分离。我们对上面的简单工厂进行改造。
首先创建统一的工厂接口

1
2
3
public interface PhoneFactory {
Phone createPhone();
}

再分别创建不同手机品牌的工厂,比如有小米手机工厂,华为手机工厂等等

1
2
3
4
5
6
7
8
9
10
11
12
13
public class XiaoMiFactory implements PhoneFactory {
@Override
public Phone createPhone() {
return new XiaoMiPhone();
}
}

public class HuaWeiFactory implements PhoneFactory {
@Override
public Phone createPhone() {
return new HuaWeiPhone();
}
}

客户端测试截图

image-20220107172456117

如果这时候需要我们还能生成苹果手机,就比较容易了,我们只需要再添加一个苹果手机工厂类,实现手机工厂就行了,无需再更改工厂类的代码。

增加苹果手机类,实现手机接口

1
2
3
4
5
6
public class IPhone implements Phone {
@Override
public void brand() {
System.out.println("苹果手机...");
}
}

增加苹果手机工厂类,实现手机工厂接口

1
2
3
4
5
6
public class IPhoneFactory implements PhoneFactory {
@Override
public Phone createPhone() {
return new IPhone();
}
}

客户端测试截图

image-20220107172431934

当我们有其他手机需要生产时,只需要先定义手机类,实现Phone接口,再定义手机工厂类,实现PhoneFactory接口,就能实现不用更改其他地方的代码就能生产出不同品牌的手机,完全符合开闭原则。但是还有一个问题,当我们新增一种品牌的手机时需要新增两个类,如果有很多手机品牌时,就会造成有很多类,造成类爆炸,而且当我们需要其他产品的时候,发现没有生产其他产品的工厂,此时我们可以把产品组合起来,一个工厂生产所有的产品,比如小米工厂要能生产手机和路由器。

抽象工厂模式

先定义一个手机产品的顶级接口,有手机的一些功能

1
2
3
4
5
6
7
8
9
10
11
12
13
public interface Phone {
//开机
void start();

//关机
void shutdown();

//打电话
void callup();

//发邮件
void sendSMS();
}

再定义一个路由器的顶级接口,有路由器的一些功能

1
2
3
4
5
6
7
8
9
10
11
12
13
public interface Router {
//开机
void start();

//关机
void shutdown();

//打开wifi
void openwifi();

//设置
void setting();
}

再写华为和小米的产品的4个实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class HuaWeiPhone implements Phone{
@Override
public void start() {
System.out.println("华为手机开机...");
}

@Override
public void shutdown() {
System.out.println("华为手机关机...");
}

@Override
public void callup() {
System.out.println("华为手机打电话...");
}

@Override
public void sendSMS() {
System.out.println("华为手机发送短信...");
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class HuWeiRouter implements Router{
@Override
public void start() {
System.out.println("华为路由器开机...");
}

@Override
public void shutdown() {
System.out.println("华为路由器关机...");
}

@Override
public void openwifi() {
System.out.println("华为路由器打开wifi...");
}

@Override
public void setting() {
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 XiaoMiPhone implements Phone{
@Override
public void start() {
System.out.println("小米手机开机...");
}

@Override
public void shutdown() {
System.out.println("小米手机关机...");
}

@Override
public void callup() {
System.out.println("小米手机打电话...");
}

@Override
public void sendSMS() {
System.out.println("小米手机发送短信...");
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class XiaoMiRouter implements Router{
@Override
public void start() {
System.out.println("小米路由器开机...");
}

@Override
public void shutdown() {
System.out.println("小米路由器关机...");
}

@Override
public void openwifi() {
System.out.println("小米路由器打开wifi...");
}

@Override
public void setting() {
System.out.println("小米路由器设置...");
}
}

再定义一个顶级的产品接口,能生产手机和路由器。

1
2
3
4
5
6
7
8
9
public interface ProductFactory {

//生成手机
Phone phoneProduct();

//生成路由器
Router routerProduct();

}

再写两个华为工厂和小米工厂的实现类,各自都实现产品接口。

1
2
3
4
5
6
7
8
9
10
11
public class HuaWeiFactory implements ProductFactory{
@Override
public Phone phoneProduct() {
return new HuaWeiPhone();
}

@Override
public Router routerProduct() {
return new HuWeiRouter();
}
}
1
2
3
4
5
6
7
8
9
10
11
public class XiaoMiFactory implements ProductFactory{
@Override
public Phone phoneProduct() {
return new XiaoMiPhone();
}

@Override
public Router routerProduct() {
return new XiaoMiRouter();
}
}

客户端,通过 ProductFactory创建各自的工厂,通过工厂拿到对应的产品

image-20220107183755117

看一下类图

image-20220107180650884

如果这时候还想要增加产品等级机会很简单了,比如想新增一个苹果产品。我们只需要新增一个类IPhoneFactory实现ProductFactory,苹果工厂就能生产手机和路由器了,要想生产具体的苹果手机和苹果路由器还得新增一个IPhone手机类,和IRouter路由器类,分别实现Phone接口和Router接口,就可以生产具体的苹果手机和苹果路由器了。下面来看一下新增苹果产品后的类图:

image-20220107181927789

我们发现新增一个产品等级还是很容易的,但是新增一个产品族就会很难,比如我现在想要所有工程都能生产笔记本电脑,那么就会改动到ProductFactory接口的代码,因为这个接口现在定义了只生产手机和路由器,不能生产笔记本,相应的实现了HuaWeiFactory、XiaoMiFactory、IPhoneFactory也会改动,这又违背了开闭原则。所以说抽象工厂方法也会有优缺点。

优点:

  • 一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象(将一个系列的产品统一一起创建)。

缺点:

  • 产品族扩展非常困难,要增加一个系列的某一产品,既要修改工厂抽象类里加代码,又修改具体的实现类里面加代码。
  • 增加了系统的抽象性和理解难度。