模式定义
简单工厂模式(Simple Factory Pattern)定义为:简单工厂模式又称为静态工厂方法(Static Factory Method)模式,它属于类创建模式。在简单工厂模式中,可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。
模式结构
简单工厂模式包含如下角色:
- Factory(工厂角色)
工厂角色即工厂类,它是简单工厂模式的核心,负责实现创建所有实例的内部逻辑;工厂模式类可以被外界直接调用,创建所需的产品对象;在工厂类中提供了静态的工厂方法 factoryMethod(),它返回一个抽象产品类 Product,所有的具体产品都是抽象产品的子类。 - Product(抽象产品角色)
抽象产品角色是简单工厂模式所创建的所有对象的父类,负责描述所有实例所共有的公共接口,它的引入将提高系统的灵活性,使得在工厂类中只需要定义一个工厂方法,因为所有创建的具体产品对象都是其子类对象。 - ConcreteProduct(具体产品角色)
具体产品角色是简单工厂模式的创建目标,所有创建的对象都充当这个角色的某个具体类的实例。每一个具体产品角色都继承了抽象产品角色,需要实现定义在抽象产品中的抽象方法。
模式分析
在简单工厂模式中,工厂类根据工厂方法所传入的参数来动态的决定应该创建出哪一个产品类的实例。
实例:某销售管理系统支持多种支付方式,如现金支付(CashPay)、信用卡支付(CreditcardPay)、代金券支付(VoucherPay)等,在设计中如果不使用简单工厂模式,可能会存在如下的支付方法:
void pay(string type) {
if(type == "cash") {
//现金处理代码
}
else if(type == "creditcard") {
//信用卡支付处理代码
}
else if(type == "voucher") {
//代金券支付处理代码
}
else {
//......
}
}
由于不同的支付方式其支付处理方法不一致,因此该方法代码将相当冗长,而且每当需要增加新的支付方式时,不得不修改这段 if…else…代码,增加很多新的支付处理代码。代码越长意味着维护工作量越大,测试难度也越大,扩展和修改也约不灵活。因此可以考虑使用简单工厂模式对其进行重构。
通过使用简单工厂模式,可以对原有代码进行如下改进:
- 为了保证系统的扩展性并将各种支付类型对象的创建封装在一个统一的方法中,需要引入抽象支付方式类,它定义了抽象的支付方式,抽象支付方法类定义如下:
class AbstractPay { public: virtual void pay() = 0; };
- 将每一种支付方式封装在一个独立的类中,各个支付方式类相对独立,修改其一对其他类无任何影响,这些独立的支付方式类充当具体产品类的角色,是抽象支付方式类的子类。
class CashPay : public AbstractPay { public: void pay() { cout << "现金处理" << endl; } }; class CreditcardPay : public AbstractPay { public: void pay() { cout << "信用卡处理" << endl; } };
- 提供一个代码相对简单,而且只负责创建对象而不必关心对象细节的工厂类来创建各种具体的支付方式产品类,注意其工厂方法的返回类型都是抽象类型,支付方式工厂类定义如下:
class PayMethod { public: static AbstractPay* getPayMethod(string type) { AbstractPay* res = nullptr; if (type == "cash") { res = new CashPay(); return res; } else if (type == "card") { res = new CreditcardPay(); return res; } } };
通过对原有设计的重构可以发现,在使用了简单工厂模式之后,系统中类的个数增加,每一种支付处理方式都封装到单独的类中,而且工厂类中只有简单的判断逻辑代码,不需要关心具体的业务处理过程,很好的满足了“单一职责原则”。在增加新的支付方式时,只需要添加一个新的具体支付类并实现其中的pay()方法,同时对工厂类PayMethodFactory做简单的修改即可,无需对原有的代码进行大面积的改动。
简单工厂模式最大的问题在于工厂类的职责相对过重,增加新的产品需要修改工厂类的判断逻辑,这一点与开闭原则是相违背的。
简单工厂模式的要点在于:当你需要什么,只需要传入一个正确的参数,就可以获取你所需要的对象,而无需知道其创建细节。
模式优缺点
优点
- 工厂内含有必要的判断逻辑,可以判定在什么时候创建哪一个产品的实例,客户端可以免除直接创建产品对象的责任。而仅仅“消费”产品;简单工厂模式通过这种做法实现了对责任的分割。它提供了专门的工厂类用于创建对象。
- 客户端无需知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可。对于一些复杂的类名,通过简单工厂模式可以减少使用者的记忆量。
- 通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性。
缺点
- 由于工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个系统都要受到影响。
- 使用简单工厂模式将会增加系统中类的个数,在一定程度上增加了系统的复杂度和理解难度。
- 系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,在产品类型较多时,有可能造成工厂逻辑过于复杂。不利于系统的扩展和维护。
- 简单工厂模式由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构。