1.前言
设计模式一共有23种,主要分为三大类型:创建型,结构型,行为型。本篇文章着重讲解的是创建型一些相关的设计模式
2.单例模式
Singleton 模式是设计模式中最为简单、最为常见、最容易实现,也是最应该熟悉和掌握的模式。Singleton 模式就是一个类只创建一个唯一的对象,即一次创建多次使用。
此前在cpp专栏里面也专门叙述了单例模式,这里就不过多的重复叙述。想了解的可以阅读这篇文章:
3.工厂模式
在面向对象系统设计中经常可以遇到以下的两类问题:
我们经常会抽象出一些类的公共接口以形成抽象基类或者接口。这样我们可以通过声明一个指向基类的指针来指向实际的子类实现,达到了多态的目的。所以就不得不在要用到子类的地方写new 对象。这样实体类的使用者必须知道实际的子类名称,以及会使程序的扩展性和维护变得越来越困难。
还有一种情况就是在父类中并不知道具体要实例化哪一个具体的子类。只能在父类中写方法调用,具体调用哪一个类的方法交给子类实现。
为了解决上述问题,于是就引出了简单工厂模式,工厂方法模式和抽象工厂模式三种。
3.1 简单工厂模式
定义:简单工厂模式,又叫做静态工厂方法(Static Factory Method)模式,是由一个工厂对象决定创建出哪一种产品类的实例。
那么这里由一个工厂就指的是用一个静态对象,客户端通过调用这个静态对象,并传递参数给这个静态对象来实现不同对象的不同功能。
eg:我们通过一个基类来构建不同模型的图形,然后客户端不直接调用子类对象,而是通过一个静态工厂的方法来调用子类的相关成员函数
// 产品基类:定义统一接口
class Shape {
public:
virtual void draw() = 0;
virtual ~Shape() = default;
};
// 具体产品:圆形
class Circle : public Shape {
public:
void draw() override {
std::cout << "画圆形" << std::endl;
}
};
// 具体产品:矩形
class Rectangle : public Shape {
public:
void draw() override {
std::cout << "画矩形" << std::endl;
}
};
// 无工厂的情况:客户端依赖具体产品
Shape* shape1 = new Circle(); // 客户端必须知道Circle
Shape* shape2 = new Rectangle(); // 客户端必须知道Rectangle
//有工厂的情况下:
// 工厂类:负责所有产品的创建
class ShapeFactory {
public:
// 根据参数决定创建哪种产品
static Shape* createShape(const std::string& type) {
if (type == "circle") {
return new Circle();
} else if (type == "rectangle") {
return new Rectangle();
}
return nullptr; // 无效类型
}
};
// 客户端通过工厂获取产品,不依赖具体实现
Shape* shape1 = ShapeFactory::createShape("circle"); // 无需知道Circle
Shape* shape2 = ShapeFactory::createShape("rectangle"); // 无需知道Rectangle
shape1->draw(); // 画圆形
shape2->draw(); // 画矩形
简单工厂的优缺点:优点:不直接在客户端new一个子类,而是通过工厂类来完成一系列操作,降低了耦合性
缺点:违反了开闭原则,(对扩展开放,对修改关闭),不容易形成高内聚松耦合结构。 每当我们增加一种产品的时候就要去修改工厂方法,这样会破坏其内聚性,给维护带来额外开支。为了克服简单工厂方法模式的缺点,工厂方法模式和抽象工厂模式就被提了出来
3.2 工厂方法模式
定义:一个用于创建对象的接口,让子类决定实例化哪个类
工厂方法模式的核心思想:讨论的仍然是如何构建同一类型产品(都实现同一个接口)的问题,只不过是通过为每一种要生产的产品配备一个工厂,就是说每个工厂只生产一种特定的产品。这样做的好处就是当以后需要增加新的产品时,直接新增加一个对应的工厂就可以了,而不是去修改原有的工厂,符合编程原则的开闭原则。
工厂方法模式为每一种产品生成一个对应的工厂,从而替换掉简单工厂方法模式中那个静态工厂方法。
#include <iostream>
#include <memory>
// 产品基类:电脑
class Computer {
public:
virtual void showInfo() const = 0;
virtual ~Computer() = default;
};
// 具体产品:小米电脑
class XiaomiComputer : public Computer {
public:
void showInfo() const override {
std::cout << "这是一台小米电脑,性价比高!" << std::endl;
}
};
// 具体产品:Mac电脑
class MacComputer : public Computer {
public:
void showInfo() const override {
std::cout << "这是一台Mac电脑,设计精美!" << std::endl;
}
};
// 工厂基类:定义创建电脑工厂的抽象方法
class ComputerFactory {
public:
virtual std::unique_ptr<Computer> createComputer() const = 0;
virtual ~ComputerFactory() = default;
};
// 具体工厂:小米电脑工厂
class XiaomiComputerFactory : public ComputerFactory {
public:
std::unique_ptr<Computer> createComputer() const override {
return std::make_unique<XiaomiComputer>();
}
};
// 具体工厂:Mac电脑工厂
class MacComputerFactory : public ComputerFactory {
public:
std::unique_ptr<Computer> createComputer() const override {
return std::make_unique<MacComputer>();
}
};
// 客户端代码
int main() {
// 创建小米电脑工厂
std::unique_ptr<ComputerFactory> xiaomiFactory = std::make_unique<XiaomiComputerFactory>();
// 通过小米工厂创建电脑
std::unique_ptr<Computer> xiaomiComputer = xiaomiFactory->createComputer();
xiaomiComputer->showInfo(); // 输出:这是一台小米电脑,性价比高!
// 创建Mac电脑工厂
std::unique_ptr<ComputerFactory> macFactory = std::make_unique<MacComputerFactory>();
// 通过Mac工厂创建电脑
std::unique_ptr<Computer> macComputer = macFactory->createComputer();
macComputer->showInfo(); // 输出:这是一台Mac电脑,设计精美!
return 0;
}
优点:不再违反开闭原则
缺点:如果要拓展,那么类会越写越多。
3.3 抽象工厂模式
定义:抽象工厂为创建一组相关或者是相互依赖的对象提供一个接口,而不需要指定他们的具体类。
概念永远都是最难理解的,那么我们用一个实际的例子来理解这句话。
还是小米电脑和Mac电脑,那么此时我又要生产小米手机和mac手机,那么按照工厂方法模式则需要再拓展三个类,一个手机抽象类,2个具体手机类。
那么这就是两个家族之间的事了,小米家族负责生成小米手机和小米电脑,而mac家族负责生产mac电脑和mac手机。
那么这个时候抽象工厂就可以派上用场了。
还是上述例子:转换成图形如下所示:
用工厂方法模式,新增代码如下:
// 新增:产品基类:手机
class Phone {
public:
virtual void showInfo() const = 0;
virtual ~Phone() = default;
};
// 新增:具体产品:小米手机
class XiaomiPhone : public Phone {
public:
void showInfo() const override {
std::cout << "这是一台小米手机,性能强劲!" << std::endl;
}
};
// 新增:具体产品:Mac手机(iPhone)
class MacPhone : public Phone {
public:
void showInfo() const override {
std::cout << "这是一台Mac手机,生态流畅!" << std::endl;
}
};
使用抽象工厂模式代码如下:
#include <iostream>
#include <memory>
// ===== 产品接口 =====
// 电脑接口
class Computer {
public:
virtual void showInfo() const = 0;
virtual ~Computer() = default;
};
// 手机接口
class Phone {
public:
virtual void showInfo() const = 0;
virtual ~Phone() = default;
};
// ===== 具体产品 =====
// 小米电脑
class XiaomiComputer : public Computer {
public:
void showInfo() const override {
std::cout << "小米电脑:高性能处理器,轻薄设计" << std::endl;
}
};
// Mac电脑
class MacComputer : public Computer {
public:
void showInfo() const override {
std::cout << "Mac电脑:精致外观,流畅系统" << std::endl;
}
};
// 小米手机
class XiaomiPhone : public Phone {
public:
void showInfo() const override {
std::cout << "小米手机:高性价比,5G支持" << std::endl;
}
};
// Mac手机(iPhone)
class MacPhone : public Phone {
public:
void showInfo() const override {
std::cout << "Mac手机:iOS生态,A系列芯片" << std::endl;
}
};
// ===== 抽象工厂接口 =====
class DeviceFactory {
public:
virtual std::unique_ptr<Computer> createComputer() const = 0;
virtual std::unique_ptr<Phone> createPhone() const = 0;
virtual ~DeviceFactory() = default;
};
// ===== 具体工厂 =====
// 小米工厂:生产小米品牌的设备
class XiaomiFactory : public DeviceFactory {
public:
std::unique_ptr<Computer> createComputer() const override {
return std::make_unique<XiaomiComputer>();
}
std::unique_ptr<Phone> createPhone() const override {
return std::make_unique<XiaomiPhone>();
}
};
// Mac工厂:生产Mac品牌的设备
class MacFactory : public DeviceFactory {
public:
std::unique_ptr<Computer> createComputer() const override {
return std::make_unique<MacComputer>();
}
std::unique_ptr<Phone> createPhone() const override {
return std::make_unique<MacPhone>();
}
};
// ===== 客户端代码 =====
int main() {
// 创建小米工厂,生产小米设备
std::unique_ptr<DeviceFactory> xiaomiFactory = std::make_unique<XiaomiFactory>();
auto xiaomiPC = xiaomiFactory->createComputer();
auto xiaomiMobile = xiaomiFactory->createPhone();
xiaomiPC->showInfo(); // 输出:小米电脑:高性能处理器,轻薄设计
xiaomiMobile->showInfo(); // 输出:小米手机:高性价比,5G支持
// 创建Mac工厂,生产Mac设备
std::unique_ptr<DeviceFactory> macFactory = std::make_unique<MacFactory>();
auto macPC = macFactory->createComputer();
auto macMobile = macFactory->createPhone();
macPC->showInfo(); // 输出:Mac电脑:精致外观,流畅系统
macMobile->showInfo(); // 输出:Mac手机:iOS生态,A系列芯片
return 0;
}
4.原型模式
原型模式属于创建型模式,所以它是描述如何创建对象的模式。顾名思义,先搞一个原型对象出来,然后在这个原型对象的基础上修修补补再弄出一个新对象来。
定义:使用原型实例指定待创建对象的类型,并且通过复制这个原型来创建新的对象。
#include <iostream>
#include <memory>
// 原型接口
class Shape {
public:
virtual ~Shape() = default;
virtual std::unique_ptr<Shape> clone() const = 0; // 纯虚函数,定义克隆接口
virtual void printInfo() const = 0; // 纯虚函数,用于打印信息
};
// 具体原型类:Circle
class Circle : public Shape {
private:
int x;
int y;
double radius;
public:
// 构造函数
Circle(int x, int y, double radius) : x(x), y(y), radius(radius) {}
// 拷贝构造函数
Circle(const Circle& other) : x(other.x), y(other.y), radius(other.radius) {
std::cout << "Circle拷贝构造函数被调用" << std::endl;
}
// 克隆方法实现
std::unique_ptr<Shape> clone() const override {
std::cout << "Circle克隆方法被调用" << std::endl;
return std::make_unique<Circle>(*this); // 调用拷贝构造函数
}
// 打印信息
void printInfo() const override {
std::cout << "Circle: x=" << x << ", y=" << y << ", radius=" << radius << std::endl;
}
};
// 具体原型类:Rectangle
class Rectangle : public Shape {
private:
int x;
int y;
double width;
double height;
public:
// 构造函数
Rectangle(int x, int y, double width, double height)
: x(x), y(y), width(width), height(height) {}
// 拷贝构造函数
Rectangle(const Rectangle& other)
: x(other.x), y(other.y), width(other.width), height(other.height) {
std::cout << "Rectangle拷贝构造函数被调用" << std::endl;
}
// 克隆方法实现
std::unique_ptr<Shape> clone() const override {
std::cout << "Rectangle克隆方法被调用" << std::endl;
return std::make_unique<Rectangle>(*this); // 调用拷贝构造函数
}
// 打印信息
void printInfo() const override {
std::cout << "Rectangle: x=" << x << ", y=" << y
<< ", width=" << width << ", height=" << height << std::endl;
}
};
int main() {
// 使用原型模式创建对象
std::unique_ptr<Shape> circle1 = std::make_unique<Circle>(10, 20, 5.0);
std::unique_ptr<Shape> circle2 = circle1->clone(); // 通过克隆方法复制对象
std::unique_ptr<Shape> rect1 = std::make_unique<Rectangle>(0, 0, 100.0, 200.0);
std::unique_ptr<Shape> rect2 = rect1->clone(); // 通过克隆方法复制对象
// 使用拷贝构造函数直接创建对象
Circle circle3(*circle1); // 直接调用Circle的拷贝构造函数
Rectangle rect3(*rect1); // 直接调用Rectangle的拷贝构造函数
// 打印所有对象信息
std::cout << "\n所有对象的信息:" << std::endl;
circle1->printInfo();
circle2->printInfo();
circle3.printInfo();
rect1->printInfo();
rect2->printInfo();
rect3.printInfo();
return 0;
}
5.建造者模式
定义:将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。
简单来说就是将一个复杂的对象通过分成好几步来进行构建,然后统一把这好几步交给一个指挥者来来完成最后的组装。
比如组装一台电脑,那么就需要有cpu、主板、键盘、显示器等组成。
建造者模式
#include <memory>
//eg:通过苹果电脑的构造理解建造者模式
class computer {
//电脑要包含os,board,display
public:
virtual void setboard(const string& board) = 0;
virtual void setdisplay(const string& display) = 0;
virtual void setos() = 0;
void print() {
string param = "computer paramaters:\n";
param += "\tboard: " + _board + "\n";
param += "\tdisplay: " + _display + "\n";
param += "\tos: " + _os + "\n";
cout << param << endl;
}
protected:
string _os;
string _board;
string _display;
};
//具体mac电脑
class maccomputer :public computer {
public:
void setboard(const string& board) override{
_board = board;
}
void setdisplay(const string& display) override {
_display = display;
}
void setos() override {
_os = "mac x64";
}
};
//抽象构建类
class builder {
public:
virtual void buildboard(const string& board) = 0;
virtual void builddisplay(const string& display) = 0;
virtual void buildos() = 0;
virtual std::shared_ptr<computer> build() = 0;
};
//具体产品的构建
class macbookbuild :public builder{
public:
macbookbuild() :_computer(new maccomputer()) {}
void buildboard(const string& board) override {
_computer->setboard(board);
}
void builddisplay(const string& display) override {
_computer->setdisplay(display);
}
void buildos() override {
_computer->setos();
}
std::shared_ptr<computer> build() {
return _computer;
}
private:
std::shared_ptr<computer> _computer;
};
class director {
//指挥者负责组装一整套电脑
public:
director(builder* builder):_build(builder){}
void construct(const string& board, const string& display) {
_build->buildboard(board);
_build->builddisplay(display);
_build->buildos();
}
private:
std::shared_ptr<builder> _build;
};
int main() {
builder* builder = new macbookbuild();
unique_ptr<director> director(new director(builder));
director->construct("罗技","三星");
shared_ptr<computer> computer = builder->build();
computer->print();
return 0;
}
建造者模式的使用场景:当一个类的构造函数的参数超过四个,且这四个参数还是可选可不选的,那么就可以考虑使用建造者模式了。
6.总结
我们一定要牢记设计模式是前人总结的一套可以有效解决问题的经验,不要一写代码就在考虑该使用什么设计模式,这是极其不可取的。正确的做法应该是在实现业务需求的同时,尽量遵守面向对象设计的六大设计原则即可。后期随着业务的扩展,你的代码会不断的演化和重构,在此过程中设计模式绝逼会大放异彩的。
说实话博主写完这篇文章感觉原型模式模式是不是有点多余啊,想复制原来创建的新的对象,使用拷贝构造函数不就可以了嘛,为什么还要弄一个新的设计模式出来呢。目前博主的理解就到这啦,有兴趣的可以后台TT博主一起交流呀!