单例模式
保证一个类仅有一个实例,并提供一个该实例的全局访问点
在软件系统中,经常有这样一些特殊的类,必须保证他们 在系统中只存在一个实例,才能确保它们的逻辑正确性, 以及良好的效率
应用场景:
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; }
效果
发布订阅模式与观察者模式
发布订阅模式和观察者模式是同一个东西吗? 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; }
效果