工厂模式属于创建型模式,大致可以分为三类,简单工厂模式、工厂方法模式、抽象工厂模式。
1. 简单工厂模式(Simple Factory)
在面向对象系统设计中经常可以遇到以下的两类问题:
1)为了提高内聚(Cohesion)和松耦合(Coupling),我们经常会抽象出一些类的公共接口以形成抽象基类或者接口。这样我们可以通过声明一个指向基类的指针来指向实际的子类实现,达到了多态的目的。这里很容易出现的一个问题n多的子类继承自抽象基类,我们不得不在每次要用到子类的地方就编写诸如new ×××;的代码。这里带来两个问题1)客户程序员必须知道实际子类的名称(当系统复杂后,命名将是一个很不好处理的问题,为了处理可能的名字冲突,有的命名可能并不是具有很好的可读性和可记忆性,就姑且不论不同程序员千奇百怪的个人偏好了。),2)程序的扩展性和维护变得越来越困难。
2)还有一种情况就是在父类中并不知道具体要实例化哪一个具体的子类。这里的意思为:假设我们在类A中要使用到类B,B是一个抽象父类,在A中并不知道具体要实例化那一个B的子类,但是在类A的子类D中是可以知道的。在A中我们没有办法直接使用类似于new ×××的语句,因为根本就不知道×××是什么。
以上两个问题也就引出了Factory模式的两个最重要的功能:
1)定义创建对象的接口,封装了对象的创建;
2)使得具体化类的工作延迟到了子类中。
我们通常使用Factory模式来解决上面给出的两个问题。在第一个问题中,我们经常就是声明一个创建对象的接口,并封装了对象的创建过程。Factory这里类似于一个真正意义上的工厂(生产对象)。在第二个问题中,我们需要提供一个对象创建对象的接口,并在子类中提供其具体实现(因为只有在子类中可以决定到底实例化哪一个类)。
简单工厂模式(Simple Factory Pattern):又称为静态工厂方法(Static Factory Method)模式,它属于类创建型模式。在简单工厂模式中,可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。
ULM图:
简单工厂模式包含如下角色:
• Factory:工厂角色
• Product:抽象产品角色
• ConcreteProduct:具体产品角色
模式分析
将对象的创建和对象本身业务处理分离可以降低系统的耦合度,使得两者修改起来都相对容易。
在调用工厂类的工厂方法时,由于工厂方法是静态方法,使用起来很方便,可通过类名直接调用,而且只需要传入一个简单的参数即可,在实际开发中,还可以在调用时将所传入的参数保存在XML等格式的配置文件中,修改参数时无须修改任何源代码。
简单工厂模式最大的问题在于工厂类的职责相对过重,增加新的产品需要修改工厂类的判断逻辑,这一点与开闭原则是相违背的。
简单工厂模式的要点在于:当你需要什么,只需要传入一个正确的参数,就可以获取你所需要的对象,而无须知道其创建细节。
实例一:简单电视机工厂
实现代码如下:
// 简单工厂模式.cpp
#include <iostream>
using namespace std;//抽象产品类TV
class TV
{
public:virtual void play() = 0;
};//具体产品类HaierTV
class HaierTV:public TV
{
public:void play() override{cout << "海尔电视播放中..." << endl;}
};//具体产品类HisenseTV
class HisenseTV:public TV
{
public:void play() override{cout << "海信电视播放中..." << endl;}
};//工厂类TVFactory
class TVFactory
{
public:enum TV_TYPE //switch后表达式必须是整数型或枚举型{Haier,Hisense};TV* productTV(TV_TYPE brand){TV* tv = nullptr;switch (brand){case TV_TYPE::Haier:tv = new HaierTV();break;case TV_TYPE::Hisense:tv = new HisenseTV();break;}return tv;}
};int main()
{TVFactory *tvFactory = new TVFactory();TV *tv = tvFactory->productTV(TVFactory::Haier);tv->play();tv = tvFactory->productTV(TVFactory::Hisense);tv->play();return 0;
}
模式优缺点:
工厂类含有必要的判断逻辑,可以决定在什么时候创建哪一个产品类的实例,客户端可以免除直接创建产品对象的责任,而仅仅“消费”产品;简单工厂模式通过这种做法实现了对责任的分割,它提供了专门的工厂类用于创建对象。
•客户端无须知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可,对于一些复杂的类名,通过简单工厂模式可以减少使用者的记忆量。
•通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性。
缺点
•由于工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个系统都要受到影响。
•使用简单工厂模式将会增加系统中类的个数,在一定程序上增加了系统的复杂度和理解难度。
•系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,在产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展和维护。
•简单工厂模式由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构。
模式适用环境
在以下情况下可以使用简单工厂模式:
•工厂类负责创建的对象比较少:由于创建的对象较少,不会造成工厂方法中的业务逻辑太过复杂。
•客户端只知道传入工厂类的参数,对于如何创建对象不关心:客户端既不需要关心创建细节,甚至连类名都不需要记住,只需要知道类型所对应的参数。
摘自:https://www.cnblogs.com/WindSun/p/10252425.html
2. 工厂方法模式(Factory Method)
工厂方法模式(Factory Method Pattern)又称为工厂模式,也叫虚拟构造器(Virtual Constructor)模式或者多态工厂(Polymorphic Factory)模式,它属于类创建型模式。在工厂方法模式中,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。
ULM图
工厂方法模式包含如下角色:
• Product:抽象产品
• ConcreteProduct:具体产品
• Factory:抽象工厂
• ConcreteFactory:具体工厂
模式分析
工厂方法模式是简单工厂模式的进一步抽象和推广。由于使用了面向对象的多态性,工厂方法模式保持了简单工厂模式的优点,而且克服了它的缺点。在工厂方法模式中,核心的工厂类不再负责所有产品的创建,而是将具体创建工作交给子类去做。这个核心类仅仅负责给出具体工厂必须实现的接口,而不负责哪一个产品类被实例化这种细节,这使得工厂方法模式可以允许系统在不修改工厂角色的情况下引进新产品。
当系统扩展需要添加新的产品对象时,仅仅需要添加一个具体产品对象以及一个具体工厂对象,原有工厂对象不需要进行任何修改,也不需要修改客户端,很好地符合了“开闭原则”。而简单工厂模式在添加新产品对象后不得不修改工厂方法,扩展性不好。工厂方法模式退化后可以演变成简单工厂模式。
实例一:电视机工厂
将原有的工厂进行分割,为每种品牌的电视机提供一个子工厂,海尔工厂专门负责生产海尔电视机,海信工厂专门负责生产海信电视机,如果需要生产TCL电视机或创维电视机,只需要对应增加一个新的TCL工厂或创维工厂即可,原有的工厂无须做任何修改,使得整个系统具有更加的灵活性和可扩展性。
实现源码:
// 工厂方法模式
#include <iostream>
using namespace std;//抽象产品TV
class TV
{
public:virtual void play() = 0;
};//具体产品类 HaierTV
class HaierTV:public TV
{
public:void play() override{cout << "海尔电视机播放中..." << endl;}
};//具体产品类HisenseTV
class HisenseTV:public TV
{
public:void play() override{cout << "海信电视机播放中..." << endl;}
};//抽象工厂类 TVFactory
class TVFactory
{
public:virtual TV* productTV() = 0;
};//具体工厂类 HaierTVFactory
class HaierTVFactory:public TVFactory
{
public:TV* productTV() override{cout << "海尔工厂生产海尔电视机..." << endl;return new HaierTV();}
};//具体工厂类 HisenseTVFactory
class HisenseTVFactory:public TVFactory
{
public:TV* productTV() override{cout << "海信工厂生产海信电视机..." << endl;return new HisenseTV();}
};//客户端
int main()
{TV* tv = nullptr;TVFactory* factory = nullptr;factory = new HaierTVFactory();tv = factory->productTV();tv->play();factory = new HisenseTVFactory();tv = factory->productTV();tv->play();return 0;
}
模式优缺点
优点
• 在工厂方法模式中,工厂方法用来创建客户所需要的产品,同时还向客户隐藏了哪种具体产品类将被实例化这一细节,用户只需要关心所需产品对应的工厂,无须关心创建细节,甚至无须知道具体产品类的类名。
• 基于工厂角色和产品角色的多态性设计是工厂方法模式的关键。它能够使工厂可以自主确定创建何种产品对象,而如何创建这个对象的细节则完全封装在具体工厂内部。工厂方法模式之所以又被称为多态工厂模式,是因为所有的具体工厂类都具有同一抽象父类。
• 使用工厂方法模式的另一个优点是在系统中加入新产品时,无须修改抽象工厂和抽象产品提供的接口,无须修改客户端,也无须修改其他的具体工厂和具体产品,而只要添加一个具体工厂和具体产品就可以了。这样,系统的可扩展性也就变得非常好,完全符合“开闭原则”。
缺点
• 在添加新产品时,需要编写新的具体产品类,而且还要提供与之对应的具体工厂类,系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,有更多的类需要编译和运行,会给系统带来一些额外的开销。
• 由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要用到DOM、反射等技术,增加了系统的实现难度。
模式适用环境
在以下情况下可以使用工厂方法模式:
• 一个类不知道它所需要的对象的类:在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可,具体的产品对象由具体工厂类创建;客户端需要知道创建具体产品的工厂类。
• 一个类通过其子类来指定创建哪个对象:在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展。
• 将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定,可将具体工厂类的类名存储在配置文件或数据库中。
摘自:https://www.cnblogs.com/WindSun/p/10252800.html
3. 抽象工厂模式(Abstract Factory)
模式动机
在工厂方法模式中具体工厂负责生产具体的产品,每一个具体工厂对应一种具体产品,工厂方法也具有唯一性,一般情况下,一个具体工厂中只有一个工厂方法或者一组重载的工厂方法。但是有时候我们需要一个工厂可以提供多个产品对象,而不是单一的产品对象。
为了更清晰地理解工厂方法模式,需要先引入两个概念:
• 产品等级结构:产品等级结构即产品的继承结构,如一个抽象类是电视机,其子类有海尔电视机、海信电视机、TCL电视机,则抽象电视机与具体品牌的电视机之间构成了一个产品等级结构,抽象电视机是父类,而具体品牌的电视机是其子类。
• 产品族:在抽象工厂模式中,产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品,如海尔电器工厂生产的海尔电视机、海尔电冰箱,海尔电视机位于电视机产品等级结构中,海尔电冰箱位于电冰箱产品等级结构中。
当系统所提供的工厂所需生产的具体产品并不是一个简单的对象,而是多个位于不同产品等级结构中属于不同类型的具体产品时需要使用抽象工厂模式。
抽象工厂模式是所有形式的工厂模式中最为抽象和最具一般性的一种形态。
抽象工厂模式与工厂方法模式最大的区别在于,工厂方法模式针对的是一个产品等级结构,而抽象工厂模式则需要面对多个产品等级结构,一个工厂等级结构可以负责多个不同产品等级结构中的产品对象的创建 。当一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象时,抽象工厂模式比工厂方法模式更为简单、有效率。
模式定义
抽象工厂模式(Abstract Factory Pattern):提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。抽象工厂模式又称为Kit模式,属于对象创建型模式。
ULM图
抽象工厂模式包含如下角色:
• AbstractFactory:抽象工厂
• ConcreteFactory:具体工厂
• AbstractProduct:抽象产品
• Product:具体产品
模式分析
模式实例与解析
实例一:电器工厂
• 一个电器工厂可以产生多种类型的电器,如海尔工厂可以生产海尔电视机、海尔空调等,TCL工厂可以生产TCL电视机、TCL空调等,相同品牌的电器构成一个产品族,而相同类型的电器构成了一个产品等级结构,现使用抽象工厂模式模拟该场景。
实现源码:
// 抽象工厂模式
#include <iostream>
using namespace std;//抽象产品类 Television
class Television
{
public:virtual void play() = 0;
};//具体产品类 HaierTelevision
class HaierTelevision:public Television
{
public:void play() override{cout << "海尔电视播放中..." << endl;}
};//具体产品类 TCLTelevision
class TCLTelevision : public Television
{
public:void play() override{cout << "TCL电视播放中..." << endl;}
};//抽象产品 AirConditioner
class AirConditioner
{
public:virtual void changeTemperature() = 0;
};//具体产品 HaierAirConditioner
class HaierAirConditioner : public AirConditioner
{
public:void changeTemperature() override{cout << "海尔空调温度改变中..." << endl;}
};//具体产品 TCLAirConditioner
class TCLAirConditioner : public AirConditioner
{
public:void changeTemperature() override{cout << "TCL空调温度改变中..." << endl;}
};//抽象工厂 EFactory
class EFactory
{
public:virtual Television* productTelevision() = 0;virtual AirConditioner* productAirConditioner() = 0;
};//具体工厂 HaierFactory
class HaierFactory : public EFactory
{
public:Television* productTelevision() override{return new HaierTelevision();}AirConditioner* productAirConditioner() override{return new HaierAirConditioner();}
};//具体工厂 TCLFactory
class TCLFactory : public EFactory
{
public:Television* productTelevision() override{return new TCLTelevision();}AirConditioner* productAirConditioner() override{return new TCLAirConditioner();}
};//客户端
int main()
{EFactory* factory;Television* tv;AirConditioner* ac;factory = new HaierFactory();tv = factory->productTelevision();tv->play();ac = factory->productAirConditioner();ac->changeTemperature();factory = new TCLFactory();tv = factory->productTelevision();tv->play();ac = factory->productAirConditioner();ac->changeTemperature();return 0;
}
模式优缺点
优点
• 抽象工厂模式隔离了具体类的生成,使得客户并不需要知道什么被创建。由于这种隔离,更换一个具体工厂就变得相对容易。所有的具体工厂都实现了抽象工厂中定义的那些公共接口,因此只需改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。另外,应用抽象工厂模式可以实现高内聚低耦合的设计目的,因此抽象工厂模式得到了广泛的应用。
• 当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象。这对一些需要根据当前环境来决定其行为的软件系统来说,是一种非常实用的设计模式。
• 增加新的具体工厂和产品族很方便,无须修改已有系统,符合“开闭原则”。
缺点
• 在添加新的产品对象时,难以扩展抽象工厂来生产新种类的产品,这是因为在抽象工厂角色中规定了所有可能被创建的产品集合,要支持新种类的产品就意味着要对该接口进行扩展,而这将涉及到对抽象工厂角色及其所有子类的修改,显然会带来较大的不便。
• 开闭原则的倾斜性(增加新的工厂和产品族容易,增加新的产品等级结构麻烦)
模式适用环境
在以下情况下可以使用抽象工厂模式:
• 一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有类型的工厂模式都是重要的。
• 系统中有多于一个的产品族,而每次只使用其中某一产品族。
• 属于同一个产品族的产品将在一起使用,这一约束必须在系统的设计中体现出来。
• 系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现。
摘自:https://www.cnblogs.com/WindSun/p/10253248.html
以下内容摘自:https://blog.csdn.net/wuzhekai1985/article/details/6660462
首先介绍简单工厂模式,它的主要特点是需要在工厂类中做判断,从而创造相应的产品。当增加新的产品时,就需要修改工厂类。有点抽象,举个例子就明白了。有一家生产处理器核的厂家,它只有一个工厂,能够生产两种型号的处理器核。客户需要什么样的处理器核,一定要显示地告诉生产工厂。下面给出一种实现方案。
ULM图:
实现代码:
enum CTYPE {COREA, COREB};
class SingleCore
{
public: virtual void Show() = 0;
};
//单核A
class SingleCoreA: public SingleCore
{
public: void Show() { cout<<"SingleCore A"<<endl; }
};
//单核B
class SingleCoreB: public SingleCore
{
public: void Show() { cout<<"SingleCore B"<<endl; }
};
//唯一的工厂,可以生产两种型号的处理器核,在内部判断
class Factory
{
public: SingleCore* CreateSingleCore(enum CTYPE ctype) { if(ctype == COREA) //工厂内部判断 return new SingleCoreA(); //生产核A else if(ctype == COREB) return new SingleCoreB(); //生产核B else return NULL; }
};
这样设计的主要缺点之前也提到过,就是要增加新的核类型时,就需要修改工厂类。这就违反了开放封闭原则:软件实体(类、模块、函数)可以扩展,但是不可修改。于是,工厂方法模式出现了。所谓工厂方法模式,是指定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使一个类的实例化延迟到其子类。
听起来很抽象,还是以刚才的例子解释。这家生产处理器核的产家赚了不少钱,于是决定再开设一个工厂专门用来生产B型号的单核,而原来的工厂专门用来生产A型号的单核。这时,客户要做的是找好工厂,比如要A型号的核,就找A工厂要;否则找B工厂要,不再需要告诉工厂具体要什么型号的处理器核了。下面给出一个实现方案。
ULM图:
实现代码:
class SingleCore
{
public: virtual void Show() = 0;
};
//单核A
class SingleCoreA: public SingleCore
{
public: void Show() { cout<<"SingleCore A"<<endl; }
};
//单核B
class SingleCoreB: public SingleCore
{
public: void Show() { cout<<"SingleCore B"<<endl; }
};
class Factory
{
public: virtual SingleCore* CreateSingleCore() = 0;
};
//生产A核的工厂
class FactoryA: public Factory
{
public: SingleCoreA* CreateSingleCore() { return new SingleCoreA; }
};
//生产B核的工厂
class FactoryB: public Factory
{
public: SingleCoreB* CreateSingleCore() { return new SingleCoreB; }
};
工厂方法模式也有缺点,每增加一种产品,就需要增加一个对象的工厂。如果这家公司发展迅速,推出了很多新的处理器核,那么就要开设相应的新工厂。在C++实现中,就是要定义一个个的工厂类。显然,相比简单工厂模式,工厂方法模式需要更多的类定义。
既然有了简单工厂模式和工厂方法模式,为什么还要有抽象工厂模式呢?它到底有什么作用呢?还是举这个例子,这家公司的技术不断进步,不仅可以生产单核处理器,也能生产多核处理器。现在简单工厂模式和工厂方法模式都鞭长莫及。抽象工厂模式登场了。它的定义为提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。具体这样应用,这家公司还是开设两个工厂,一个专门用来生产A型号的单核多核处理器,而另一个工厂专门用来生产B型号的单核多核处理器,下面给出实现的代码。
ULM图:
实现代码:
//单核
class SingleCore
{
public: virtual void Show() = 0;
};
class SingleCoreA: public SingleCore
{
public: void Show() { cout<<"Single Core A"<<endl; }
};
class SingleCoreB :public SingleCore
{
public: void Show() { cout<<"Single Core B"<<endl; }
};
//多核
class MultiCore
{
public: virtual void Show() = 0;
};
class MultiCoreA : public MultiCore
{
public: void Show() { cout<<"Multi Core A"<<endl; } };
class MultiCoreB : public MultiCore
{
public: void Show() { cout<<"Multi Core B"<<endl; }
};
//工厂
class CoreFactory
{
public: virtual SingleCore* CreateSingleCore() = 0;virtual MultiCore* CreateMultiCore() = 0;
};
//工厂A,专门用来生产A型号的处理器
class FactoryA :public CoreFactory
{
public: SingleCore* CreateSingleCore() { return new SingleCoreA(); } MultiCore* CreateMultiCore() { return new MultiCoreA(); }
};
//工厂B,专门用来生产B型号的处理器
class FactoryB : public CoreFactory
{
public: SingleCore* CreateSingleCore() { return new SingleCoreB(); } MultiCore* CreateMultiCore() { return new MultiCoreB(); }
};