意图
工厂方法模式是一种创建型设计模式, 其在父类中提供一个创建对象的方法, 允许子类决定实例化对象的类型。
问题
假设你正在开发一款物流管理应用。1.0版本只能支持处理卡车运输,因此大部分的代码都位于名为Truck的类中。随着业务越来越广泛,该应用需要支持处理轮船运输。但是,由于大部分的代码都在Truck类中,那么添加Ship类则需要修改全部代码。
更糟糕的是,每添加一种新的运输方式,就需要对代码进行一次大规模的修改。心力憔悴啊。
解决方案
工厂方法模式建议使用特殊的工厂方法代替对于对象构造函数的直接调用 (即使用 new运算符)。*(对象仍将通过 new运算符创建, 只是该运算符改在工厂方法中调用罢了。 )*工厂方法返回的对象通常被称作 “产品”。
我们通过在子类中重写工厂方法,来改变返回产品的类型。但是,只有当产品具有共同的接口时,子类才能返回不同类型的产品,且基类中的工厂方法还将返回类型声明为这一共有接口。
利用工厂方法,对问题提出解决方案,类图如下:
工厂方法模式结构
- Product:所有产品的共有接口。
- Concrete Products:具体产品,对Product接口的不同实现
- Creator:创建者声明了返回产品对象的工厂方法。 该方法的返回对象类型必须与Product接口相匹配。注意,创建者类包含一些与产品相关的核心业务逻辑。
- Concrete Creators:将会重写基类工厂方法, 使其返回不同类型的具体产品。
对问题的代码实现
#includeusing namespace std;// 产品接口中将声明所有具体产品都必须实现的操作。class Transport {public:~Transport(){}virtual void delivery() const = 0;//不同具体产品对此进行不同实现};// 具体产品需提供产品接口的各种实现。class Truck : public Transport{public:virtual void delivery() const{cout << "卡车:通过盒子运输" << endl;}};class Ship : public Transport {public:virtual void delivery() const{cout << "轮船:通过集装箱运输" << endl;}};// 创建者类声明的工厂方法必须返回一个产品类的对象。创建者的子类通常会提供// 该方法的实现。class Logistics {public:~Logistics(){}//基类工厂方法,返回类型声明为产品共有接口virtual Transport* createTransport() const = 0;// 请注意,创建者的主要职责并非是创建产品。其中通常会包含一些核心业务// 逻辑,这些逻辑依赖于由工厂方法返回的产品对象。子类可通过重写工厂方// 法并使其返回不同类型的产品来间接修改业务逻辑。void plan_delivery() const{//调用工厂方法创建一个产品对象Transport* tran = createTransport();//使用产品tran->delivery();delete tran;}};// 具体创建者将重写工厂方法以改变其所返回的产品类型。class RoadLogistics :public Logistics {public:virtual Transport* createTransport() const{return new Truck();}};class SeaLogistics :public Logistics {public:virtual Transport* createTransport() const {return new Ship();}};//客户端代码void ClientCode(const Logistics* log) {cout << "开始运输:";log->plan_delivery();}int main() {cout << "APP:以卡车运输" << endl;Logistics* log1 = new RoadLogistics();ClientCode(log1);cout << endl;cout << "APP:以轮船运输" << endl;Logistics* log2 = new SeaLogistics();ClientCode(log2);delete log1;delete log2;return 0;}
该模式的优缺点
优点
- 避免创建者和具体产品之间的紧耦合
- 符合单一职责原则。将产品创建代码放在程序的单一位置,从而使代码易于维护
- 符合开闭原则。无需更改现有客户端代码,就可以在程序中引入新的产品类型
缺点
- 工厂方法模式需要引入许多新的子类, 代码可能会因此变得更复杂。 最好的情况是将该模式引入创建者类的现有层次结构中。