继续上节,这回我们讲下抽象工厂模式,抽象工厂模式是工厂模式(简单工厂、工厂方法)中最具抽象和一般性的一种形态。抽象工厂模式可以向客户端提供一个接口,使客户端在不必指定产品的具体的情况下,创建多个产品族中的产品对象。
1、定义
抽象工厂模式的定义:为创建一组相关或相互依赖的对象提供一个接口,而且无须指定它们的具体类。
通俗一点理解,即对一组具有相同主题的工厂进行封装。比如说:生产一台 PC 机,如果使用工厂方法模式的话,就会有主板工厂、显卡工厂、CPU 工厂等,而使用抽象工厂的话,只会有一个 PC 工厂,而 PC 工厂中又涵盖了主板工厂、显卡工厂、CPU 工厂。所以,工厂方法模式针对的是同一类或同等级产品,而抽象工厂模式针对的是多种类(多等级)的产品设计。
2、何为产品族
上面我们讲到一组相关或相互依赖的对象,这个怎么理解?比如,PC 机的主板和显卡就是两个相互依赖的产品线(也叫做产品族)。
3、模式特点
抽象工厂模式使用时,一般具有如下特点:
- 系统中有多个产品族,每个具体工厂负责创建同一族但属于不同产品等级(产品种类)的产品
- 系统一次只能消费某一族产品,即相同产品族的产品是一起被使用的
当系统需要新增一个产品族时,只需要增加新的工厂类即可,无需修改源代码;但是如果需要产品族中增加一个新种类的产品时,则所有的工厂类都需要修改。
抽象工厂模式中的抽象工厂类的职责就是定义每个工厂要实现的功能,即定义多个产品族的产品的创建。这里,同一产品族下有多个产品时,对应的抽象工厂就会有多个抽象方法用来提供创建这些产品的接口。
4、组成角色
抽象工厂一般包含四种角色,分别是:
- 抽象工厂(Abstract Factory):提供了创建产品的接口,包含多个创建产品的方法,即包含多个类似 new Product () 的方法;
- 具体工厂(Concrete Factory):实现抽象工厂定义的接口,完成某个具体产品的创建;
- 抽象产品(Abstract Product):抽象产品定义,一般有多少抽象产品,抽象工厂中就包含多少个创建产品的方法;
- 具体产品(Concrete Product):抽象产品的实现类。
实现抽象产品所定义的接口,和具体工厂是多对一的关系:
5、使用实例
这里继续以上面的冰箱、电视为例进行说明,其结构图如下所示:
本例中,使用抽象工厂模式来设计两个工厂,一个是 TCL 厂另一个是美的厂,分别用来生产 TCL 和美的的电视、冰箱这两种产品,工厂类中使用 newTelevision()、newRefrigerator() 分别表示对其的创建。
程序代码如下,先是抽象工厂定义:
代码语言:javascript复制public interface Factory {
Television newTelevision();
Refrigerator newRefrigerator();
}
然后是两个抽象产品:
代码语言:javascript复制public interface Television {
void dosomething();
}
...
public interface Refrigerator {
void dosomething();
}
再就是两个具体工厂类:
代码语言:javascript复制public class TCLFactory implements Factory {
@Override
public Television newTelevision() {
return new TCLTelevision();
}
@Override
public Refrigerator newRefrigerator() {
return new TCLRefrigerator();
}
}
代码语言:javascript复制public class MeiDFactory implements Factory {
@Override
public Television newTelevision() {
return new MeiDTelevision();
}
@Override
public Refrigerator newRefrigerator() {
return new MeiDRefrigerator();
}
}
最后,是四个具体的产品类:
代码语言:javascript复制public class TCLTelevision implements Television {
@Override
public void dosomething() {
System.out.println("我是TCL电视机,我可以看电视");
}
}
...
public class MeiDTelevision implements Television {
@Override
public void dosomething() {
System.out.println("我是美的电视机,我可以看电视");
}
}
...
public class TCLRefrigerator implements Refrigerator {
@Override
public void dosomething() {
System.out.println("我是TCL冰箱,我可以洗衣服");
}
}
...
public class MeiDRefrigerator implements Refrigerator {
@Override
public void dosomething() {
System.out.println("我是美的冰箱,我可以洗衣服");
}
}
下面,就是我们的测试类:
代码语言:javascript复制public class Main {
public static void main(String[] args) {
Factory factory = new TCLFactory();
Television television = factory.newTelevision();
Refrigerator refrigerator = factory.newRefrigerator();
television.dosomething(); // 我是TCL电视机,我可以看电视
refrigerator.dosomething(); // 我是TCL冰箱,我可以洗衣服
factory = new MeiDFactory();
television = factory.newTelevision();
refrigerator = factory.newRefrigerator();
television.dosomething(); // 我是美的电视机,我可以看电视
refrigerator.dosomething(); // 我是美的冰箱,我可以洗衣服
}
}
6、模块拓展
假设某一天,我们要新增一个产品族,比如加一个海尔的电视机和冰箱,这时的做法就是直接新增一个海尔工厂,新增后的结构图如下:
这个时候,就很好的体现了开闭原则,对修改关闭,对拓展开放,不需要修改源代码即可实现功能拓展。
当产品族中需要增加一个新种类的产品时,比如新增产品 “风扇”,新的结构图简单罗列如下:
由图中可以清晰看到,当需要增加一个新的产品时,所有的工厂类都需要进行修改,不满足开闭原则。所以,这也是抽象工厂的一个弊端。
7、总结
本节主要介绍了抽象工厂模式的概念、特点,以及产品族的概念,另外说明了抽象工厂模式的通用设计类图,优缺点等,大家可以好好理解下。