1. 抽象工厂模式简介
抽象工厂设计模式是创建型模式之一。抽象工厂模式与工厂模式几乎相似,只是它更像工厂中的工厂。
如果您熟悉Java 中的工厂设计模式
,或看过上一篇我写的“java简单工厂模式”,您会注意到我们有一个工厂类。此工厂类根据提供的输入返回不同的子类,工厂类使用 if-else 或 switch 语句来实现这一点。
但是在抽象工厂模式中,我们摆脱了 if-else
块,并为每个子类设置了一个工厂类。然后是一个抽象工厂类,它将根据输入的工厂类返回子类。起初,这似乎令人困惑,但一旦您看到实现,就很容易掌握和理解工厂和抽象工厂模式之间的细微差别。就像我们的工厂模式帖子一样,我们将使用相同的超类和子类。
2. 抽象工厂模式的原理
抽象工厂模式的角色包括:
- 抽象产品:定义了产品的规范和接口。
- 具体产品:实现了抽象产品的接口,是实际的产品对象。
- 抽象工厂:提供了创建产品的接口,但不负责实现这些接口。
抽象工厂模式通过提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类,实现了产品的创建与使用的分离。这种模式特别适用于那些需要创建多个相关产品对象的场景,如组装电脑时选择CPU、硬盘、内存等配件,或者创建一个包含多种类型产品的产品族,如手机和电脑等电子产品。通过抽象工厂模式,可以更好地管理这些产品的创建和组合,提高系统的可维护性和可扩展性。
3. 抽象工厂的使用场景
下面,整理出了4中典型的关于“抽象工厂模式”的使用场景介绍。
3.1 系统独立于产品创建、组合和表示
当系统需要独立于其产品的创建、组合和表示时,抽象工厂模式可以提供一个统一的接口来创建一系列相关的产品对象,而不必关心具体的实现细节。
3.2 多个产品系列配置
如果一个系统需要由多个产品系列中的一个来配置,抽象工厂模式可以提供一个统一的接口来创建这些产品系列中的对象,确保系统的一致性和可维护性。
3.3 强调一系列相关的产品对象设计
当需要强调一系列相关的产品对象的设计以便进行联合使用时,抽象工厂模式可以确保这些对象一起被正确地创建和使用,以满足特定的功能需求。
3.4 提供产品类库的接口而非实现:
如果提供一个产品类库,而只想显示它们的接口而不是实现时,抽象工厂模式可以通过提供一个统一的接口来创建这些产品对象,使得客户端代码不依赖于具体的实现细节,增加了系统的灵活性和可扩展性。
4. 抽象工厂的代码示例介绍
我们将针对此图进行编程,着重介绍抽象工厂模式。
4.1 抽象工厂设计模式超类和子类
4.1.1 Computer.java
public abstract class Computer {
public abstract String getRAM();
public abstract String getHDD();
public abstract String getCPU();
@Override
public String toString(){
return "RAM= "+this.getRAM()+", HDD="+this.getHDD()+", CPU="+this.getCPU();
}
}
4.1.2 PC.java
public class PC extends Computer {
private String ram;
private String hdd;
private String cpu;
public PC(String ram, String hdd, String cpu){
this.ram=ram;
this.hdd=hdd;
this.cpu=cpu;
}
@Override
public String getRAM() {
return this.ram;
}
@Override
public String getHDD() {
return this.hdd;
}
@Override
public String getCPU() {
return this.cpu;
}
}
4.1.3 Server.java
public class Server extends Computer {
private String ram;
private String hdd;
private String cpu;
public Server(String ram, String hdd, String cpu){
this.ram=ram;
this.hdd=hdd;
this.cpu=cpu;
}
@Override
public String getRAM() {
return this.ram;
}
@Override
public String getHDD() {
return this.hdd;
}
@Override
public String getCPU() {
return this.cpu;
}
}
4.2 编写每个子类的工厂类
首先我们需要创建一个抽象工厂接口或抽象类,如上图这个抽象给抽象工厂类叫ComputerAbstractFactory.java
。
public interface ComputerAbstractFactory {
public Computer createComputer();
}
注意该createComputer()
方法返回的是超类的一个实例Computer
。现在我们的工厂类将实现此接口并返回各自的子类。
4.2.1 PCFactory.java
public class PCFactory implements ComputerAbstractFactory {
private String ram;
private String hdd;
private String cpu;
public PCFactory(String ram, String hdd, String cpu){
this.ram=ram;
this.hdd=hdd;
this.cpu=cpu;
}
@Override
public Computer createComputer() {
return new PC(ram,hdd,cpu);
}
}
类似地,我们将有一个用于Server子类的工厂类。ServerFactory.java
。
4.2.2 ServerFactory.java
public class ServerFactory implements ComputerAbstractFactory {
private String ram;
private String hdd;
private String cpu;
public ServerFactory(String ram, String hdd, String cpu){
this.ram=ram;
this.hdd=hdd;
this.cpu=cpu;
}
@Override
public Computer createComputer() {
return new Server(ram,hdd,cpu);
}
}
现在我们将创建一个消费者类,为客户端类创建子类提供入口点。ComputerFactory.java
。
public class ComputerFactory {
public static Computer getComputer(ComputerAbstractFactory factory){
return factory.createComputer();
}
}
注意,它是一个简单的类,getComputer
方法接受ComputerAbstractFactory
参数并返回Computer
对象。此时实现应该已经很清晰了。
让我们编写一个简单的测试方法,看看如何使用抽象工厂来获取子类的实例。TestDesignPatterns.java
。
public class TestDesignPatterns {
public static void main(String[] args) {
testAbstractFactory();
}
private static void testAbstractFactory() {
Computer pc = com.journaldev.design.abstractfactory.ComputerFactory.getComputer(new PCFactory("2 GB","500 GB","2.4 GHz"));
Computer server = com.journaldev.design.abstractfactory.ComputerFactory.getComputer(new ServerFactory("16 GB","1 TB","2.9 GHz"));
System.out.println("AbstractFactory PC Config::"+pc);
System.out.println("AbstractFactory Server Config::"+server);
}
}
上述程序的输出将是:
AbstractFactory PC Config::RAM= 2 GB, HDD=500 GB, CPU=2.4 GHz
AbstractFactory Server Config::RAM= 16 GB, HDD=1 TB, CPU=2.9 GHz
5. 抽象工厂模式优缺点
抽象工厂模式的优点包括封装性和解耦性、产品族一致性、易于替换和扩展、提高系统灵活性,而缺点则包括复杂性增加、不易于新增产品、不支持单一产品的变化。
5.1 抽象工厂模式优点
5.1.1 封装性和解耦性:
抽象工厂模式将对象的创建和使用分离,客户端代码不需要关心具体的产品类,从而实现了解耦。客户端代码只需要依赖抽象工厂接口,而不依赖具体产品。
5.1.2 产品族一致性
抽象工厂模式确保一组相关或依赖的产品能够协同工作,因为每个具体工厂类都会创建一整套相关产品。这有助于保持产品之间的一致性。
5.1.3 易于替换和扩展
通过添加新的具体工厂类和产品类,可以轻松扩展抽象工厂模式,而不需要修改现有的客户端代码。这使得系统更易于维护和扩展。
5.1.4 提高系统灵活性
抽象工厂模式允许在运行时切换不同的具体工厂,从而使应用程序更容易适应不同的配置或环境,提高了系统的灵活性。
5.2 抽象工厂模式缺点
5.2.1 复杂性增加:
抽象工厂模式引入了多个抽象类和接口,以及多个具体工厂和产品类,因此可能会增加系统的复杂性。对于小规模应用程序或简单的需求,可能显得过于繁琐。
5.2.2 不易于新增产品
如果需要新增一种产品,抽象工厂模式的修改会比较复杂,因为需要同时修改抽象工厂接口和所有具体工厂类。这可能会导致修改的传播,影响到现有的代码。
5.2.3 不支持单一产品的变化
抽象工厂模式适用于一组相关产品的创建,但如果只有一个产品发生变化,那么整个工厂都需要进行修改,可能不够灵活。
5.3 抽象工厂模式优缺点总结
总的来说,抽象工厂模式适用于需要创建一组相关产品,同时具备高度灵活性和可维护性的场景。
6. JDK 中的抽象工厂设计模式示例
javax.xml.parsers.DocumentBuilderFactory#newInstance()
javax.xml.transform.TransformerFactory#newInstance()
javax.xml.xpath.XPathFactory#newInstance()
7. 总结
抽象工厂模式是一种设计模式,它属于创建型模式,主要用于构建产品族。这种模式当有多个抽象角色时使用,向客户端提供一个接口,使得客户端在不必指定产品的具体情况下,能够创建多个产品族中的产品对象。抽象工厂模式是所有工厂模式中最抽象和最具一般性的一种形态,它针对的是多个产品族结构,而不是单个产品系列结构。
在抽象工厂模式中,每一个工厂负责创建一个产品族,而不是单个产品。这与工厂方法模式不同,后者是针对单个产品系列的。抽象工厂模式的实现原理包括将一组相关或相互依赖的对象抽象成产品族,每个工厂负责创建一个产品族。这种模式允许在不修改客户端代码的情况下增加新的产品族,同时保持了代码的灵活性和可扩展性。
抽象工厂模式的角色包括:
- 抽象产品:定义了产品的规范和接口。
- 具体产品:实现了抽象产品的接口,是实际的产品对象。
- 抽象工厂:提供了创建产品的接口,但不负责实现这些接口。
抽象工厂模式的优点包括:
- 灵活性:可以轻松扩展新的工厂类,支持新的主题或对象类型。
- 可维护性:将对象的创建与使用分离,使代码更清晰、易于维护。
- 可靠性:避免对象的不正确创建和使用,提高代码可靠性。
然而,抽象工厂模式也存在一些缺点,例如不利于添加新种类产品,因为每增加一个新产品种类可能需要修改现有的工厂类,这违反了开闭原则。