一、工厂方法模式概述
在前一章的简单工厂模式中,只提供了一个工厂类,该工厂类处于对产品类进行实例化的中心位置,它负责每一个产品子类的创建细节,并决定何时实例化哪一个产品类,当要将新产品加入到系统中时,必须修改工厂类,加入必要的处理逻辑,这违背了软件设计的开闭原则,而下面介绍的工厂方法模式将能很地解决这些问题。
工厂方法模式包含以下角色:
1. 抽象产品(Product):抽象产品是定义产品的接口,是工厂方法模式所创建对象的超类型,也就是产品对象的共同父类或接口。
2. 具体产品(ConcreteProduct):具体产品类实现了抽象产品接口,某种类型的具体产品由专门的具体工厂创建,它们之间一一对应。
3. 抽象工厂(Factory):在抽象工厂中,声明了工厂方法,用于返回一个产品。抽象工厂是工厂方法模式的核心,任何在模式中创建对象的工厂类都必须实现该接口。
4. 具体工厂(ConcteteFactory):具体工厂是抽象工厂类的子类,实现了抽象工厂中定义的工厂方法,并可由用户调用,返回一个具体产品类的实例。
工厂方法模式是简单工厂模式的进一步抽象和推广,它保持了简单工厂模式的优点,而且克服了它的缺点。在工厂方法模式中,核心的工厂类不再负责所有产品的创建,而是将具体创建工作交给子类去做。这个核心类仅仅负责给出具体工厂必须实现的接口,而不负责哪一个产品类被实例化这种细节,这使得工厂方法模式可以允许系统在不修改工厂角色的情况下引进新产品。在工厂方法模式中,工厂类与产品类之间具体平行的等级结构,它们之间一一对应。工厂方法模式的核心是一个抽象工厂类,而简单工厂模式把核心放在一个具体类上,这是这两种模式最明显的区别。对于工厂方法模式,当需要添加一个新的产品对象时,只需添加一个具体产品对象和一个具体工厂对象就可以了,而原有工厂对象不需要进行任何修改,客户端也不需要修改,这很好地符合了软件设计的开闭原则。
假设我们设计出了一些具有某方面特长的人工智能系统的核心模块(如擅长计算的模块、擅长逻辑推理模块的等),但是却没法让这些模块能够协同工作,以模拟人类的大脑。我们希望设计出能够加载不同核心模块的框架,在这个框架里我们可以测试和优化各个不同的核心模块,而且可以加载全新的模块。这时工厂方法模式就可派上用场了,因为使用工厂方法模式设计的框架,当向其中加入新的产品(核心模块)时,无须修改抽象工厂和抽象产品提供的接口,只须添加一个具体工厂和具体产品就可以了,框架的可扩展性很好。
二、工厂方法模式实例
例如下面这个类图描述的模拟人工智能系统,AIFactoryInterface接口是抽象工厂,它描述了具体工厂应该具有的公共属性和方法。CalculateAIFactory类和InferenceAIFactory类都是具体工厂类,它们都实现了AIFactoryInterface接口,它们负责创建具体的产品(智能系统的核心模块)。CalculateAIFactory工厂创建的对象(模块)擅长计算,InferenceAIFactory工厂创建的对象(模块)擅长逻辑推理。AIInterface接口是负责描述具体产品所具体的共同属性和方法,CalculateAI类和InferenceAI类都实现了AIInterface接口,它们是两种不同的具体产品(模块),它们的loadSystem()方法能加载各自不同的核心子系统。CalculateAI类能加载擅长计算的核心,InferenceAI类能加载擅长逻辑推理的核心。最顶层的Agent类是由用户调用的,它将会根据用户需求委托AIFactoryInterface接口创建用户想要的具有特定功能的产品(即生产将具体特定功能的核心模块并加载到框架中)。
1.Java实现版本
1. Agent类(客户类,我在这个类内对这个程序进行了测试)
public class Agent {
public static void main(String args[]){
try{
AIInterface testAI;
AIFactoryInterface factory;
//这个参数可在程序运行时根据用户的输入(或配置文件)确定,即可在程序运行时根据用户的输入或配置文件的信息确定创建哪种类型的AI
factory = new InferenceAIFactory();
testAI = factory.produceAI();
testAI.loadSystem();
}
catch(Exception e){
System.out.println(e.getMessage());
}
}
}
2. AIFactoryInterface接口(描述生产AI工产品的公共接口)
public interface AIFactoryInterface {
public AIInterface produceAI();
}
3. CalculateAIFactory类(生产擅长计算的AI)
public class CalculateAIFactory implements AIFactoryInterface{
@Override
public AIInterface produceAI() {
System.out.println("CalculateAIFactory produce CalculateAI.");
return new CalculateAI();
}
}
4. InferenceAIFactory 类(生产擅长逻辑推理的 AI )
public class InferenceAIFactory implements AIFactoryInterface{
@Override
public AIInterface produceAI() {
System.out.println("InferenceAIFactory produce InferenceAI.");
return new InferenceAI();
}
}
5. AIInterface类(描述AI共有方法和属性)
public interface AIInterface {
public void loadSystem();
}
6. CalculateAI类(加载擅长计算的AI)
public class CalculateAI implements AIInterface{
@Override
public void loadSystem() {
System.out.println("I'm good at calculation.");
}
}
7. InferenceAI类(加载擅长逻辑推理的AI)
public class InferenceAI implements AIInterface{
@Override
public void loadSystem() {
System.out.println("I'm good at logical reasoning.");
}
}
在实际的应用开发中,一般不直接使用new关键字来创建对象,而是将具体类的类名写入配置文件中,再通过Java的反射机制,读取XML格式的配置文件,根据存储在XML文件中的类名字符串生成对象。Java的反射(Java Reflection)可以在程序运行时获取已知名称或已有对象的相关信息,包括类的方法、属性、超类的信息等。在反射中用得最多的是Class类,Class类的实例表示正在运行的Java应用程序中的类和接口。
2.C++实现版本
#include <string>
#include <iostream>
using namespace std;
class AIInterface
{
public:
virtual void loadSystem()
{
}
};
class InferenceAI: public AIInterface
{
public:
virtual void loadSystem()
{
cout << "I'm good at logical reasoning." << endl;
}
};
class CalculateAI: public AIInterface
{
public :
virtual void loadSystem()
{
cout << "I'm good at calculation." << endl;
}
};
class AIFactoryInterface
{
public:
virtual AIInterface* produceAI()
{
}
};
class InferenceAIFactory : public AIFactoryInterface
{
public :
virtual AIInterface* produceAI()
{
return new InferenceAI();
}
};
class CalculateAIFactory : public AIFactoryInterface
{
public:
virtual AIInterface* produceAI()
{
return new CalculateAI();
}
};
int main()
{
//这个参数可在程序运行时根据用户的输入(或配置文件)确定,即可在程序运行时根据用户的输入或配置文件的信息确定创建哪种类型的AI
AIFactoryInterface* factory = new CalculateAIFactory();
AIInterface* testAI = factory->produceAI();
testAI->loadSystem();
delete testAI;
testAI = NULL;
delete factory;
factory = NULL;
return 0;
}
三、分析和总结
下面再总结和深入分析一下工厂方法模式:下图是工厂方法模式的一个最基础的结构图
基于工厂角色和和产品角色的多态性设计是工厂方法模式的关键,它能够使工厂可以自主确定创建哪种产品对象,而如何创建这个对象的细节完全封装在具体工厂内部。在系统加入新的产品时,无须修改抽象工厂和抽象产品的接口,不用修改客户端,也不用修改其他具体工厂和具体产品,而只需要添加一个具体工厂具体产品就行了。这样系统的可扩展性非常好,完全符合开闭原则。但是工厂方法模式也有一些无法避免的缺点:在添加新产品时,需要编写新的具体产品类,而且还要提供与之对应的具体工厂类,系统中的类个数会成对增加,这不仅在一定程度上增加了系统的复杂性,而且要编译和运行的类多了,会给系统带来一些额外的开销。