【C++】设计模式

news2024/11/20 8:37:31

目录

设计模式概述

单例模式

饿汉模式

懒汉模式

工厂模式

简单工厂模式

工厂方法模式

抽象工厂模式

观察者模式


设计模式概述

设计模式:一套反复被人使用、多数人知晓的、经过分类编目的代码设计经验的总结。一种固定的写代码的思维逻辑方式,一种设计类与类之间关系的抽象思维的定式。

设计模式的目的是应付客观情况的变化,提高代码的复用性和维护性。

设计模式的分类

  • 创建型模式(设计对象实例化的方式,5种):单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式
  • 结构型模式(描述如何组合类和对象获得更大的结构,7种):代理模式、装饰者模式、适配器模式、桥接模式、组合模式、外观模式、享元模式
  • 行为型模式(描述类和对象的交互以及分配职责,11种):模板方法模式、命令模式、责任链模式、策略模式、中介者模式、观察者模式、备忘录模式、访问者模式、状态模式、解释器模式、迭代器模式

设计模式的七大设计原则

  • 单一职责原则:一个类应该只有一个引起它变化的原因,变化的方向隐含它的职责
  • 开放封闭原则:对扩展开放,对更改封闭,类模块可扩展但不可修改
  • 依赖倒置原则:高层(稳定)不依赖底层(变化),两者依赖抽象(稳定),抽象(稳定)不依赖细节(变化),细节依赖抽象(稳定)
  • 里氏替换原则:子类必须能够替代他们的基类
  • 接口隔离原则:一个接口只提供一种对外功能,接口应该小而完备,尽量私有化用户用不到的接口
  • 优先组合而非继承原则:类的继承通常是“白箱复用”,类的组合通常是“黑箱复用”,继承在一定程序上破坏封装性,子类和父类的耦合度高
  • 迪米特法则:对象应该对其他对象尽可能少的了解,各个模块相互调用时,提供一个统一的接口来实现

在实际设计模式选择中,并不是所有的设计原则都要满足,而是根据不同的应用场景尽可能多的满足设计原则。

单例模式

单例模式:在程序的生命周期内,一个类只能有一个实例,确保该类的唯一性。

单例模式分为饿汉模式懒汉模式,区别在于创建实例的时间不同。

  • 饿汉模式:系统运行时,在主程序之前就直接初始化创建实例,当需要时直接调用实例。饿汉模式不存在多线程的线程安全问题,但缺陷是若初始化数据过多会导致程序启动慢、多个单例创建并有依赖关系时无法控制。
  • 懒汉模式:系统运行时,实例并不创建,只有当第一次需要使用这个实例的时候,才会初始化创建实例,这种方式需要考虑线程安全。

饿汉模式

#include <iostream>
#include <map>
using namespace std;

class Singleton
{
public:
	static Singleton& get_instance()
	{
		return _st;
	}

	void insert(string name, int salary)
	{
		_info[name] = salary;
	}

	void print()
	{
		for (auto& e : _info)
		{
			cout << e.first << " : " << e.second << endl;
		}
		cout << endl;
	}

private:
	Singleton()
	{}

	Singleton(const Singleton& st) = delete;
	Singleton& operator=(const Singleton& st) = delete;

private:
	map<string, int> _info;
	static Singleton _st;
};

Singleton Singleton::_st;

int main()
{
	Singleton::get_instance().insert("张三", 12000);
	Singleton& st1 = Singleton::get_instance();
	st1.insert("李四", 30000);
	st1.insert("王五", 45000);
	st1.insert("赵六", 60000);

	st1.print();

	Singleton& st2 = Singleton::get_instance();
	st2.print();
	st2.insert("周七", 8000);
	st2.insert("张三", 24000);

	st1.print();
	st2.print();
	// st1、st2 表示同一个实例

	return 0;
}

懒汉模式

第一种方式:加锁保证线程安全

#include <iostream>
#include <map>
#include <mutex>
using namespace std;

// RAII锁管理类
template<class Lock>
class LockGuard
{
public:
	LockGuard(Lock& lk)
		: _lk(lk)
	{
		_lk.lock();
	}

	~LockGuard()
	{
		_lk.unlock();
	}

private:
	Lock& _lk;
};

class Singleton
{
public:
	static Singleton& get_instance()
	{
		// 双重验证
		if (_pst == nullptr)		// 判断是否需要加锁,提高效率
		{
			LockGuard<mutex> lock(_mtx);
			//lock_guard<mutex> lock(_mtx);	// 锁管理器可以直接使用库里面的
			if (_pst == nullptr)	// 判断是否已经创建实例,保证线程安全
			{
				_pst = new Singleton;
			}
		}

		return *_pst;
	}

	void insert(string name, int salary)
	{
		_info[name] = salary;
	}

	void print()
	{
		for (auto& e : _info)
		{
			cout << e.first << " : " << e.second << endl;
		}
		cout << endl;
	}

	// 单例模式一般不考虑资源回收问题,都是在程序结束时自动回收资源
	// 只有在需要在需要手动回收做数据处理的时候,再提供回收接口
	void del_instance()
	{
		lock_guard<mutex> lock(_mtx);
		if (_pst)
		{
			delete _pst;
			_pst = nullptr;
		}
	}

private:
	Singleton()
	{}

	Singleton(const Singleton& st) = delete;
	Singleton& operator=(const Singleton& st) = delete;

private:
	map<string, int> _info;
	static Singleton* _pst;
	static mutex _mtx;
};

Singleton* Singleton::_pst;
mutex Singleton::_mtx;

int main()
{
	Singleton::get_instance().insert("张三", 12000);
	Singleton& st1 = Singleton::get_instance();
	st1.insert("李四", 30000);
	st1.insert("王五", 45000);
	st1.insert("赵六", 60000);

	st1.print();

	Singleton& st2 = Singleton::get_instance();
	st2.print();
	st2.insert("周七", 8000);
	st2.insert("张三", 24000);

	st1.print();
	st2.print();

	return 0;
}

第二种方式:静态局部对象保证线程安全

#include <iostream>
#include <map>
#include <mutex>
using namespace std;

class Singleton
{
public:
	static Singleton& get_instance()
	{
		static Singleton st;
		return st;
	}

	void insert(string name, int salary)
	{
		_info[name] = salary;
	}

	void print()
	{
		for (auto& e : _info)
		{
			cout << e.first << " : " << e.second << endl;
		}
		cout << endl;
	}

private:
	Singleton()
	{}

	Singleton(const Singleton& st) = delete;
	Singleton& operator=(const Singleton& st) = delete;

private:
	map<string, int> _info;
};

int main()
{
	Singleton::get_instance().insert("张三", 12000);
	Singleton& st1 = Singleton::get_instance();
	st1.insert("李四", 30000);
	st1.insert("王五", 45000);
	st1.insert("赵六", 60000);

	st1.print();

	Singleton& st2 = Singleton::get_instance();
	st2.print();
	st2.insert("周七", 8000);
	st2.insert("张三", 24000);

	st1.print();
	st2.print();

	return 0;
}

C++11之前,静态局部变量的初始化不能保证线程安全,C++11之后,静态局部变量的初始化是线程安全的。

工厂模式

简单工厂模式

简单工厂模式属于类的创建型模式,又叫做静态工厂模式。通过专门定义一个类来负责创建其他类的示例,被创建的示例通常都具有共同的父类。

实现步骤:

  1. 提供一个工厂类:负责创建所有实例的内部逻辑,可被外界直接调用,创建所需的产品对象。
  2. 提供一个抽象产品类:简单工厂模式所创建的所有模式的父类,它负责描述所有实例所共有的公共接口。
  3. 提供多个具体产品类:简单工厂模式所创建的具体实例对象。

简单工厂类代码示例:实现运算器

#include <iostream>
using namespace std;

// 抽象产品类
class Operation
{
public:
	double _lVal;
	double _rVal;
	virtual double get_result() = 0;
};

// 加减乘除具体产品类
class AddOperator : public Operation
{
	double get_result()
	{
		return _lVal + _rVal;
	}
};

class SubOperator : public Operation
{
	double get_result()
	{
		return _lVal - _rVal;
	}
};

class MulOperator : public Operation
{
	double get_result()
	{
		return _lVal * _rVal;
	}
};

class DivOperator : public Operation
{
	double get_result()
	{
		return _lVal / _rVal;
	}
};

// 工厂类
class OperatorFactory
{
public:
	static Operation* create_operation(const char op)
	{
		switch (op)
		{
		case '+':
			return new AddOperator();
		case '-':
			return new SubOperator();
		case '*':
			return new MulOperator();
		case '/':
			return new DivOperator();
		default:
			return nullptr;
		}
	}
};

int main()
{
	Operation* add = OperatorFactory::create_operation('+');
	add->_lVal = 10;
	add->_rVal = 20;
	cout << add->get_result() << endl;

	add = OperatorFactory::create_operation('-');
	add->_lVal = 10;
	add->_rVal = 20;
	cout << add->get_result() << endl;

	return 0;
}

优点:

  • 帮助封装,实现组件封装,面向接口编程
  • 解耦合,客户端和具体实现类的解耦合

缺点:

  • 可能增加客户端的复杂度
  • 不方便扩展

工厂方法模式

工厂方法模式又被称为多态工厂模式。工厂方法模式的意义是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类当中。核心工厂类不再负责产品的创建,这样核心类成为一个抽象工厂角色,仅负责具体工厂子类必须实现的接口,这样进一步抽象化的好处是使得工厂方法模式可以使系统在不修改具体工厂角色的情况下引进新产品。

工行方法模式和简单工厂模式在结构上相似。工厂方法类的核心是一个抽象工厂类,而简单工厂模式把核心放在一个具体类上,工厂方法类之所以又叫多态工厂类是因为具体工厂类都有共同的接口,或者有共同的抽象父类。当系统拓展需要添加新的产品对象时,仅仅需要添加一个具体对象和一个具体工厂对象,原有工厂对象不需要进行任何修改,也不需要修改客户端,很好的符合了“开放-封闭”原则,而简单工厂模式在添加新产品对象后不得不修改工厂方法,拓展性不好。

实现步骤:

  1. 提供一个抽象工厂类:所有具体工厂类的父类
  2. 提供与产品对应的工厂类:负责实例化产品对象
  3. 提供一个抽象产品类:所有产品的父类
  4. 提供一个或多个产品类:工厂方法模式所创建的具体实例对象

 工厂方法模式代码示例:建造飞机火箭

#include <iostream>
using namespace std;

// 抽象产品类
class AbstractProduct
{
public:
	virtual void make_product() = 0;

	virtual ~AbstractProduct()
	{}
};

// 抽象工厂类
class AbstractFactory
{
public:
	virtual AbstractProduct* create_product() = 0;

	virtual ~AbstractFactory()
	{}
};

// 具体产品类
class PlaneProduct : public AbstractProduct
{
public:
	void make_product()
	{
		cout << "make a plane..." << endl;
	}
};

class PlaneFactory : public AbstractFactory
{
	AbstractProduct* create_product()
	{
		return new PlaneProduct;
	}
};

class RocketProduct : public AbstractProduct
{
public:
	void make_product()
	{
		cout << "make a rocket..." << endl;
	}
};

class RocketFactory :public AbstractFactory
{
	AbstractProduct* create_product()
	{
		return new RocketProduct;
	}
};

int main()
{
	AbstractFactory* factory = new PlaneFactory;
	AbstractProduct* product = factory->create_product();
	product->make_product();

	factory = new RocketFactory();
	product = factory->create_product();
	product->make_product();

	return 0;
}

优点:

  • 需求改变时改动小
  • 具体的创建示例过程与客户端分离

缺点:

  • 新增产品时,工程量大

抽象工厂模式

抽象工厂模式是所有形态的工厂模式中最为抽象和一般性的,抽象工厂模式可以向客户端提供一个接口,使得客户端不必指定产品的具体类型的情况下,能够创建多个产品族的产品对象。

抽象工厂方法是针对一个产品族,使得易于交换产品系列,只需改变具体的工厂就可以使用不同的产品配置。当一个族中的产品对象被设计成一起工作且一个应用只是用同一族的对象,例如设计系统生成不同的UI界,按钮、边框等UI元素在一起使用,并且只能同属于一种风格,这很容易使用抽象工厂实现。

实现步骤:

  1. 提供一个抽象工厂类:声明一组创建一族产品的方法
  2. 提供一个具体工厂类:实现在抽象工厂创建产品的工厂方法
  3. 提供一个抽象产品类:抽象产品中声明了产品具有的业务方法
  4. 提供一个具体产品类:实现抽象产品接口中声明的业务方法

抽象工厂代码示例:鼠标键盘产品族

#include <iostream>
using namespace std;

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

	virtual ~Product() {}
};

// 抽象产品族1:键盘
class Keyboard : public Product 
{
public:
	virtual ~Keyboard() {}
};

class LogiKeyboard : public Keyboard
{
	void show()
	{
		cout << "罗技键盘" << endl;
	}
};

class RazerKeyboard : public Keyboard
{
	void show()
	{
		cout << "雷蛇键盘" << endl;
	}
};

// 抽象产品族2:鼠标
class Mouse : public Product
{
public:
	virtual ~Mouse() {}
};

class LogiMouse : public Mouse
{
	void show()
	{
		cout << "罗技鼠标" << endl;
	}
};

class RazerMouse : public Mouse
{
	void show()
	{
		cout << "雷蛇鼠标" << endl;
	}
};

// 抽象工厂
class AbstractFactory
{
public:
	virtual Keyboard* create_keyboard() = 0;
	virtual Mouse* create_mouse() = 0;

	virtual ~AbstractFactory() {}
};

// 具体工程
class LogiFactory : public AbstractFactory
{
	Keyboard* create_keyboard()
	{
		return new LogiKeyboard;
	}

	Mouse* create_mouse()
	{
		return new LogiMouse;
	}
};

class RazerFactory : public AbstractFactory
{
	Keyboard* create_keyboard()
	{
		return new RazerKeyboard;
	}

	Mouse* create_mouse()
	{
		return new RazerMouse;
	}
};

int main()
{
	AbstractFactory* factory = new LogiFactory;
	Keyboard* keyboard = factory->create_keyboard();
	Mouse* mouse = factory->create_mouse();

	keyboard->show();
	mouse->show();

	delete keyboard;
	delete mouse;
	delete factory;

	factory = new RazerFactory;
	keyboard = factory->create_keyboard();
	mouse = factory->create_mouse();

	keyboard->show();
	mouse->show();

	delete keyboard;
	delete mouse;
	delete factory;
	keyboard = nullptr;
	mouse = nullptr;
	factory = nullptr;

	return 0;
}

优点:

  • 抽象工厂封装了变化,封装了对象创建的具体细节
  • 增加新的产品族很方便,无须修改已有系统
  • 针对接口进行编程,而不是针对具体产品进行编程

缺点:

  • 增加新的产品等级结构需对原系统做较大修改(违背“开放-封闭原则”)

观察者模式

观察者模式定义了对象之间的一对多依赖,当一个对象改变状态时,它的所有依赖都会收到通知并自动更新。

实现步骤:

  1. 抽象主题(抽象被观察者)角色:把所有观察者角色保存在一个集合里,每个主题都有任意数量的观察者。抽象主题提供一个接口,可以增加和删除对象。
  2. 具体主题(具体被观察者)角色:将所有状态存入具体观察者对象。在具体主题的内部发生改变时,给所有注册过的观察者发送通知。
  3. 抽象观察者角色:观察者的抽象类。定义了一个更新接口,使得在得到主题改通知时更新自己。
  4. 具体观察者角色:实现抽象观察者定义的接口。在得到主题更改通知时更新自身状态。

示例:气象系统有三个部分分别是气象站(获取实际气象数据的物理装置)、WeatherData对象(用来追踪气象站的数据并更新布告板)、布告板(显示当前天气状态给用户)。WeatherData对象知道如何跟物理气象站联系,以取得更新信息。WeatherData对象会随即更新三个布告板的显示:目前状况(温度、湿度、气压)、气象统计、天气预报。我们的程序是建立一个应用,利用WeatherData对象取得数据,并更新三个布告板:目前状况、气象统计和天气预报。

#include <iostream>
#include <list>
using namespace std;

class Observer
{
public:
	virtual void update(float tempreature, float humudity, float pressure) = 0;

	virtual ~Observer()
	{}
};

class DisplayElement
{
public:
	virtual void display()
	{};

	virtual ~DisplayElement()
	{}
};

class Subject
{
public:
	virtual void regsiter_observer(Observer* obs)
	{};

	virtual void remove_observer(Observer* obs)
	{};

	virtual void notify_observer() = 0;

	virtual ~Subject()
	{}
};

// WeatherData类:注册、删除、通知观察者
class WeatherData : public Subject
{
public:
	void regsiter_observer(Observer* obs)
	{
		_observers.push_back(obs);
	}

	void remove_observer(Observer* obs)
	{
		_observers.remove(obs);
	}

	void notify_observer()
	{
		list<Observer*>::iterator it = _observers.begin();
		while (it != _observers.end())
		{
			Observer* obs = *it;
			obs->update(_tempreature, _humidity, _pressure);
			++it;
		}
	}

	void measurements_changed()
	{
		notify_observer();
	}

	void set_measurements(float t, float h, float p)
	{
		_tempreature = t;
		_humidity = h;
		_pressure = p;
		measurements_changed();
	}

private:
	list<Observer*> _observers;
	float _tempreature;
	float _humidity;
	float _pressure;
};

// 布告板:申请注册、实时更新和显示
class CurrentConditionsDisplay : public Observer, public DisplayElement
{
public:
	CurrentConditionsDisplay(WeatherData* weatherData)
	{
		_weatherData = weatherData;
		_weatherData->regsiter_observer(this);
	}

	void update(float tempreature, float humidity, float pressure)
	{
		_tempreature = tempreature;
		_humidity = humidity;
		_pressure = pressure;
		display();
	}

	void display()
	{
		cout << "Current Conditions" << endl;
		cout << "tempreature: " << _tempreature << endl;
		cout << "humidity: " << _humidity << endl;
		cout << "pressure: " << _pressure << endl;
		cout << endl;
	}

private:
	float _tempreature;
	float _humidity;
	float _pressure;
	Subject* _weatherData;
};

// 客户代码
int main()
{
	WeatherData* testData = new WeatherData();
	CurrentConditionsDisplay* testDisplay = new CurrentConditionsDisplay(testData);

	testData->set_measurements(32.5, 65, 30.4);
	testData->set_measurements(37.5, 75, 34.4);
	testData->set_measurements(22.5, 45, 26.4);
	// 修改数据,实现自动更新布告板

	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/542723.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

chatgpt赋能Python-python3_8降级3_7

Python 3.8降级至3.7&#xff1a;为什么需要这么做&#xff1f; Python 3.8是Python编程语言的最新版本&#xff0c;拥有许多令人兴奋的新功能和改进。但是&#xff0c;在某些情况下&#xff0c;您可能需要降级Python版本&#xff0c;特别是当某些库或框架不兼容Python 3.8时。…

AI绘图实战(十一):将纸质儿童画修改为电子照片/3D Openpose插件使用 | Stable Diffusion成为设计师生产力工具

S&#xff1a;AI能取代设计师么&#xff1f; I &#xff1a;至少在设计行业&#xff0c;目前AI扮演的主要角色还是超级工具&#xff0c;要顶替&#xff1f;除非甲方对设计效果无所畏惧~~ 预先学习&#xff1a; 安装及其问题解决参考&#xff1a;《Windows安装Stable Diffusion …

在vscode调试c++代码报错

在vscode调试c代码报错 一、我在vscode调试c代码,报错:错误原因&#xff1a;解决办法: 二、上面的问题解决之后&#xff0c;报错问题变了错误原因&#xff1a;路径中的“随笔”是中文&#xff0c;路径中不能出现中文&#xff01;解决办法&#xff1a;将路径中的“随便”改成英文…

『python爬虫』25. 接入超级鹰处理验证码(保姆级图文)

目录 1. 验证码平台的使用1.1 下载demo程序1.2 注册后生成软件id1.3 查验证码类型1.4 demo文件中填写我们的用户参数测试效果 2. 分析超级鹰的登录3. 完整代码总结 欢迎关注 『python爬虫』 专栏&#xff0c;持续更新中 欢迎关注 『python爬虫』 专栏&#xff0c;持续更新中 1.…

Golang每日一练(leetDay0070) 移除链表元素、计数质数

目录 203. 移除链表元素 Remove Linked-list Elements &#x1f31f; 204. 计数质数 Count Primes &#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Rust每日一练 专栏 Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 专栏 Java每…

chatgpt赋能Python-python3_8安装pyqt5

Python3.8安装PyQt5教程 介绍PyQt5 PyQt5是一个用于创建桌面应用程序的Python模块。它利用Qt框架的本地GUI应用程序开发工具包&#xff0c;为Python开发者提供了一种方便的方式来创建跨平台的应用程序。PyQt5支持在Windows&#xff0c;MacOS和Linux等主要桌面操作系统上构建G…

ChatGPT api 接口调用测试

参考文档&#xff1a; https://platform.openai.com/docs/quickstart/build-your-application示例说明&#xff1a; 本示例会生成一个简单的ChatGPT api接口调用server程序&#xff0c;该程序可以给用户输入的宠物类别为宠物取三个名字。打开网页后&#xff0c;会看到用户输入…

chatgpt赋能Python-python3_8_5怎么保存

Python 3.8.5&#xff1a;什么是它&#xff0c;为什么它重要&#xff0c;以及如何安装和保存&#xff1f; Python是当今世界上最受欢迎的编程语言之一。Python 3.8.5是该语言的最新版本&#xff0c;它引入了一些重要的改进和新特性。这篇文章将介绍Python 3.8.5并向您展示如何…

macOS Ventura 13.4 (22F66) Boot ISO 原版可引导镜像下载

本站下载的 macOS 软件包&#xff0c;既可以拖拽到 Applications&#xff08;应用程序&#xff09;下直接安装&#xff0c;也可以制作启动 U 盘安装&#xff0c;或者在虚拟机中启动安装。另外也支持在 Windows 和 Linux 中创建可引导介质。 macOS Ventura 13.4 包括以下增强功…

[创业之路-68]:科创板上市公司符合哪些条件

上交所发布《关于在上交所设立科创板并试点注册制相关情况答记者问》。上交所将认真落实习指示&#xff0c;在证监会的指导下&#xff0c;积极研究制订科创板和注册制试点方案&#xff0c;向市场征求意见并履行报批程序后实施。科创板是独立于现有主板市场的新设板块&#xff0…

chatgpt赋能Python-python3_8_2怎么用

Python 3.8.2 指南&#xff1a;介绍、使用和结论 Python 3.8.2 是近期发布的一个重大更新&#xff0c;它提供了很多新的特性和改进&#xff0c;使得 Python 编程更加高效、方便和强大。本文将向您介绍 Python 3.8.2 的主要特性和使用方法&#xff0c;帮助您更好的利用 Python …

【前后端分离博客】学习笔记05 --- canal

一、概述 使用canal rabbitMQ 实现 MySQL 和 Elasticsearch 的数据同步 图解&#xff1a; 流程如下&#xff1a; 给mysql开启binlog功能 mysql完成增、删、改操作都会记录在binlog中 canal监听binlog变化并发送消息到MQ&#xff0c;项目接收消息并实时更新elasticsearch中…

IS215UCVEH2AE speed tronic涡轮控制PCB板

IS215UCVEH2AETPMC815-11IS215UCVEM08B IS215UCVEH2AE是通用电气公司Mark VI Speedtronic控制系统的一个组件。该板的功能相当于VME控制卡。VME卡架是Mark VI的标准架&#xff0c;有13或21槽架可供选择。 IS215UCVEH2AE是一个带有附加前面板的矩形板。面板顶部和底部有一个固…

震惊——某白帽破解zabbix系统,实现命令执行,最终获取shell

如果你坚持了自己的梦想&#xff0c;全世界都会为你让路。 一、漏洞说明 zabbix&#xff08;[zbiks]&#xff09;是一个基于WEB界面的提供分布式系统监视以及网络监视功能的企业级的开源解决方案&#xff08;百度百科定义&#xff09;。很多Zabbix的admin口令使用了初始账号&…

【学习日记2023.5.17】之 项目入门介绍_YApi_Swagger

文章目录 1. 项目入门1.1 软件开发整体介绍1.2 项目介绍1.3 开发环境搭建1.3.1 前端环境搭建1.3.2 后端环境搭建1.3.3 登录功能完善1.3.4 前后端分离开发流程 1.4 导入接口文档1.5 Swagger介绍使用方式Swagger常用注解 1.6 注意事项 1. 项目入门 1.1 软件开发整体介绍 软件开发…

PySide6/PyQT多线程之 高效管理多线程:暂停、恢复和停止的最佳实践

前言 关于 PySide6/PyQT 多线程&#xff0c;正确地处理多线程编程并确保线程之间的同步和通信并不容易。 本文以一个示例代码为基础&#xff0c;介绍 PySide6/PyQT多线程的运用&#xff0c;展示如何创建和管理线程&#xff0c;以及如何实现线程之间的同步和通信。 设想这么一个…

IP组播路由协议(组播内部网关协议)

IP组播路由协议:用来建立组播树&#xff0c;是实现组播传输的关键技术。分为源分发树和共享分发树。 PIM:协议无关组播 密集模式&#xff1a; PIM-DM:密集模式PIM DVMRP:距离矢量组播路由协议 MOSPF:组播开放式最短链路优先 稀疏模式 CBT:基于核心的树 PIM-SM:稀疏模式PIM 这…

微星UEFI签名密钥泄露引发“末日”供应链攻击恐慌

对硬件制造商微星Micro-Star International&#xff08;更广为人知的名称是 MSI&#xff09;的勒索软件入侵引发了人们对毁灭性供应链攻击的担忧&#xff0c;这些攻击可能会注入恶意更新&#xff0c;这些更新已使用受大量最终用户设备信任的公司签名密钥进行签名。 这有点像世…

chatgpt赋能Python-python3_7怎么更新

Python3.7如何更新&#xff1f; 对于拥有10年Python编程经验的工程师来说&#xff0c;更新Python版本是必不可少的。现在最新版本的Python是3.7&#xff0c;那么这个版本应该如何更新呢&#xff1f; 更新步骤 下载Python3.7安装包 在Python官网上下载即可。如果你使用的是L…

Cube Map 系列之:手把手教你 实现 立方体贴图

什么是Cube Map 在开始立方体贴图之前&#xff0c;我们先简单了解下cube map。 cube map 包含了六个纹理&#xff0c;分别表示了立方体的六个面&#xff1b; 相较二维的纹理使用坐标uv来获取纹理信息&#xff0c;这里我们需要使用三维的方向向量来获取纹理信息&#xff08;一…