前言
创建型模式主要关注对象的创建过程,提供了一种创建对象的最佳方式,并隐藏了创建逻辑的细节。本章介绍创建型模式中的工厂方法模式和抽象工厂模式。
设计模式详解
1. 简单工厂模式
又称静态工厂模式,包含一个抽象产品类,多个产品的实现类,一个工厂类。由工厂类的对象决定创建哪一种产品类的实例。
#include <iostream>
#include <string>
#include <memory>
using namespace std;
// 抽象产品类(所有产品的基类)
class Vehicle {
public:
Vehicle() {}
virtual void Wheel() = 0;
};
// 具体产品类
class Car : public Vehicle {
public:
Car() {}
virtual void Wheel() override {
std::cout << "4个轮子" << std::endl;
}
};
class Motorcycle : public Vehicle {
public:
Motorcycle() {}
virtual void Wheel() override {
std::cout << "2个轮子" << std::endl;
}
};
// 工厂类
class Factory {
public:
static std::shared_ptr<Vehicle> Create(const std::string& name) {
if (name == "小汽车") {
return std::make_shared<Car>();
}
else if (name == "摩托车") {
return std::make_shared<Motorcycle>();
}
else {
std::cout << "没有这种产品" << std::endl;
return nullptr;
}
}
};
int main() {
std::shared_ptr<Vehicle> vehicle = Factory::Create("小汽车");
vehicle->Wheel();
vehicle = Factory::Create("摩托车");
vehicle->Wheel();
return 0;
}
优缺点
优点:
只需要传入一个正确的参数,就可以获取你所需要的对象,而无需知道其细节创建。
缺点:
工厂类的职责相对过重,增加新的产品,需要修改工厂类的判断逻辑,违背了开闭原则。
2. 工厂方法模式(Factory Method)
问题
在软件设计中,我们经常遇到需要创建不同类型对象的情况。但是,如果直接在代码中实例化对象,会使代码紧密耦合在一起,难以维护和扩展。此外,如果对象的创建方式需要变化,那么就需要在整个代码中进行大量的修改。工厂方法模式旨在解决这个问题。
解决方案
工厂方法模式又称多态性工厂模式。该模式定义了一个创建对象的接口,但是由子类决定要实例化的类是哪一个,也就是说相比简单工厂模式,工厂方法模式将实例化推迟到了子类中,核心的工厂类不再负责所有产品的创建,而是将具体的工作交给子类去做。
#include <iostream>
#include <string>
#include <memory>
using namespace std;
// 抽象产品类(所有产品的基类)
class Vehicle {
public:
Vehicle() {}
virtual void Wheel() = 0;
};
// 具体产品类
class Car : public Vehicle {
public:
Car() {}
virtual void Wheel() override {
std::cout << "4个轮子" << std::endl;
}
};
class Motorcycle : public Vehicle {
public:
Motorcycle() {}
virtual void Wheel() override {
std::cout << "2个轮子" << std::endl;
}
};
// 工厂类(抽象工厂)
class Factory {
public:
virtual std::shared_ptr<Vehicle> Create() = 0;
};
//工厂类(具体工厂)
class CarFactory : public Factory {
public:
virtual std::shared_ptr<Vehicle> Create() override {
return std::make_shared<Car>();
}
};
class MotorcycleFactory : public Factory {
public:
virtual std::shared_ptr<Vehicle> Create() override {
return std::make_shared<Motorcycle>();
}
};
int main() {
std::shared_ptr<Factory> factory(new CarFactory());
std::shared_ptr<Vehicle> vehicle = factory->Create();
vehicle->Wheel();
factory.reset(new MotorcycleFactory());
vehicle = factory->Create();
vehicle->Wheel();
return 0;
}
优缺点
优点:
用户只需要关心所需产品对应的工厂,无需关心创建细节;加入新产品时符合开闭原则,不需要修改其他的工厂类的代码,提高了可扩展性。
缺点:
类的个数容易过多(每个工厂只负责生产一种产品),增加了复杂度;增加了系统的抽象性和理解难度。
3. 抽象工厂模式
问题
在某些情况下,需要创建一系列相关或相互依赖的对象,这些对象属于一组相关的产品族。同时,系统需要保证这些产品族之间的一致性。如果直接在代码中创建这些对象,会使得代码与具体产品的细节紧密耦合,不利于后续的扩展和维护。
解决方案
抽象工厂模式提供了一个接口,用于创建一系列相关或相互依赖的对象。通过使用抽象工厂接口及其具体实现,可以将对象的创建与客户端代码分离,从而实现系统的松耦合。与工厂方法模式不同的是,工厂方法模式中的工厂只生产单一的产品,而抽象工厂模式中的工厂生产多个产品。
抽象工厂模式涉及多个角色
- 抽象工厂(Abstract Factory):声明了一组用于创建不同产品的抽象方法。具体的工厂类必须实现这些方法来创建具体的产品对象。
- 具体工厂(Concrete Factory):实现抽象工厂接口,负责创建特定种类的产品对象。
- 抽象产品(Abstract Product):定义了产品的通用接口,具体产品必须实现这个接口。
- 具体产品(Concrete Product):实现抽象产品接口,是抽象工厂创建的实际对象。
#include <iostream>
#include <string>
#include <memory>
using namespace std;
// 系列产品1
class Vehicle {
public:
Vehicle(string name) : m_name(name) {}
virtual void Show() = 0;
protected :
string m_name;
};
class Car : public Vehicle {
public:
Car(string name) : Vehicle(name) {}
void Show() override {
cout << "生产了一辆小汽车: " << m_name << endl;
}
};
class Motorcycle :public Vehicle {
public:
Motorcycle(string name) : Vehicle(name) {}
void Show() override {
cout << "生产了一辆摩托车: " << m_name << endl;
}
};
// 系列产品2
class Light {
public:
Light() {}
virtual void Show() = 0;
};
class CarLight :public Light {
public:
void Show() override {
cout << "小汽车灯!" << endl;
}
};
class MotorcycleLight :public Light {
public:
void Show() override {
cout << "摩托车灯!" << endl;
}
};
// 工厂方法 => 抽象工厂(对有一组关联关系的产品簇提供产品对象的统一创建)
class AbstractFactory {
public:
virtual shared_ptr<Vehicle> createVehicle(string name) = 0; // 工厂方法 创建机动车
virtual shared_ptr<Light> createLight() = 0; // 工厂方法 创建水果
};
// 宝马工厂
class BMWFactory :public AbstractFactory {
public:
shared_ptr<Vehicle> createVehicle(string name) override {
return make_shared<Car>(name);
}
shared_ptr<Light> createLight() override {
return make_shared<CarLight>();
}
};
// 哈雷工厂
class HarleyFactory :public AbstractFactory {
public:
shared_ptr<Vehicle> createVehicle(string name) override {
return make_shared<Motorcycle>(name);
}
shared_ptr<Light> createLight() override {
return make_shared<MotorcycleLight>();
}
};
int main() {
shared_ptr<AbstractFactory> bmwFac (new BMWFactory());
shared_ptr<Vehicle> vehicle = bmwFac->createVehicle("汗血宝马");
vehicle->Show();
shared_ptr<Light> light = bmwFac->createLight();
light->Show();
shared_ptr<AbstractFactory> harleyFac(new HarleyFactory());
vehicle = harleyFac->createVehicle("地狱摩托");
vehicle->Show();
light = harleyFac->createLight();
light->Show();
return 0;
}
适用场景
抽象工厂模式最早的应用是用于创建属于不同操作系统的视窗构件。如 java 的 AWT 中的 Button 和 Text 等构件在 Windows 和 UNIX 中的本地实现是不同的。
- 当需要创建的对象是一系列相互关联或相互依赖的产品族时,如电器工厂中的电视机、洗衣机、空调等。
- 系统中有多个产品族,但每次只使用其中的某一族产品。如有人只喜欢穿某一个品牌的衣服和鞋。
- 系统中提供了产品的类库,且所有产品的接口相同,客户端不依赖产品实例的创建细节和内部结构。
抽象工厂模式的扩展有一定的“开闭原则”倾斜性:
- 当增加一个新的产品族时只需增加一个新的具体工厂,不需要修改原代码,满足开闭原则。
- 当产品族中需要增加一个新种类的产品时,则所有的工厂类都需要进行修改,不满足开闭原则。
另一方面,当系统中只存在一个等级结构的产品时,抽象工厂模式将退化到工厂方法模式。
优缺点
优点:
- 抽象工厂封装了变化,封装了对象创建的具体细节
- 增加新的产品族很方便,无须修改已有系统
- 针对接口进行编程而不是针对具体进行编程
缺点:
- 增加新的产品等级结构需对原系统做较大修改(违背开放封闭)
…
To be continued.