设计模式之单例、工厂、发布订阅者模式设计模式

单例模式

​ 保证一个类仅有一个实例,并提供一个该实例的全局访问点

​ 在软件系统中,经常有这样一些特殊的类,必须保证他们 在系统中只存在一个实例,才能确保它们的逻辑正确性, 以及良好的效率

应用场景:

DBPool 、读取配置文件

单例模式分类:

  • 1、懒汉式 — 需要使用单例的时候,才进行初始化
  • 2、饿汉式 — 未调用单例的时候,已经进行初始化

写一个单例模式的demo

#include <iostream>
#include <mutex>
#include <thread>
 
using namespace std;
 
//设计线程的个数
#define PTHREAD_NUM  20
//懒汉式 饿汉式 单例模式的选型
#define SINGELTON_SELECTOR 0
 
//单例模式
 
#if SINGELTON_SELECTOR
 
//懒汉式  -- 调用的时候才初始化
class Singleton{
 
private:
	Singleton(){
		cout<<"Singleton construct  1111\n";
	}
	~Singleton(){
		cout<<"Singleton destruct   1111\n";
	}
 
	//禁止拷贝构造
	Singleton(const Singleton &si) = delete;
	//禁止等号赋值
	Singleton & operator=(const Singleton &si) = delete;
 
public:
	static Singleton * getInstance(){
		static Singleton m_singleton;
		return &m_singleton;
	}
};
 
#else
 
//饿汉式 -- 调用之前就已经初始化好,调用的时候直接返回地址
class Singleton{
 
private:
	Singleton(){
		cout<<"Singleton construct   2222\n";
	}
	~Singleton(){
		cout<<"Singleton destruct    2222\n";
	}
 
	//禁止拷贝构造
	Singleton(const Singleton &si) = delete;
	//禁止等号赋值
	Singleton & operator=(const Singleton &si) = delete;
	static Singleton m_singleton;
	
public:
	static Singleton * getInstance(){
		return &m_singleton;
	}
};
 
Singleton Singleton::m_singleton;
 
 
#endif 
 
//定义一个互斥锁,保证只有一个线程在打印 单例变量的地址
static mutex m;
void print_address()
{
	Singleton* singleton = Singleton::getInstance();
 
	m.lock();
	cout<<singleton<<endl;
	m.unlock();
}
//测试单例模式
void test_singleton()
{
 
	thread threads[PTHREAD_NUM];
	for(auto &t : threads)
		t = thread(print_address);
 
	for(auto &t : threads)
		t.join();
}
 
int main(int argc,char * argv[])
{
 
	cout<<"main\n";
 
	test_singleton();
 
}
 

工厂模式

  • 定义一个用于创建对象的接口,让子类决定实例化哪一个类。
  • Factory Method使得一个类的实例化延迟(目的:解耦,手段:虚函数)到子类
  • 在软件系统中,经常面临着创建对象的工作;由于需求的 变化,需要创建的对象的具体类型经常变化

使用工厂模式提供一种“封装机制”来避免客户程序和这种“具 体对象创建工作”的紧耦合 来解决这个问题

应用场景:

  • 数据导出,导出为Excel,文本,XML
  • 支付接口,可能对应不同的支付网关

写一个工厂模式的demo

#include <iostream>
 
using namespace std;
 
 
//工厂模式 -- 模拟一个简s单文件的解析方式
//定义一个产品的概念
class Parse_file{
public:
	Parse_file(){}
	//定义虚析构函数,防止父类指针指向子类对象后,释放内存出现内存泄露的问题
	virtual ~Parse_file(){}
	//定义一个接口,子类负责实现
	virtual bool myparse(string data) = 0;
 
};
 
//定义实际的产品 text方式解析
class text_parse : public Parse_file{
public:
	text_parse(){}
	virtual ~text_parse(){}
 
	virtual bool myparse(string data){
		cout<<"以 text 的方式保存 数据"<<data<<endl;
		return true;
	}
	
};
 
 
//定义实际的产品 xml方式解析
class xml_parse : public Parse_file{
public:
	xml_parse(){}
	virtual ~xml_parse(){}
 
	virtual bool myparse(string data){
		cout<<"以 xml 的方式保存 数据"<<data<<endl;
		return true;
	}
};
 
 
//定义实际的产品 json方式解析
class json_parse : public Parse_file{
public:
	json_parse(){}
	virtual ~json_parse(){}
 
	virtual bool myparse(string data){
		cout<<"以 json 的方式保存 数据"<<data<<endl;
		return true;
	}
};
 
 
//定义实际的产品 protobuf方式解析
class protobuf_parse : public Parse_file{
public:
	protobuf_parse(){}
	virtual ~protobuf_parse(){}
 
	virtual bool myparse(string data){
		cout<<"以 protobuf 的方式保存 数据"<<data<<endl;
		return true;
	}
};
 
//定义工厂来生产产品
class factory{
public:
	factory(){}
	virtual ~factory(){}
 
	//定义工厂的解析方法
//便于子类继承
	virtual bool myparse(int type,string data){
		Parse_file * pp = nullptr;
		pp = parse_method(type);
 
		int ret = false;
 
		if(pp){
			pp->myparse(data);
			delete pp;
			ret = true;
		}
		else{
			cout<<"no parse function\n";
		}
		return ret;
	}
protected:
//便于子类继承
	virtual Parse_file * parse_method(int type){
		Parse_file * pp = nullptr;
		if(type == 1){
			pp = new text_parse();
		}else if(type == 2){
			pp = new xml_parse();
		}
		return pp;
	}
};
 
//扩展工厂
class factory2 : public factory{
public:
	factory2(){}
	virtual ~factory2(){}
 
protected:
//便于子类继承
	virtual Parse_file * parse_method(int type){
		Parse_file * pp = nullptr;
		if(type == 3){
			pp = new json_parse();
		}else if(type == 4){
			pp = new protobuf_parse();
		}
		else{
			pp = factory::parse_method(type);
		}
		return pp;
	}
};
 
int main()
{
	factory * fac = new factory();
	fac->myparse(1,"数据");
	fac->myparse(2,"数据");
	fac->myparse(3,"数据");
	fac->myparse(4,"数据");
 
	cout<<"\n\n-----------------\n\n";
 
 
	factory * fac2 = new factory2();
 
	fac2->myparse(1,"数据");
	fac2->myparse(2,"数据");
	fac2->myparse(3,"数据");
	fac2->myparse(4,"数据");
	
	return 0;
}

效果

img

发布订阅模式与观察者模式

发布订阅模式和观察者模式是同一个东西吗? NONONO

  • 观察者模式里,只有两个角色 —— 观察者 + 被观察者
  • 发布订阅模式里 —— 观察者 + 中间经纪人 +被观察者

观察者模式中的推模型和拉模型:

推模型:

目标对象主动向观察者推送目标的详细信息,不 管观察者是否需要,推送的信息通常是目标对象的全部或 部分数据,相当于广播通信。

拉模型:

目标对象在通知观察者的时候,只传递少量的信 息。如果观察者需要更具体的信息,由观察者主动到目标 对象中获取,相当于是观察者从目标对象中拉数据。一般 这种模型的实现中,会把目标对象通过update方法传递给 观察者,这样在观察者需要获取数据的时候,就可以通过 这个引用来获取了。

应用场景:

​ 公众号通知,淘宝通知,知乎通知,微信通知等等。

写一个观察者模式的demo

//观察者模式,需要弄明白 何为观察者,何为目标
//以我们用手机看报纸为例, 我们 是观察者, 报纸是目标
//接下来我们来模拟一下观察者模式
 
#include <iostream>
#include <list>
 
using namespace std;
 
class subject;
 
//定义抽象的观察者
class observer{
 
public:
	observer(){}
	virtual ~observer(){}
 
	virtual void update(subject * sub) = 0;//读摘要
	virtual void update(string content) = 0;//读内容
 
};
 
//定义一个抽象的目标
class subject{
 
public:
	subject(){}
	virtual ~subject(){}
//设置内容
	virtual int setContent(string content)=0;
//得到具体内容 -- 用于推模型
	virtual string getContent()=0;
//得到摘要 -- 用于拉模型
 	virtual string getSummary()=0;
//订阅
	virtual void attach(observer * ob){
		oblist.push_back(ob);
	}
//取消订阅
	virtual void detach(observer * ob){
		oblist.remove(ob);
	}
//通知所有订阅者 -- 推模型
	virtual void notifyAllobserver(string content) {
		for(auto &a : oblist){
			a->update(content);
		}
	}
//通知所有订阅者 -- 拉模型	
	virtual void notifyAllobserver(){
		for(observer * reader : oblist){
			reader->update(this);
		}
	}
private:
	list<observer *> oblist;
 
};
 
 
//定义具体的 观察者,读者
class reader: public observer
{
public:
	reader(){}
	virtual ~reader(){}
//拉模型
	virtual void update(subject * sub){
		cout<<getName()<<"正在阅读的内容是:"<<sub->getContent()<<endl;
	}
//推模型
	virtual void update(string content){
		cout<<getName()<<"正在阅读的内容是:"<<content<<endl;
	}
	
	string getName(){return m_name;}
	void setName(string name){m_name = name;}
private:
	string m_name;
 
};
 
 
//定义具体的目标,推送新闻信息
class newspaper:public subject
{
public:
	newspaper(){};
	virtual ~newspaper(){}
 
//设置内容
	virtual int setContent(string content){
		m_content = content;
		notifyAllobserver(); //默认是拉模型,就想给你推送一个摘要一样
		return 1;
	}
//得到具体内容 -- 用于推模型
	virtual string getContent(){
		return m_content;
	}
//得到摘要 -- 用于拉模型
 	virtual string getSummary(){
 		return "摘要";
 	}
 
 
private:
	string m_content;
};
 
 
 
int main(int argc,char *argv[])
{
 
	//定义报纸主题
	newspaper *subject = new newspaper();
 
	//定义读者
	reader * r1 = new reader();
	r1->setName("adele");
 
	reader * r2 = new reader();
	r2->setName("Bob");
 
	reader * r3 = new reader();
	r3->setName("ceilina");
 
 
	//设置内容
	
	//报纸开始加入订阅者
	subject->attach(r1);
	subject->setContent("今天多云");
	cout << "\n----------华丽的分割线 \n"<<endl;
 
	
	subject->attach(r2);
	subject->setContent("今天晴天");
	cout << "\n----------华丽的分割线 \n"<<endl;
	
	subject->attach(r3);
	subject->setContent("over");
	
 
	cout<<"-------end-----\n";
 
 
	return 0;
}
 

效果

img

作者:小魔童哪吒

版权声明:本文为xiaomotong原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/xiaomotong/p/14496014.html