设计模式
设计模式的六大原则:https://zhuanlan.zhihu.com/p/110130347
适配器模式
假设Client想要一个正方形(client Interface),但是提供的服务(service)是个圆形,那么我就把这个圆通过适配器(adapter)装到正方形里,adapter继承了正方形的接口,你以为你用的是个正方形,实际上里面是个圆。
优点:
单一职责原则:可以将接口或数据转换代码从程序主要业务逻辑中分离。
开闭原则: 只要客户端代码通过客户端接口与适配器进行交互,你就能在不修改现有客户端代码的情况下在程序中添加新类型的适配器。
缺点:
代码整体复杂度增加
#include<iostream>
using namespace std;//Target:客户端需要的目标接口,适配器你往哪转
class JsonInfo
{
public:virtual ~JsonInfo() {}//这个就是读取的json信息,我们要实现的其实就是xml转为jsonvirtual string resquest() const {return "Some Json msg.......";}
};//Adaptee: 需要转换的目标,客户端不兼容的接口,需要转换为Target
class XmlInfo
{
public:string specialResquest() const{return "Some Xml msg.......";}
};//Adapter: 适配器,把Adaptee包装起来,让他看上去像是Target
//我们是要转为json,所以需要继承,这样才可以看上去像json
class XmlJsonAdapter :public JsonInfo
{public:XmlJsonAdapter(XmlInfo* adptee) :m_adptee(adptee) {}virtual string resquest() const override{string string = m_adptee->specialResquest();return "Json->XmlJsonAdapter->XmlInfo";}
private://通过对象的方式把XmlInfo内容给包住XmlInfo* m_adptee;
};//表示长图的client
void clientCode(const JsonInfo* info)
{cout << "股票分析软件Json: " << info->resquest()<< endl;cout << "-----------------------" << endl;
}int main()
{JsonInfo jsonInfo;clientCode(&jsonInfo);XmlInfo xmlInfo;XmlJsonAdapter jsonAdapter(&xmlInfo);clientCode(&jsonAdapter);return 0;
}
经典的“方钉圆孔问题”
/*
有一个圆孔,可以把圆钉放进去,但现在只能提供方钉,所以需要一个适配器,把方钉裹成圆钉,就可以被适配器调用了。
获得裹后圆钉的半径,方钉的宽度除2再乘根号2圆孔就相当于client,圆钉表示客户端可以处理的类型,方钉相当于服务器提供的类型
*/
#include<iostream>
using namespace std;//定义圆钉
class RoundPeg
{
public:RoundPeg(){}virtual ~RoundPeg() {}RoundPeg(const double& r) :m_radius(r) {};virtual int getRadius() const{return this->m_radius;}
private:int m_radius;
};//定义方钉
class SqurePeg
{
public:virtual ~SqurePeg() {};SqurePeg(const int& wid) :m_wid(wid) {};int getWidth(){return m_wid;}
private:int m_wid;
};//定义圆孔
class RoundHole
{
public:RoundHole(const int& r) :radius(r) {}int getRadius(){return this->radius;}bool fits(RoundPeg* roundPeg){if (radius < roundPeg->getRadius())return false;elsereturn true;}
private:int radius;
};//定义适配器,继承自圆钉
class Adapter : public RoundPeg
{
public:Adapter(SqurePeg* adptee) :m_adptee(adptee) {}int getRadius() const override{int r = m_adptee->getWidth() * sqrt(2) / 2;return r;}private:SqurePeg* m_adptee;
};int main()
{//定义圆孔RoundHole hole = RoundHole(5);//定义圆钉RoundPeg r_peg1 = RoundPeg(3);RoundPeg r_peg2 = RoundPeg(10);//定义方钉SqurePeg s_peg1 = SqurePeg(2);SqurePeg s_peg2 = SqurePeg(10);bool res = hole.fits(&r_peg1);if (res) cout << "r_peg1 畅通无阻" << endl;else cout << "r_peg1 堵死" << endl;res = hole.fits(&r_peg2);if (res) cout << "r_peg2 畅通无阻" << endl;else cout << "r_peg2 堵死" << endl;Adapter adapter(&s_peg1);res = hole.fits(&adapter);if (res) cout << "s_peg1 畅通无阻" << endl;else cout << "s_peg1 堵死" << endl;Adapter adapter2(&s_peg2);res = hole.fits(&adapter2);if (res) cout << "s_peg2 畅通无阻" << endl;else cout << "s_peg2 堵死" << endl;return 0;
}
单例模式
单例模式:一个类不管创建多少次对象,永远只能得到该类型的一个实例。像日志模块,数据库模块就会用到。
个人理解,单例本质上就是全局变量,只不过是又包装了一层。
饿汉式单例模式:
饿汉式单例,即使没调用对象,static Singleton instance;也会实例化,因为存在数据段,会在main执行前执行初始化。
一定是线程安全的,线程函数启动前实例就会初始化好。
缺点:对象的实例化会调用构造函数,但是这个构造函数在实际应用时,会构造很多东西,比如加载配置文件什么的,要是不使用这些东西,构造的不就浪费了,白费时间
#include<iostream>using namespace std;class Singleton{public://3 获取类的唯一实例对象的接口方法static Singleton* getInstance(){return &instance;}private://2 定义一个唯一类的实例对象static Singleton instance;//1 构造函数私有化,并禁止拷贝构造和赋值构造Singleton(){}Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;};Singleton Singleton::instance;int main(){Singleton* p1 = Singleton::getInstance();Singleton* p2 = Singleton::getInstance();return 0;}
懒汉式单例实现模式:
把对象的实例化延迟到第一次获取该实例对象的时候
线程安全的单例模式-使用互斥锁加双重判断
#include<iostream>#include<mutex>using namespace std;mutex mtx;class Singleton{public://3 获取类的唯一实例对象的接口方法//锁加双重判断static Singleton* getInstance(){//lock_guard<mutex> guard(mtx); //锁的粒度太大了,单线程完全不用这样if (instance == nullptr){lock_guard<mutex> guard(mtx);/*开辟内存构造对象给instance赋值*/if (instance == nullptr){instance = new Singleton();}}//出括号释放锁return instance;}private://2 定义一个唯一类的实例对象 存放在数据段static Singleton volatile instance; //加上volatile后,当一个线程对instance改变,其他线程立马就能看到改变了,看的都是内存里的值//1 构造函数私有化,并禁止拷贝构造和赋值构造Singleton(){}Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;};Singleton* volatile Singleton::instance=nullptr;int main(){Singleton* p1 = Singleton::getInstance();Singleton* p2 = Singleton::getInstance();return 0;}
上面的代码可以关注下 volatile 的用法。
观察者模式
主要关注的是对象之间的一对多通信,也就是多个对象都依赖一个对象,当该对象状态发生改变的时候,其他对象都能够接收到相应的通知。
#include<iostream>
#include<unordered_map>
#include<list>
using namespace std;class Observer
{
public://处理消息的接口virtual void handle(int msgid) = 0;
};//观察者1
class Observer1 :public Observer
{
public:void handle(int msgid){switch (msgid){case 1:cout << "Observer1 recv 1 msg!" << endl;break;case 2:cout << "Observer1 recv 2 msg!" << endl;break;default:cout << "Observer1 recv unknown msg!" << endl;break;}}
};//观察者2
class Observer2 :public Observer
{
public:void handle(int msgid){switch (msgid){case 2:cout << "Observer2 recv 2 msg!" << endl;break;default:cout << "Observer2 recv unknown msg!" << endl;break;}}
};//观察者3
class Observer3 :public Observer
{
public:void handle(int msgid){switch (msgid){case 1:cout << "Observer3 recv 1 msg!" << endl;break;case 3:cout << "Observer3 recv 3 msg!" << endl;break;default:cout << "Observer3 recv unknown msg!" << endl;break;}}
};class Subject
{
public:void addObserver(Observer* obs, int msgid){//使用unordered_map,重载的[]运算符,如果找到了就直接插入,找不到会重新创建subMap[msgid].push_back(obs);}//主题检测发生变化,通知相应的观察者对象处理事件void dispatch(int msgid){auto it = subMap.find(msgid);if (it != subMap.end()){for (auto pObs : it->second){pObs->handle(msgid);}}}
private://使用一个map存储哪个观察者对哪个消息感兴趣unordered_map<int, list<Observer*>> subMap;
};int main()
{Subject subject;Observer* p1 = new Observer1();Observer* p2 = new Observer2();Observer* p3 = new Observer3();subject.addObserver(p1, 1);subject.addObserver(p1, 2);subject.addObserver(p2, 2);subject.addObserver(p3, 1);subject.addObserver(p3, 3);int msgid = 0;for (;;){cout << "输入消息ID:";cin >> msgid;if (msgid == -1)break;subject.dispatch(msgid);}return 0;
}
代理模式
这里主要说的是保护代理,即给不同的用户提供不同的对象访问权限。
通过代理类,来控制实际对象的访问权限。
下面的代码,主要是实现了用户观看普通电影,VIP电影,用券影的相关权限。
#include<iostream>
using namespace std;//抽象类
class VideoSite
{
public:virtual void freeVideo() = 0;virtual void vipVideo() = 0;virtual void ticketVideo() = 0;
};//委托类
class FixBugVideoSite :public VideoSite
{virtual void freeVideo(){cout << "观看免费电影" << endl;}virtual void vipVideo(){cout << "观看免费电影" << endl;}virtual void ticketVideo(){cout << "观看免费电影" << endl;}
};//代理类 代理委托类
class FreeVideoSite :public VideoSite
{
public:FreeVideoSite() { pVideo = new FixBugVideoSite(); }~FreeVideoSite() { delete pVideo; }//必须全部重写这些方法,要是不重写,代理类本身也成抽象类了void freeVideo(){pVideo->freeVideo();}void vipVideo(){cout << "您是普通用户,请升级VIP后观看" << endl;}void ticketVideo(){cout << "您未购买券,请购买后观看" << endl;}
private:VideoSite* pVideo;
};class VipVideoSite :public VideoSite
{
public:VipVideoSite() { pVideo = new FixBugVideoSite(); }~VipVideoSite() { delete pVideo; }//必须全部重写这些方法,要是不重写,代理类本身也成抽象类了void freeVideo(){cout << "观看普通电影" << endl;}void vipVideo(){pVideo->vipVideo();}void ticketVideo(){cout << "您未购买券,请购买后观看" << endl;}
private:VideoSite* pVideo;
};class ticketVideoSite :public VideoSite
{
public:ticketVideoSite() { pVideo = new FixBugVideoSite(); }~ticketVideoSite() { delete pVideo; }//必须全部重写这些方法,要是不重写,代理类本身也成抽象类了void freeVideo(){cout << "观看普通电影" << endl;}void vipVideo(){cout << "观看VIP电影" << endl;}void ticketVideo(){pVideo->ticketVideo();}
private:VideoSite* pVideo;
};void showMovie(VideoSite* ptr)
{ptr->freeVideo();ptr->vipVideo();ptr->ticketVideo();
}int main()
{VideoSite* p1 = new FreeVideoSite();VideoSite* p2 = new ticketVideoSite();showMovie(p1);showMovie(p2);delete p1;delete p2;return 0;
}
简单工厂模式
在创建的类比较多的时候可以使用工厂模式,主要是封装了对象的创建。
把这些对象都封装到一个工厂类里面,不需要知道这个对象是怎么创建的,就比如士兵上战场,只需要给他一把枪就可以了,不需要让他再new一个枪出来。
不符合开闭原则
#include<iostream>
#include<memory>
using namespace std;//工厂模式主要是封装了对象的创建
/*
简单工厂模式
不符合开放封闭原则
如果要多加几个工厂,就得修改switch
*/
class Car
{
public:Car(string name) :m_name(name) {};virtual void show() = 0;
protected:string m_name;
};class Bmw : public Car
{
public:Bmw(string name) :Car(name) {}void show(){cout << "购买了一辆宝马:" << m_name << endl;}
};class Audi : public Car
{
public:Audi(string name) :Car(name) {}void show(){cout << "购买了一辆奥迪:" << m_name << endl;}
};enum CarType
{BMW,AUDI
};class SimpleFactory
{
public:Car* createCar(CarType ct){switch (ct){case BMW:return new Bmw("X1");case AUDI:return new Audi("A8");default:cerr << "传入参数不正确" << endl;break;}return nullptr;}
};int main()
{unique_ptr<SimpleFactory> factory(new SimpleFactory());unique_ptr<Car> p1(factory->createCar(BMW));unique_ptr<Car> p2(factory->createCar(AUDI));p1->show();p2->show();return 0;
}
工厂方法
可以弥补简单工厂方法的缺点,符合开闭原则。以本例来看,如果想生产其他的汽车,只需要添加对应的工厂类即可。
#include<iostream>
#include<memory>
using namespace std;/*
工厂方法可以解决简单工厂模式遇到的问题
一个基类,包含一个虚工厂函数,实现多态
多个子类,重写父类的工厂函数,每个工厂负责生产一种汽车,如果想生产其他的汽车,只需要添加对应的工厂类
*/class Car
{
public:Car(string name) :m_name(name) {};virtual void show() = 0;
protected:string m_name;
};class Bmw : public Car
{
public:Bmw(string name) :Car(name) {}void show(){cout << "购买了一辆宝马:" << m_name << endl;}
};class Audi : public Car
{
public:Audi(string name) :Car(name) {}void show(){cout << "购买了一辆奥迪:" << m_name << endl;}
};//工厂类
class Factory
{
public:virtual Car* createCar(string name) = 0;
};//宝马工厂
class BmwFty : public Factory
{Car* createCar(string name){return new Bmw(name);}
};//奥迪工厂
class AudiFty : public Factory
{Car* createCar(string name){return new Audi(name);}
};int main()
{unique_ptr<Factory> bmwfty(new BmwFty());unique_ptr<Factory> audifty(new AudiFty());unique_ptr<Car> p1(bmwfty->createCar("X1"));unique_ptr<Car> p2(audifty->createCar("A8"));p1->show();p2->show();return 0;
}
抽象工厂方法
一个工厂可能不止是生产汽车,也要生产相关的配件。
#include<iostream>
#include<memory>
using namespace std;/*
一个工厂不只生产车,还会生产相关的零件
*///产品系列1
class Car
{
public:Car(string name) :m_name(name) {};virtual void show() = 0;
protected:string m_name;
};class Bmw : public Car
{
public:Bmw(string name) :Car(name) {}void show(){cout << "购买了一辆宝马:" << m_name << endl;}
};class Audi : public Car
{
public:Audi(string name) :Car(name) {}void show(){cout << "购买了一辆奥迪:" << m_name << endl;}
};//系列产品2
class Light
{
public:virtual void show() = 0;
};class BmwLight :public Light
{void show(){cout << "BMW LIght" << endl;}
};class AudiLight :public Light
{void show(){cout << "Audi LIght" << endl;}
};//工厂类 -> 抽象工厂类(对有一组关联关系的产品,提供产品对象的统一创建)
//如果新建灯也写一个类,那类太多了,太麻烦了
class AbsFactory
{
public:virtual Car* createCar(string name) = 0; //工厂方法 创建汽车virtual Light* createLight() = 0; //工厂方法 创建汽车关联的产品,车灯
};//宝马工厂
class BmwFty : public AbsFactory
{Car* createCar(string name){return new Bmw(name);}Light* createLight(){return new BmwLight();}
};//奥迪工厂
class AudiFty : public AbsFactory
{Car* createCar(string name){return new Audi(name);}Light* createLight(){return new AudiLight();}
};int main()
{unique_ptr<AbsFactory> bmwfty(new BmwFty());unique_ptr<AbsFactory> audifty(new AudiFty());unique_ptr<Car> p1(bmwfty->createCar("X1"));unique_ptr<Car> p2(audifty->createCar("A8"));unique_ptr<Light> l1(bmwfty->createLight());unique_ptr<Light> l2(audifty->createLight());p1->show();l1->show();p2->show();l2->show();return 0;
}
工厂模式总结:
- 简单工厂:把对象的创建封装到一个接口函数里,通过传入不同的标识,返回创建的对象。客户不用自己负责 new 一个对象,不用了解对象创建的详细过程。但是不符合设计模式的开闭原则。
- 工厂方法:定义一个基类,提供一个纯虚函数(创建产品),定义派生类(具体产品的工厂)负责创建爱你对应的产品,可以做到不同的产品在不同的工厂里创建,能够实现对现有的工厂,以及产品的修改关闭。缺点是:实际上,很多产品都会有关联关系,不应该放在不同的工厂里去创建。
- 抽象工厂方法:把有关联关系的的产品放在一个抽象工厂里面AbsFactory,派生类负责创建该系列所有的产品。