前言
工厂模式一般指的是简单工厂模式、工厂方法模式、抽象工厂模式,这是三种工厂模式的最后一篇,其他两种的文章链接如下:
建议三种模式放在一起对比学习,更能领会其中的奥秘。看懂UML类图,更是奥秘中的奥秘哦,在UML类图中不同的箭头和线条,代表的意义是不同的,发现有很多人都画错了,这里简单的梳理了一下,如果需要可以移步这里:设计模式之基础:UML类图怎么看?_uml图怎么看-CSDN博客
什么是抽象工厂模式?
抽象工厂模式是一种创建型的工厂模式,它提供了一个接口,使客户端可以在不指定具体产品的情况下创建多个产品组中的产品对象。当存在多个抽象角色时,抽象工厂模式可以用来创建一个工厂,该工厂能够根据需要生成相应类型的产品。这种模式将产品的生成和使用解耦,使得客户端无需了解具体产品的实现细节。
抽象工厂模式的核心思想是将工厂封装成了一个抽象接口,通过这个接口来创建产品。它允许客户端通过调用抽象工厂的接口来生成多个产品族中的产品对象,而无需知道每个产品族的实现细节。抽象工厂模式适用于存在多个产品族的情况,这些产品族具有不同的实现,但是客户端需要使用多个产品族中的产品。通过使用抽象工厂模式,客户端可以灵活地使用不同的产品族,而无需对代码进行大量修改。
与工厂方法模式有什么区别?
抽象工厂模式和工厂方法模式都是设计模式中的对象创建型模式,主要用于封装对象的创建过程,以减少代码耦合。两种模式之间存在以下区别:
- 工厂角色不同:在工厂方法模式中,工厂方法是实现客户端与具体产品类解耦的关键,客户端通过继承或实现工厂方法实现创建产品的功能。而在抽象工厂模式中,抽象工厂是客户端与具体产品类解耦的关键,客户端通过实例化抽象工厂对象并调用其创建产品的方法实现创建产品的功能。
- 创建产品的模式不同:工厂方法模式中,每个具体工厂类只能创建一个具体产品类的实例,即"一对一"的关系。而在抽象工厂模式中,每个具体工厂类可以创建多个具体产品类的实例,即"一对多"的关系。
- 产品类型不同:在工厂方法模式中,工厂方法返回的产品类型是固定的,即每个具体工厂类只能创建一个固定类型的产品。而在抽象工厂模式中,抽象工厂返回的产品类型可以是多种多样的,即每个具体工厂类可以创建多个不同类型的产品。
总之,抽象工厂模式相对于工厂方法模式,更适用于创建一系列相关或相互依赖的对象,以及对产品进行分类和组织的情况。
抽象工厂UML类图
在抽象工厂模式中,存在四种角色:
- 抽象工厂角色(Abstract Factory):担任这个角色的是工厂方法模式的核心,它是与应用系统业务逻辑无关的。抽象工厂包含对多个产品结构的声明,任何工厂类都必须实现这个接口。
- 具体工厂角色(Concrete Factory):这个角色直接在客户端的调用下创建产品的实例。这个角色含有选择合适的产品对象的逻辑,而这个逻辑是与应用系统的业务逻辑紧密相关的。具体工厂类是抽象工厂的一个实现,负责实例化某个产品族中的产品对象。
- 抽象产品角色(Abstract Product):担任这个角色的类是工厂方法模式所创建的对象的父类,或它们共同拥有的接口。抽象模式所创建的所有对象的父类,它负责描述所有实例所共有的公共接口。
- 具体产品角色(Concrete Product):抽象工厂模式所创建的任何产品对象都是某一个具体产品类的实例。
抽象工厂实现示例
还以女娲造人的传说举一个例子:刚开始造人,女娲觉得还挺有意思的,造的时间长了,都是黄皮肤的中国人,显得很单调,没啥意思了,于是拓展了一下业务,开始造黑色皮肤的非洲人了,用抽象工厂模式来演示这个过程会是什么样呢?
UML类图如下:
伪代码示例如下:
java
public interface Human {
/**
* 人类会吃东西
*/
void eat();
/**
* 人类会喝东西
*/
void drink();
}
java
public class YellowMan implements Human {
@Override
public void eat() {
System.out.println("黄色皮肤的中国男人在吃东西");
}
@Override
public void drink() {
System.out.println("黄色皮肤的中国男人在喝水");
}
}
java
public class YelloWoman implements Human {
@Override
public void eat() {
System.out.println("黄色皮肤的中国女人在吃东西");
}
@Override
public void drink() {
System.out.println("黄色皮肤的中国女人在喝水");
}
}
java
public interface HumanFactory {
Human createMan();
Human createWoman();
}
java
public class YelloHumanFactory implements HumanFactory{
@Override
public Human createMan() {
return new YellowMan();
}
@Override
public Human createWoman() {
return new YelloWoman();
}
}
java
public class BlackHumanFactory implements HumanFactory{
@Override
public Human createMan() {
return new BlackMan();
}
@Override
public Human createWoman() {
return new BlackWoman();
}
}
java
public class Test {
public static void main(String[] args) {
HumanFactory yellowHumanFactory=new YelloHumanFactory();
Human man = yellowHumanFactory.createMan();
Human woman = yellowHumanFactory.createWoman();
man.eat();
man.drink();
woman.eat();
woman.drink();
HumanFactory blackHumanFactory=new BlackHumanFactory();
Human man1 = blackHumanFactory.createMan();
Human woman1 = blackHumanFactory.createWoman();
man1.eat();
man1.drink();
woman1.eat();
woman1.drink();
}
}
女娲这都造人业务都拓展到海外了,光能造黄种人、黑种人怎么行,还想造白种人怎么办呢?很简单:
1、实现Human接口,再实现一个白种男人、一个白种女人的类;
2、实现HumanFactory接口,再实现一个白种人的制造工厂;
java
public class WhiteMan implements Human {
@Override
public void eat() {
System.out.println("白种欧洲男人吃东西");
}
@Override
public void drink() {
System.out.println("白种欧洲男人在喝水");
}
}
java
public class WhiteWoman implements Human {
@Override
public void eat() {
System.out.println("白种欧洲女人在吃东西");
}
@Override
public void drink() {
System.out.println("白种欧洲女人在喝水");
}
}
java
public class WhiteHumanFactory implements HumanFactory{
@Override
public Human createMan() {
return new WhiteMan();
}
@Override
public Human createWoman() {
return new WhiteWoman();
}
}
java
public class Test {
public static void main(String[] args) {
HumanFactory whiteHumanFactory=new WhiteHumanFactory();
Human man2 = whiteHumanFactory.createMan();
Human woman2 = whiteHumanFactory.createWoman();
man2.eat();
man2.drink();
woman2.eat();
woman2.drink();
}
}
有一天女娲觉得光造些男人、女人太单调了,男人、女人之间老是干架,怎么调合一下呢?那就造一些可爱小朋友吧。这时就会发现,如果还使用抽象工厂模式,就会很尴尬:HumanFactory接口里只有造男人、造女人的抽象方法,如果再加一个造小朋友的接口,就要对HuanFactory接口及其实现类具体工厂修改,这就破坏了开闭原则。如果除了造黑人、白人、黄人外,还想造点绿巨人,其实倒还好,和增加造白人的逻辑一样。这就是在决定是否使用抽象工厂模式的秘密所在,如果要扩展的是一个产品族,比较简单且适用,如果想丰富某一产品族的某个产品系列,就比较麻烦了;
抽象工厂应用场景
有没有发现这种方式非常好?完全符合开闭原则,无须对原有系统进行修改;下面就总结一下抽象工厂模式的实际应用场景有哪些?
- 产品组装:在现实生活中,抽象工厂模式最典型的例子就是产品的组装。例如,在电脑的组装过程中,我们通常需要选择一系列的配件,如CPU、硬盘、内存、主板、电源、机箱等。选择不同的配件,组装出来的电脑是不同的,这就可以看作是在使用不同的工厂来生产不同的产品。
- 提供产品类库:如果我们需要提供一个产品类库,但只想显示它们的接口而不是实现,那么就可以使用抽象工厂模式。例如,在设计一个绘图软件时,我们可能希望提供一个图形类库,但并不想暴露出每个图形的具体实现细节,这时就可以使用抽象工厂模式来生成各种图形对象。
- 多产品系列:如果一个系统需要由多个产品系列中的一个来配置,那么也可以使用抽象工厂模式。例如,在汽车制造中,可能需要根据用户的选择来配置不同的汽车模型。
- 独立于产品的创建、组合和表示:如果一个系统需要独立于它的产品的创建、组合和表示,那么也可以使用抽象工厂模式。例如,在开发一个游戏引擎时,我们可能希望游戏开发者能够使用我们提供的引擎来创建各种类型的游戏,而无需关心游戏引擎底层的具体实现。
总结
抽象工厂模式是一种设计程序时的工程方法,前面也说过,方法是为了解决问题的,不是灵丹妙药,用上了就一定很好,是绝不能生搬硬套的,重在理解和灵活运用。
对于抽象工厂模式有以下优点:
- 隔离了具体类的生成,使得客户端并不需要知道什么被创建。
- 当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象。
- 增加新的产品族很方便,无须修改已有系统,符合开闭原则。
抽象工厂模式也有一些缺点:
- 增加新的产品等级结构麻烦,需要对原有系统进行较大的修改,甚至需要修改抽象层代码,违背了开闭原则。
- 如果客户端需要创建多个产品族的多个对象,那么代码会变得很冗长和复杂。
- 如果产品族之间有依赖关系,那么需要仔细考虑如何处理这些依赖关系,以避免出现循环依赖或其他问题。
因此,在使用抽象工厂模式时,需要根据具体的情况来考虑其优缺点,并决定是否使用该模式。