基于c++的设计模式之工厂模式及控制反转(IOC),依赖注入(DI)详解及本人学习经验
基于c++设计模式之工厂模式及控制反转(IOC),依赖注入(DI)详解及本人学习经验
1.控制反转
2.依赖注入
3.工厂模式
3.1 简单工厂模式
3.2 工厂方法模式
3.3 抽象工厂模式
3.4 简单工厂vs工厂方法vs抽象工厂
本人学习经验
对于设计模式,我想说的是,在真正写到涉及多个功能的代码的时候,是非常有必要的,特别是面向对象编程。我刚接触到设计模式这个词语,是在刚学习c++的时候,之前搞java的时候,学spring框架的时候,了解过依赖注入和控制反转,但是也仅仅知道这是什么,现在搞c++自己用到的时候发现不是很熟悉,就是遇到一种类需要使用到另一个类的时候就需要用到依赖注入。所以系统学一下设计模式,而且一定要避免纸上谈兵,不仅要学会理论,一定一定要尝试去写代码,刚开始不是很熟悉的时候,可以照着ai去写,慢慢就会形成自己的风格。
简介
C++中的工厂模式、控制反转(IoC)和依赖注入(DI)。这三者都是面向对象设计中用于解耦的高级技术,它们之间层层递进,关系密切。
1.控制反转
简介
控制反转是一种软件设计原则,它的核心思想是将程序的控制权从应用程序代码本身转移给一个外部框架或容器。
在传统的程序设计中,我们的代码会主动、显式地创建依赖对象并调用其方法(即“控制权”在我们自己手中)。
而通过 IoC,这个创建和组装的流程被反转了——由一个外部的“容器”来负责创建依赖对象并“注入”到我们的代码中。我们的代码从“主动创建”变成了“被动接收”。通俗点来讲,就是我们不需要进行依赖的创建,便于实现“解耦”。
我们进行写代码的时候,控制反转最常见的实现方式就是依赖注入。
核心思想
反转程序的控制权,将依赖的创建和管理从应用程序转移到外部。
IOC容器
当我们进行一个大型项目的时候,依赖关系十分复杂,需要一个IOC容器,它是一个高级的,专门用于自动完成依赖注入的框架
它的职责是:
1.创建对象
2.了解对象之间的依赖关系(通过配置或者代码)
3.管理对象的生命周期
4.运行时自动将依赖项注入到需要它的对象中
C++ 中著名的 IoC 容器有:
Boost.DI:一个非常强大且符合现代 C++ 风格的头部库。
2.依赖注入
依赖注入是控制反转的一种具体实现技术。它的核心概念是:一个类不应该自己创建它所依赖的对象,而应该由外部来提供。
依赖注入是实现IOC最主要的技术,主要有三种方式
1.构造函数注入(最常用)
2.Setter方法注入
3.接口注入(较少用)
代码实例
- 头文件
#include <iostream>
#include <memory>
#include <string>
class music
{
public:
virtual ~music() = default;
virtual void show(const std::string &name) = 0;
private:
std::string name;
};
// 两个不同的类来演示依赖注入
class musicA : public music
{
void show(const std::string &name);
};
class musicB : public music
{
void show(const std::string &name);
};
class listen
{
public:
// 静态工厂创建对象,并在初始化的时候将依赖注入
static std::unique_ptr<listen> create(std::unique_ptr<music> music);
~listen();
void playMusic(const std::string &name);
private:
// 强制使用工厂模式
listen(std::unique_ptr<music> music);
// 将music依赖注入
std::unique_ptr<music> music_;
};
- 源文件
#include "DI.h"
void musicA::show(const std::string &name)
{
std::cout << "第A首音乐:" << name << std::endl;
}
void musicB::show(const std::string &name)
{
std::cout << "第B首音乐:" << name << std::endl;
}
std::unique_ptr<listen> listen::create(std::unique_ptr<music> music)
{
return std::unique_ptr<listen>(new listen(std::move(music)));
}
listen::listen(std::unique_ptr<music> music) : music_(std::move(music))
{
}
void listen::playMusic(const std::string &name)
{
music_->show(name);
}
listen::~listen() = default;
- 测试案例
#include "DI.h"
int main()
{
// 通过注入不同的依赖来实现不同的函数
auto listen1 = listen::create(std::make_unique<musicA>());
listen1->playMusic("把回忆拼好给你");
auto listen2 = listen::create(std::make_unique<musicB>());
listen2->playMusic("星辰大海");
}
3.工厂模式
核心思想
工厂模式是一种创建型设计模式,它核心思想是将对象的创建与使用分离。客户端不需要知道对象的具体类型,也不需要知道创建的细节,只需通过一个共同的接口来获取对象。
它主要解决了:对象创建的逻辑与对象使用逻辑的紧耦合问题。
好处
1.降低耦合度
传统的new关键字创建对象时候,会将客户端代码与具体实现紧密绑定,一旦需要更换类,或者类的构造函数发生变化,所有创建该类的地方都需要修改,使用工厂模式可以将创建过程封装起来,客户端只依赖于一个抽象的接口,而不是具体的实现类。这样就算类的构造函数发生变化的时候,只需要修改工厂内的逻辑,客户端代码不需要再动。
2.遵循开闭原则
对软件功能扩展开放,对修改关闭。通俗点来讲,就是使用工厂模式,要新增产品,只需要增加一个新的类和对应的工厂类(或者扩展原有工厂的创建方法),而无需修改已有的工厂和客户端代码,使系统扩展变得更加容易
3.简化客户端代码
某些对象的创建过程可能十分复杂(例如需要复杂的初始化,配置或者依赖其他对象),例如使用aws sdk S3 for cpp时,需要初始化s3客户端,如果将代码放到客户端各处,会使客户端代码变得十分难以维护。使用工厂模式,将类的初始化“黑盒化”,客户端只需要调用一个简单的工厂方法(如Create()),就可以获得一个已经初始化好的,可以直接使用的对象,使客户端的代码更加清晰和简洁
4.提供统一的创建接口
工厂模式为创建一系列相关或依赖的对象提供了一个统一的入口。无论是创建哪种具体类型的对象,客户端都使用相同的方法,这提高了代码的一致性和可读性。
5.更容易管理对象的生命周期
在 C++ 中,资源管理(特别是内存)至关重要。工厂可以统一管理对象的创建和销毁。例如,工厂可以返回 std::unique_ptr 或 std::shared_ptr,明确所有权语义,防止内存泄漏。客户端无需担心如何释放对象。
3.1 简单工厂模式
严格来说,这更像一种编程习惯,而不是一个设计模式。它使用一个单独的类(通常是静态方法)来负责创建对象。
简单工厂模式还有一种实现模式是静态工厂,区别就在于简单工厂需要创建对象实例,通过对象实例来调用方法,静态工厂直接通过类名来调用工厂函数,创建对象。
测试案例(静态工厂)
- 头文件
#include <iostream>
#include <memory>
// 音乐接口
class MusicProduct
{
public:
virtual void Operator(const std::string &music) = 0;
virtual ~MusicProduct() = default;
};
// 具体音乐A
class MusicA : public MusicProduct
{
public:
void Operator(const std::string &music) override;
};
// 具体音乐B
class MusicB : public MusicProduct
{
void Operator(const std::string &music) override;
};
// 工厂类
class SimpleFactory
{
public:
// 静态方法
static std::unique_ptr<MusicProduct> CreateMusic(const std::string &type)
{
if (type == "A")
{
return std::make_unique<MusicA>();
}
else if (type == "B")
{
return std::make_unique<MusicB>();
}
return nullptr;
}
};
- 源文件
#include "factory.h"
void MusicA::Operator(const std::string &music)
{
std::cout << "你喜欢的音乐是:" << music << std::endl;
}
void MusicB::Operator(const std::string &music)
{
std::cout << "你喜欢的音乐是:" << music << std::endl;
}
- 测试案例
#include "factory.h"
int main()
{
auto musicA = SimpleFactory::CreateMusic("A");
musicA->Operator("把回忆拼好给你");
auto musicB = SimpleFactory::CreateMusic("B");
musicB->Operator("无限");
}
3.2 工厂方法模式
定义一个用于创建对象的接口,但让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
优点: 完全符合开闭原则。要增加新产品,只需添加新的具体工厂和具体产品类,无需修改现有代码。
缺点: 类的数量会增多,系统更复杂。
测试案例
- 头文件
#include <iostream>
#include <memory>
// 音乐接口
class MusicProduct
{
public:
virtual void Operator(const std::string &music) = 0;
virtual ~MusicProduct() = default;
};
// 具体音乐A
class MusicA : public MusicProduct
{
public:
void Operator(const std::string &music) override;
};
// 具体音乐B
class MusicB : public MusicProduct
{
void Operator(const std::string &music) override;
};
// 抽象工厂
class MusicFactory
{
public:
virtual std::unique_ptr<MusicProduct> Create() = 0;
virtual ~MusicFactory() = default;
};
// 生产A的工厂
class FactoryA : public MusicFactory
{
public:
std::unique_ptr<MusicProduct> Create() override;
};
// 生产B的工厂
class FactoryB : public MusicFactory
{
public:
std::unique_ptr<MusicProduct> Create() override;
};
- 源文件
#include "factory.h"
void MusicA::Operator(const std::string &name)
{
std::cout << "这里是musicA" << std::endl;
std::cout << "你喜欢的音乐是:" << name << std::endl;
}
void MusicB::Operator(const std::string &name)
{
std::cout << "这里是musicB" << std::endl;
std::cout << "你喜欢的音乐是:" << name << std::endl;
}
std::unique_ptr<MusicProduct> FactoryA::Create()
{
return std::unique_ptr<MusicProduct>(new MusicA());
}
std::unique_ptr<MusicProduct> FactoryB::Create()
{
return std::unique_ptr<MusicProduct>(new MusicB());
}
- 测试案例
#include "factory.h"
int main()
{
std::unique_ptr<MusicFactory> factoryA = std::make_unique<FactoryA>();
auto musicA = factoryA->Create();
musicA->Operator("把回忆拼好给你");
std::unique_ptr<MusicFactory> factoryB = std::make_unique<FactoryB>();
auto musicB = factoryB->Create();
musicB->Operator("Letting go");
}
3.3 抽象工厂模式
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。它处理的是“产品族”的概念。
测试案例
- 头文件
#include <iostream>
#include <memory>
#include <string>
class Shape
{
public:
virtual ~Shape() = default;
virtual void draw() const = 0;
};
class Circle : public Shape
{
void draw() const override;
};
class Square : public Shape
{
void draw() const override;
};
class Color
{
public:
virtual ~Color() = default;
virtual void fill() const = 0;
};
class Red : public Color
{
public:
void fill() const override;
};
class Blue : public Color
{
public:
void fill() const override;
};
// 抽象工厂
class AbstractFactory
{
public:
virtual ~AbstractFactory() = default;
virtual std::unique_ptr<Shape> createShape() const = 0;
virtual std::unique_ptr<Color> createColor() const = 0;
virtual std::string getTheme() const = 0;
};
// 具体工厂:红色圆形主题
class RedCircleFactory : public AbstractFactory
{
std::unique_ptr<Shape> createShape() const override;
std::unique_ptr<Color> createColor() const override;
std::string getTheme() const;
};
// 具体工厂:蓝色方形主题
class BlueSquareFactory : public AbstractFactory
{
public:
std::unique_ptr<Shape> createShape() const override;
std::unique_ptr<Color> createColor() const override;
std::string getTheme() const override;
};
// 客户端
class Application
{
private:
std::unique_ptr<AbstractFactory> factory;
public:
Application(std::unique_ptr<AbstractFactory> factory);
void renderUI() const;
};
- 源文件
#include "factory.h"
void Red::fill() const
{
std::cout << "填充红色" << std::endl;
}
void Blue::fill() const
{
std::cout << "填充蓝色" << std::endl;
}
void Circle::draw() const
{
std::cout << "绘制圆形" << std::endl;
}
void Square::draw() const
{
std::cout << "绘制方形" << std::endl;
}
std::unique_ptr<Shape> RedCircleFactory::createShape() const
{
return std::make_unique<Circle>();
}
std::unique_ptr<Color> RedCircleFactory::createColor() const
{
return std::make_unique<Red>();
}
std::string RedCircleFactory::getTheme() const
{
return "红色圆形主题";
}
std::unique_ptr<Shape> BlueSquareFactory::createShape() const
{
return std::make_unique<Square>();
}
std::unique_ptr<Color> BlueSquareFactory::createColor() const
{
return std::make_unique<Blue>();
}
std::string BlueSquareFactory::getTheme() const
{
return "蓝色方形主题";
}
Application::Application(std::unique_ptr<AbstractFactory> factory) : factory(std::move(factory))
{
}
void Application::renderUI() const
{
std::cout << "主题:" << factory->getTheme() << std::endl;
auto shape = factory->createShape();
auto color = factory->createColor();
shape->draw();
color->fill();
}
- 测试案例
#include "factory.h"
int main()
{
auto app1 = std::make_unique<Application>(std::make_unique<RedCircleFactory>());
app1->renderUI();
auto app2 = std::make_unique<Application>(std::make_unique<BlueSquareFactory>());
app2->renderUI();
}
3.4 简单工厂vs工厂方法vs抽象工厂
| 特点 | 简单工厂 | 工厂方法 | 抽象工厂 |
|---|---|---|---|
| 目的 | 将对象的创建逻辑集中在一个类中 | 将对象的创建延迟到子类 | 创建相关产品的家族 |
| 复杂度 | 简单 | 中等 | 较高 |
| 扩展性 | 需要修改工厂类来添加新产品 | 易于扩展新产品,只需添加新的创建者类 | 扩展产品家族容易,但添加新产品困难 |
| 适用场景 | 产品类型较少,变化不大的情况 | 需要创建单一产品,但具体类型可能变化 | 需要创建一系列相关的产品 |
选择指南
1.简单工厂:当产品类型固定且不会频繁变化时
2.工厂方法:当需要创建单一产品,但具体实现可能变化时
3.抽象工厂:当需要创建相关产品的完整家族时
关系链
工厂模式 → 实现了初步的IOC → 依赖注入是更彻底的IOC → IOC容器自动化了DI
更多推荐
所有评论(0)