设计模式(一)

设计模式原则

  • 单一职责原则
    • 含义:每个类应该只有一个职责,只有一个原因可以引起类的变化
    • 目的:通过将不同职责分离到不同的类中,从而提高代码的可读性和可维护性
  • 开闭原则
    • 含义:类、函数等应该对拓展开放、对修改关闭
    • 目的:通过继承和多态机制,允许系统在不修改现有代码的情况下进行拓展,从而提高系统的稳定性和灵活性
  • 里氏替换原则
    • 含义:子类对象必须能够替换其基类对象,并且不会导致程序的逻辑错误
    • 目的:确保继承关系的正确性,使得子类可以完全替代基类
  • 依赖倒置原则
    • 含义:高层模块不应该依赖其底层模块,两者应该依赖其抽象。抽象不依赖其细节,但是细节应该依赖抽象
    • 目的:通过依赖接口或者抽象类类解耦高层和底层的模块,从而提高系统的可维护性和可拓展性
  • 接口隔离原则
    • 含义:客户端不应该依赖它不需要的接口,应该将大的接口拆分的成小的接口
    • 目的:通过使用多个特定客户端接口,而不是一个通用接口,提高系统的灵活性和可维护性
  • 合成/聚合复用原则
    • 含义:多使用对象的合成而不是继承来达到复用的目的
    • 目的:通过组合或者聚合其他对象类实现新的功能
  • 迪米特原则
    • 含义:一个对象应该对其他对象有最少的了解,减少与其他对象的交互
    • 目的:通过减少对象直接的耦合,提高系统的模块化和可维护性

构建型模式

单例模式

单例模式的主要目的是确保一个类在整个程序中只有一个实例,并提供全局的访问点。

单例模式的特点

  • 唯一实例:单例模式确保一个类只有一个实例存在,避免了不必要的实例化,节省了系统资源。

  • 全局访问点:单例模式提供了一个全局访问点,可以通过这个访问点获取唯一的实例。

  • 控制实例创建:通过单例模式,程序可以更好地控制实例的创建和生命周期。
    单例模式的应用场景

  • 需要全局访问的唯一实例:如日志系统、配置管理、线程池、数据库连接等。

  • 节省资源:某些资源消耗较大的对象,如文件系统、数据库连接等,可以通过单例模式避免重复创建,节省资源。

  • 控制实例数量:需要严格控制某个类的实例数量,避免过多的实例对系统性能造成影响。
    单例模式的优缺点

  • 优点

    • 节省资源:通过控制实例的创建数量,节省系统资源。
    • 全局访问:提供一个全局访问点,方便获取唯一实例。
    • 实例控制:严格控制实例的创建和生命周期,确保系统的一致性。
  • 缺点

    • 难以扩展:单例模式限制了类的扩展,增加新功能时可能需要修改现有代码。
    • 隐藏依赖:全局访问点可能导致依赖关系隐藏,使代码难以理解和维护。
    • 并发问题:在多线程环境下,需要特别注意线程安全问题,否则可能导致多个实例被创建。

Effective C++提供方法(仅支持C++11以后)

cpp 复制代码
template<typename T>
class Singleton {
private:
	Singleton(){}
	~Singleton(){}
public:
	Singleton(const Singleton&) = delete;
	Singleton& operator = (const Singleton) = delete;
	static T& getInstance() {
		static Singleton _eton;
		return _eton;
	}
};

饿汉式

类加载的时候就创建实例,线程安全但是可能会造成资源浪费。(害怕吃不上,提前准备好)

相似方法总结

  • 构造函数私有化,防止外部进行实例
  • 禁止拷贝构造函数和赋值运算运算,防止被复制
  • 互斥锁的设计
  • 静态实例,类外初始化
  • 公共方法中提供全局访问点
cpp 复制代码
class Singleton
{
private:
	Singleton() {};
	Singleton(const Singleton&) = delete;
	Singleton& operator=(const Singleton&) = delete;
	static Singleton* instance;

public:
	static Singleton* getInstance()
	{
		return instance;
	}
};
//Singleton* instance = nullptr;
Singleton* Singleton::instance = nullptr;

懒汉式

第一次调用的时候,再创建实例,避免资源的浪费,但是可能会造成线程不安全。

cpp 复制代码
class Singleton_lazy
{
private:
	Singleton_lazy(){}
	Singleton_lazy(const Singleton_lazy&) = delete;
	Singleton_lazy& operator = (const Singleton&) = delete;
	static Singleton_lazy* instance;
public:
	static Singleton_lazy* getInstance()
	{
		if (instance == nullptr)
		{
			instance == new Singleton_lazy();
		}
		return instance;
	}
};
Singleton_lazy* Singleton_lazy::instance = nullptr;

线程安全的懒汉式

使用互斥锁去保证线程安全。

cpp 复制代码
#include<mutex>
class Singleton_lazy_mutex
{
private:
	Singleton_lazy_mutex(){}
	Singleton_lazy_mutex(const Singleton_lazy_mutex&) = delete;
	Singleton_lazy_mutex& operator = (const Singleton_lazy_mutex&) = delete;
	static Singleton_lazy_mutex* instance;
	static std::mutex mtx;
public:
	Singleton_lazy_mutex* getInstance()
	{
		std::lock_guard<std::mutex> lock(mtx);
		if (instance == nullptr)
		{
			instance = new Singleton_lazy_mutex();
		}
		return instance;
	}
};
Singleton_lazy_mutex* Singleton_lazy_mutex::instance = nullptr;
std::mutex Singleton_lazy_mutex::mtx;

C++11标准实现线程安全

C++11引入线程安全的静态局部变量初始化

cpp 复制代码
class singleton_11
{
private:
	singleton_11(){}
	singleton_11(const singleton_11&) = delete;
	singleton_11& operator=(const singleton_11&) = delete;

public:
	static singleton_11& getInstance()
	{
		static singleton_11 instance;
		return instance;
	}
};

双重检查锁定

第一次检查的时候不加锁,只有实例为空的时候才可以加锁,减少了加锁的开销。

cpp 复制代码
class singleton_double
{
private:
	singleton_double(){}
	singleton_double(const singleton_double&) = delete;
	singleton_double& operator=(const singleton_double&) = delete;

	static singleton_double* instance;
	static std::mutex mtx;
public:
	singleton_double* getInstance()
	{
		if (instance == nullptr)
		{
			std::lock_guard<std::mutex> lock(mtx);
			if (instance == nullptr)
			{
				instance = new singleton_double();
			}
		}
		return instance;
	}
};
singleton_double* singleton_double::instance = nullptr;
std::mutex singleton_double::mtx;

工厂模式

简单工厂模式

设计一个汽车工厂,当需要某种类型的汽车时,直接告诉工厂自己的需求即可,不需要关注汽车的各种零部件如何组装,也不需要关注不同的类型的汽车的设计图纸。作为用户只需要问工厂伸手要就行。

简单工厂模式即是让工厂承担构建所有的对象的全部职责。用户想要什么类型的产品,只需让工厂生产出来即可。

工厂模式的弊端

  • 超级类的产生:当生产的要求过多的时候,工厂需要承担的职责就会变多,从而变成超级类。例如市场对汽车的类型需要多样,从而导致工厂需要不断的更改其方法。工厂类此时拥有多个引起修改的原因,违背了单一职责原则。
  • 生产新产品的时候,必须在工厂类中添加新的分支。违反设计模式中的开闭原则,因为的开闭原则下要求增加新功能的时候,只需要增加新的类,而不是修改现在已经拥有的类。
  • 增加程序的复杂性,提高调试和维护成本:如果是一个简单的系统中,直接创建类对象可能比通过工厂类来创建更加的简单明了。
cpp 复制代码
#include <iostream>
#include <string>

// 抽象产品类
class Product {
public:
    virtual void use() = 0;
};

// 具体产品类A
class ConcreteProductA : public Product {
public:
    void use() override {
        std::cout << "Using Product A" << std::endl;
    }
};

// 具体产品类B
class ConcreteProductB : public Product {
public:
    void use() override {
        std::cout << "Using Product B" << std::endl;
    }
};

// 工厂类
class SimpleFactory {
public:
    static Product* createProduct(const std::string& type) {
        if (type == "A") {
            return new ConcreteProductA();
        } else if (type == "B") {
            return new ConcreteProductB();
        }
        return nullptr;
    }
};

// 客户端代码
int main() {
    Product* productA = SimpleFactory::createProduct("A");
    if (productA) {
        productA->use();
        delete productA;
    }

    Product* productB = SimpleFactory::createProduct("B");
    if (productB) {
        productB->use();
        delete productB;
    }

    return 0;
}

工厂方法模式

工厂方法模式的产生就是为了解决简单工厂模式弊端。工厂方法模式规定每一个产品都有一个专属的工厂,例如越野车有越野车的工厂,轿车有轿车的工厂等。

工厂方法模式解决简单工厂模式的弊端。首先,生产的汽车种类变多的时候,工厂类不会变成超级类,当需要修改某个汽车厂只需要对某个汽车厂进行修改,不需要像之前对总工厂进行修改。从而符合单一职责的设计原则。其次,当生产新的汽车类型的时候,不需要修改现有的工厂,只需要添加新的工厂即可。保持了面向对象的可拓展性,符合开闭原则。

优缺点

  • 优点

    • 遵循开闭原则:可以在不修改现有代码的情况下增加新的产品类。
    • 代码复用:工厂方法将具体产品的实例化逻辑封装在子类中,避免了重复代码。
    • 解耦:将对象的创建与使用分离,减少了类之间的耦合。
    • 单一职责原则:每个具体工厂类只负责一种具体产品的创建,职责单一,代码更清晰。
  • 缺点

    • 增加复杂性:每增加一个产品类,就需要增加一个相应的具体工厂类,增加了系统的复杂性。
    • 类爆炸:可能会引入大量的子类,导致类数量急剧增加,维护起来比较繁琐。
cpp 复制代码
class Product
{
public:
	virtual void use() = 0;
	virtual ~Product(){}
};

class car1 :public Product
{
public:
	void use()override
	{
		std::cout << "越野车生产方法" << std::endl;
	}
};

class car2 :public Product
{
public:
	void use() override
	{
		std::cout << "轿车生产方法" << std::endl;
	}
};

class Factory
{
public:
	virtual Product* createProduct() = 0;
	virtual ~Factory(){}
};

class createCar1 :public Factory
{
public:
	Product* createProduct() override
	{
		return new car1;
	}
};

class createCar2 :public Factory
{
public:
	Product* createProduct()override
	{
		return new car2;
	}
};

int main()
{
	//建造越野车工厂,生产越野车
	Factory* factory1 = new createCar1();
	Product* product1 = factory1->createProduct();
	product1->use();
	delete product1;
	delete factory1;

	//小轿车工厂,生产小轿车
	Factory* factory2 = new createCar2();
	Product* product2 = factory2->createProduct();
	product2->use();
	delete product2;
	delete factory2;

	return 0;
}

抽象工厂模式

抽象工厂可以理解为一个超级工厂,通过该工厂可以创建生产其他各种类型物品的子工厂。这些生成的子工厂可以按照工厂的逻辑提供具体函数实现或者对象。

cpp 复制代码
class Fruit {
public:
	Fruit(){}
	virtual void show() = 0;
};
class Apple :public Fruit {
public:
	Apple(){}
	void show()override {
		std::cout << "生产苹果" << std::endl;
	}
};
class Banana :public Fruit {
public:
	Banana(){}
	void show()override {
		std::cout << "生产香蕉" << std::endl;
	}
};

class Animal {
public:
	virtual void voice() = 0;
};
class Dog :public Animal {
public:
	void voice() override {
		std::cout << "呕吼~~~" << std::endl;
	}
};
class Lamp :public Animal {
public:
	void voice() override {
		std::cout << "沸羊羊" << std::endl;
	}
};

//将水果工厂和动物工厂进行组合("超级工厂")
class Factory {
public:
	virtual std::shared_ptr<Fruit> getFruit(const std::string& name) = 0;
	virtual std::shared_ptr<Animal>getAnimal(const std::string& name) = 0;
};

//水果分厂
class FruitFactory :public Factory {
public:
	std::shared_ptr<Animal> getAnimal(const std::string& name) override {
		return std::shared_ptr<Animal>();
	}
	std::shared_ptr<Fruit> getFruit(const std::string& name) override {
		if (name == "苹果") {
			return std::make_shared<Apple>();
		}
		else if (name == "香蕉") {
			return std::make_shared<Banana>();
		}
		return std::shared_ptr<Fruit>();
	}
};

//动物分厂
class AnimalFactory :public Factory {
public:
	std::shared_ptr<Fruit> getFruit(const std::string& name) override {
		return std::shared_ptr<Fruit>();
	}
	std::shared_ptr<Animal>getAnimal(const std::string& name) override {
		if (name == "羊") {
			return std::make_shared<Lamp>();
		}
		else if (name == "狗") {
			return std::make_shared<Dog>();
		}
		return shared_ptr<Animal>();
	}
};

//工厂生产者
class FactorProducter {
public:
	static std::shared_ptr<Factory>getFacory(const std::string& name) {
		if (name == "动物") {
			return std::make_shared<AnimalFactory>();
		}
		else {
			return std::make_shared<FruitFactory>();
		}
	}
};

int main()
{
	//利用超级工厂(FactorProducter)创建一个生产水果的工厂(FruitFactory)
	std::shared_ptr<Factory> fruit_factory = FactorProducter::getFacory("果子");
	//然后水果工厂根据需求生产出苹果和香蕉
	std::shared_ptr<Fruit> fruit = fruit_factory->getFruit("苹果");
	fruit->show();
	fruit = fruit_factory->getFruit("香蕉");
	fruit->show();

	std::shared_ptr<Factory> animal_factory = FactorProducter::getFacory("动物");
	std::shared_ptr<Animal> animal = animal_factory->getAnimal("狗");
	animal->voice();
	
	return 0;
}

建造者模式

建造者模式,即是用简单的对象一步一步构建成一个复杂对象的模式,将创建复杂对象的类进行分离,从而提供构建复杂对象的方法。

实现核心分析

  • 抽象产品类
  • 具体产品类:从抽象产品类构建的具体产品
  • 抽象Builder类:构建一个产品对象,所需要工具的抽象
  • 具体产品的Builder类:实现抽象接口,构建具体的组件
  • 指挥者Director类:统一组件过程,提供给调用者使用,通过指挥者来构建具体的产品
cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
#include<memory>
#include<string>

using namespace std;

//抽象电脑类
class Computer {
protected:
	string _board;
	string _display;
	string _os;

public:
	using ptr = shared_ptr<Computer>;
	Computer(){}
	//主板、显示器、系统g
	void setBoard(const string& board) { _board = board; }
	void setDisplay(const string& display) { _display = display; }
	virtual void setOs() = 0;
	//转换为字符串
	string toString() {
		string computer = "Computer:{\n";
		computer += "\tboard=" + _board + ",\n";
		computer += "\tdisplay=" + _display + "\n";
		computer += "\tOs=" + _os + ",\n";
		computer += "}\n";
		return computer;
	}
};

//具体产品
class Lenovo :public Computer {
public:
	using ptr = shared_ptr<Lenovo>;
	Lenovo(){}
	void setOs()override {
		_os = "Amd 8090";
	}
};

//抽象建造者模式
class Builder {
public:
	using ptr = shared_ptr<Builder>;
	virtual void buildBoard(const string& board) = 0;
	virtual void buildDisplay(const string& display) = 0;
	virtual void buildOs() = 0;
	virtual Computer::ptr build() = 0;
};

//建造者构建具体产品
class LenoveBuilder :public Builder
{
public:
	using ptr = shared_ptr<LenoveBuilder>;
	LenoveBuilder():_computer(new Lenovo()){}
	void buildBoard(const string& board)override
	{
		_computer->setBoard(board);
	}
	void buildDisplay(const string& display)override
	{
		_computer->setDisplay(display);
	}
	void buildOs() override
	{
		_computer->setOs();
	}
	Computer::ptr build() override
	{
		return _computer;
	}


private:
	Computer::ptr _computer;
};

//指挥者
class Director {
private:
	Builder::ptr _builder;
public:
	Director(Builder* builder) :_builder(builder){}
	void construct(const string& board, const string& display) {
		_builder->buildBoard(board);
		_builder->buildDisplay(display);
		_builder->buildOs();
	}
};

int main()
{
	//老板先找一个联想程序员,然后让该程序员干活(构建一个电脑并打印出来)
	Builder* build = new LenoveBuilder();
	unique_ptr<Director> pd(new Director(build));
	pd->construct("amd", "三星");
	Computer::ptr computer = build->build();
	cout << computer->toString();
	return 0;
}
相关推荐
OTWOL几秒前
两道数组有关的OJ练习题
c语言·开发语言·数据结构·c++·算法
问道飞鱼4 分钟前
【前端知识】强大的js动画组件anime.js
开发语言·前端·javascript·anime.js
拓端研究室4 分钟前
R基于贝叶斯加法回归树BART、MCMC的DLNM分布滞后非线性模型分析母婴PM2.5暴露与出生体重数据及GAM模型对比、关键窗口识别
android·开发语言·kotlin
Code成立5 分钟前
《Java核心技术I》Swing的网格包布局
java·开发语言·swing
Auc2410 分钟前
使用scrapy框架爬取微博热搜榜
开发语言·python
QQ同步助手17 分钟前
C++ 指针进阶:动态内存与复杂应用
开发语言·c++
信徒_19 分钟前
常用设计模式
java·单例模式·设计模式
凯子坚持 c23 分钟前
仓颉编程语言深入教程:基础概念和数据类型
开发语言·华为
小爬虫程序猿25 分钟前
利用Java爬虫速卖通按关键字搜索AliExpress商品
java·开发语言·爬虫
程序猿-瑞瑞27 分钟前
24 go语言(golang) - gorm框架安装及使用案例详解
开发语言·后端·golang·gorm