抽象工厂模式

抽象工厂模式

1. 简介

适用场景:

  • 客户端(应用层)不依赖于产品类实例如何被创建、实现等细节
  • 强调一系列相关的产品对象(属于同一产品族)一起使用创建对象需要大量的重复代码
  • 提供一个产品类的库,所有的产品以同样的接口出现,从而使得客户端不依赖于具体的实现

优点:

  • 具体产品在应用层的代码隔离,无需关心创建的细节
  • 将一个系列的产品统一到一起创建

缺点:

  • 规定了所有可能被创建的产品集合,产品簇中扩展新的产品困难;
  • 增加了系统的抽象性和理解难度

2. UML类图

****

  • Creator:产品族工厂,定义了一系列的产品生产行为
  • ConcreteCreator:具体的产品族工厂
  • Product:抽象产品接口
  • Product A1/A2/B1/B2:都是具体的产品,实现了相应的产品接口

3. 产品族和产品等级

​ 为了更好理解抽象工厂模式,需要先搞清楚产品等级和产品族的概念,

​ 举个粟子:手机有小米手机、华为手机,它们都是手机,这些具体的手机和抽象手机就构成了一个产品等级结构。

​ 同样的,路由器有小米路由器,华为路由器,这些具体的路由器和抽象路由器就构成了另外一个产品等级结构,实质上产品等级结构即产品的继承结构。

​ 小米手机位于手机产品等级结构中,小米路由器位于路由器的产品等级结构中,而小米手机和小米路由器都是小米公司生产的,就构成了一个产品族,

​ 同理,华为手机和华为路由器也构成了一个产品族 。划重点就是产品族中的产品都是由同一个工厂生产的,位于不同的产品等级结构。

对比工厂方法,其针对的是产品等级结构,而抽象工厂是针对产品族。在二者的使用选择上,需要结合实际业务,对于产品等级数量相对固定的产品族,可以优先考虑抽象工厂模式,但是如果频繁变动,则不大适用,因为在现有的产品族中新增产品等级时,就需要修改产品族工厂,也就违背了开闭原则

4. 代码实现

  • 定义抽象产品接口
1
2
3
4
5
6
7
8
9
10
11
//手机产品接口
public interface IPhoneProduct {
//开机
void start();
//关机
void shutdown();
//拨打电话
void callUp();
//发送短信
void sendSMS();
}
1
2
3
4
5
6
7
8
9
10
11
//路由器产品接口
public interface IRouterProduct {
//开机
void start();
//关机
void shutdown();
//开启wifi
void openWifi();
//设置参数
void setting();
}
  • 定义小米平拍手机和路由器产品实现类,华为品牌手机和路由器产品实现类
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 IPhoneProduct {
@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
22
//小米路由器产品
public class XiaomiRouter implements IRouterProduct {
@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 HuaweiPhone implements IPhoneProduct {
@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
22
//华为路由器产品
public class HuaweiRouter implements IRouterProduct {
@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
//抽象产品工厂(定义了同一个产品族的产品生产行为)
public interface IProductFactory {
//生产手机
IPhoneProduct produceTelPhone();
//生产路由器
IRouterProduct produceRouter();
}
  • 小米工厂和华为工厂实现类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//小米产品工厂
public class XiaomiProductFactory implements IProductFactory {
@Override
public IPhoneProduct produceTelPhone() {
System.out.println(">>>>>>生产小米手机");
return new XiaomiPhone();
}

@Override
public IRouterProduct produceRouter() {
System.out.println(">>>>>>生产小米路由器");
return new XiaomiRouter();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//华为产品工厂
public class HuaweiProductFactory implements IProductFactory{
@Override
public IPhoneProduct produceTelPhone() {
System.out.println(">>>>>>生产华为手机");
return new HuaweiPhone();
}

@Override
public IRouterProduct produceRouter() {
System.out.println(">>>>>>生产华为路由器");
return new HuaweiRouter();
}
}
  • 客户端
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
30
31
32
33
34
35
36
37
//客户端
public class Client {

public static void main(String[] args) {
System.out.println("===================小米系列产品=================");
//小米产品工厂实例
IProductFactory xiaomiProductFactory = new XiaomiProductFactory();
//生产小米路由器
IRouterProduct xiaomiRouter = xiaomiProductFactory.produceRouter();
xiaomiRouter.start();
xiaomiRouter.setting();
xiaomiRouter.openWifi();
xiaomiRouter.shutdown();
//生产小米手机
IPhoneProduct xiaomiPhone = xiaomiProductFactory.produceTelPhone();
xiaomiPhone.start();
xiaomiPhone.callUp();
xiaomiPhone.sendSMS();
xiaomiPhone.shutdown();

System.out.println("===================华为系列产品=================");
//华为产品工厂实例
IProductFactory huaweiProductFactory = new HuaweiProductFactory();
//生产华为路由器
IRouterProduct huaweiRouter = huaweiProductFactory.produceRouter();
huaweiRouter.start();
huaweiRouter.setting();
huaweiRouter.openWifi();
huaweiRouter.shutdown();
//生产华为手机
IPhoneProduct huaweiPhone = huaweiProductFactory.produceTelPhone();
huaweiPhone.start();
huaweiPhone.callUp();
huaweiPhone.sendSMS();
huaweiPhone.shutdown();
}
}

运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
===小米系列产品=
>>>>>>生产小米路由器
启动小米路由器
设置小米路由器参数
打开小米路由器的wifi功能
关闭小米路由器
>>>>>>生产小米手机
开启小米手机
用小米手机打电话
用小米手机发短信
关闭小米手机
===华为系列产品=
>>>>>>生产华为路由器
启动华为路由器
设置华为路由器参数
打开华为路由器的wifi功能
关闭华为路由器
>>>>>>生产华为手机
开启华为手机
用华为手机打电话
用华为手机发短信
关闭华为手机

关系图:

5. 小结

​ 从上面的整个示例可以发现,如果现在想增加一个产品等级,如新加一种笔记本产品,就需要修改抽象产品工厂接口IProductFactory,如下:

1
2
3
4
5
6
7
8
9
10
11
12
//抽象产品工厂(定义了同一个产品族的产品生产行为)
public interface IProductFactory {

//生产手机
IPhoneProduct produceTelPhone();
//生产路由器
IRouterProduct produceRouter();

//生产笔记本(新增)
IComputerProduct produceComputer();

}
  • 这种直接修改抽象接口的做法,会导致其所有实现子类都需要进行修改,违反了开闭原则。

  • 当然,如果这种修改是长期稳定的,那么也可以接受。

  • 如果该场景以工厂方法实现,那么我们需要定义一个手机工厂、一个路由器工厂、然后小米手机工厂实现类,华为手机工厂实现类,小米路由器工厂实现类,华为路由器工厂实现类,其余的产品接口和产品实现类均与上相同,这样就多出了一些产品工厂,略显啰嗦

  • 实际使用中,都需要根据业务去权衡使用工厂方法还是抽象工厂,前者关注点在产品等级上,后者关注点在产品族上,对于稳定的产品族,也即是产品等级数量稳定,使用抽象工厂会更加有效率,毕竟不再是一个工厂生产一种产品,而是一个工厂生产多种同族产品,对于不稳定的产品族,单独使用工厂方法会显得更加灵活!

打赏
  • 版权声明: 本博客所有文章除特别声明外,均采用 Apache License 2.0 许可协议。转载请注明出处!
  • © 2019-2022 Zhuuu
  • PV: UV:

请我喝杯咖啡吧~

支付宝
微信