学习抽象工厂模式之前,先回顾下工厂方法的缺陷:
- 在工厂方法模式中,每个具体产品就对应一个具体工厂类,随着业务的扩展会导致系统中存在大量的工厂类,增加系统的开销
- 工厂方法模式中的每一个具体工厂类,只能创建出同一类产品实例
抽象工厂模式的出现,可以解决上面两个问题
首先看两个概念:
1. 产品等级结构:即产品的继承结构,例如一个抽象类是电视机,其子类包括小米电视机、索尼电视机、创维电视机等,则抽象电视机与具体品牌的电视机之间构成了一个产品等级结构
2. 产品族:是指由同一个工厂生产的位于不同产品等级结构中的一组产品,例如小米电器工厂生产的小米电视机、小米空调,小米电视机位于电视机产品等级结构中,小米空调位于空调产品等级结构中,小米电视机与小米空调就构成了一个产品族
接下来就正式学习抽象工厂模式
概述
当系统所提供的工厂生产具体产品不是一个简单的对象,而是多个位于不同产品等级结构、属于不同类型的具体产品时就可以使用抽象工厂模式。
抽象工厂模式与工厂方法模式最大的区别在于,工厂方法模式针对的是一个产品等级结构,而抽象工厂模式需要面对多个产品等级结构,一个工厂等级结构可以负责多个不同产品等级结构中的产品对象的创建。
抽象工厂模式中的具体工厂不只是创建一种产品,而是负责创建一个产品族。比如创建了一个海尔电器工厂,就能够生产全部的海尔品牌的电器。
定义:提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。
结构
1). 抽象工厂:它声明了一组用于创建一族产品的方法,每一个方法对应一种产品
2). 具体工厂:它实现了在抽象工厂中声明的创建产品的方法,生成一组具体产品,这些产品构成了一个产品族,每一个产品都位于某个产品等级结构中
3). 抽象产品:它为每种产品声明接口,在抽象产品中声明了产品所具有的业务方法
4). 具体产品:它定义具体生产的具体产品对象,实现抽象产品接口中声明的方法
结构图
上图中,把不同种类的水果作为一个产品族,即 Apple 和 Mango;
每个产品族都具有 Juice 与 Can 这两个产品等级结构。
查了词典,苹果罐头与芒果罐头的英文分别是 canned apple 和 canned mango
不过为了方便,这里就使用了错误的拼写方式
代码实现
有了简单工厂模式与工厂方法模式作为练习,已经能够比较熟练地根据结构图来编写代码了,具体有下面几个步骤:
1). 创建抽象产品类
Can.java
/**
* 抽象产品 : 罐头
*/
public interface Can {
/** 抽象方法 */
void eat();
}
Juice.java
/**
* 抽象产品 : 果汁
*/
public interface Juice {
/**抽象方法*/
void drink();
}
2). 创建具体产品类
AppleCan.java
/**
* 具体产品 : 苹果罐头
* 实现 Can 接口
*/
public class AppleCan implements Can {
/**实现 eat() 方法*/
@Override
public void eat() {
System.out.println("我想吃苹果罐头!");
}
}
AppleJuice.java
/**
* 具体产品 : 苹果汁
* 实现自果汁接口
*/
public class AppleJuice implements Juice {
/**实现Juice接口的drink()方法*/
@Override
public void drink() {
System.out.println("我想喝苹果汁!");
}
}
MangoCan.java
/**
* 具体产品 : 芒果罐头
* 实现 Can 接口
*/
public class MangoCan implements Can {
/**实现 eat() 方法*/
@Override
public void eat() {
System.out.println("我想吃芒果罐头!");
}
}
MangoJuice.java
/**
* 具体产品 : 芒果汁
* 实现 Juice 接口
*/
public class MangoJuice implements Juice {
/**实现 drink() 方法*/
@Override
public void drink() {
System.out.println("我想喝芒果汁!");
}
}
3). 创建抽象工厂接口
FruitFactory.java
/**
* 抽象工厂类, 使用接口实现
*/
public interface FruitFactory {
/** 工厂方法, 返回 Juice 相关的实例 */
Juice createJuice();
/** 工厂方法, 返回 Can 相关的实例 */
Can createCan();
}
4). 创建具体工厂类
AppleFactory.java
/**
* 具体工厂类 : 苹果工厂
* 用于生产苹果相关的产品族
*/
public class AppleFactory implements FruitFactory {
/**实现 createJuice() 方法, 返回 AppleJuice 实例*/
@Override
public Juice createJuice() {
return new AppleJuice();
}
/** 实现 createCan() 方法, 返回 AppleCan 实例 */
@Override
public Can createCan() {
return new AppleCan();
}
}
MangoFactory.java
/**
* 具体工厂类 : 芒果工厂
* 生产芒果相关的产品族产品
*/
public class MangoFactory implements FruitFactory {
/**实现 createJuice() 方法, 返回 MangoJuice 实例 */
@Override
public Juice createJuice() {
return new MangoJuice();
}
/**实现 createCan() 方法, 返回 MangoCan 实例 */
@Override
public Can createCan() {
return new MangoCan();
}
}
5). 测试类
/**
* 测试类
*/
public class TestDemo {
public static void main(String[] args) {
/** 创建芒果工厂实例 */
FruitFactory factory = new MangoFactory();
/** 调用工厂方法, 分别返回 MangoJuice 与 MangoCan 实例 */
Juice juice = factory.createJuice();
Can can = factory.createCan();
/** 执行具体产品的方法 */
can.eat();
juice.drink();
}
}
6). 执行结果
我想吃芒果罐头!
我想喝芒果汁!
测试类使用的是芒果这个产品族,那如果想获取苹果的产品族,只需要修改测试类中的第七行,将 MangoFactory 替换为 AppleFactory 即可
开闭原则的倾斜性
在上面的设计中存在一个非常严重的问题:如果随着业务扩展,果汁和罐头已经无法满足客户需求,要生产新的产品——果脯,也就是添加一个新的产品等级结构。
体现在代码中,就是要修改所有的工厂角色,使得每一个工厂类都拥有创建果脯产品的方法,这也是抽象工厂模式最大的缺点。
在抽象工厂模式中增加新的产品族很方便,但是增加新的产品等级结构很麻烦,抽象工厂模式的这种性质被称为开闭原则的倾斜性。
开闭原则要求系统对扩展开放,对修改关闭,通过扩展达到增强其功能的目的,对于涉及多个产品族与多个产品等级结构的系统,其功能增强包括两个方面:
1). 增加产品族:对于增加新的产品族,抽象工厂模式很好地支持了开闭原则
2). 增加产品等级结构:对于增加新的产品等级结构,需要修改所有的工厂角色,违背了开闭原则
正因为抽象工厂模式存在开闭原则的倾斜性,它以一种倾斜的方式来满足开闭原则,为增加产品族提供方便,但不能为增加新的产品族提供这样的方便,因此要求设计人员在设计之初就能够考虑全面,不会在设计完成之后再向系统中增加新的产品等级结构。
每周末都会学习一个新的知识点,记录在博客与微信公众号里,有兴趣的朋友关注我的公众号吧,大家一起进步!!!
如果喜欢的话,扫描下方二维码关注吧!!!欢迎各位前来交流!!!