学习设计模式更重要的是学习模式设计的原则
模式设计原则:
依赖倒置原则
高层模块不应该依赖底层模块,二者都应该依赖抽象
抽象不应该依赖具体实现,具体实现应该依赖抽象
开放封闭原则
⼀个类应该对扩展开放,对修改关闭
面向接口编程
不将变量类型声明为某个特定的具体类,而是声明为某个接口
客户程序无需获知对象的具体类型,只需要知道对象所具有的接口
减少系统中各部分的依赖关系,从而实现“高内聚、松耦合”的类型设计⽅案
封装变化点
将稳定点和变化点分离,扩展修改变化点;让稳定点与变化点的实现层次分离
单⼀职责原则
⼀个类应该仅有⼀个引起它变化的原因
里⽒替换原则
子类型必须能够替换掉它的父类型
主要出现在子类覆盖父类实现,原来使⽤父类型的程序可能出现错误
覆盖了父类方法却没实现父类方法的职责
接口隔离原则
不应该强迫客户依赖于他们不⽤的⽅法
⼀般⽤于处理⼀个类拥有⽐较多的接口,而这些接⼝涉及到很多职责;
对象组合优于类继承
继承耦合度高,组合耦合度低
什么情况下使⽤设计模式?
系统的关键依赖点;
能明确找到变化点;
能明确找到复用⽅向;
对需求变化方向熟悉;
如何找到设计模式?
从重构中获得;
重构
静态转变为动态;
早绑定转变为晚绑定;
继承转变为组合;
编译时依赖转变为运行时依赖;
紧耦合转变为松耦合;
为什么要学习设计模式?
从已有的且证明有效的设计模式中获取灵感,少⾛弯路;
通⽤语⾔,知道在已有的设计模式扩展代码;
体会模式设计,设计自己的行之有效的设计模式;
学习设计模式的步骤
深刻体会上面的原则;
理解设计模式,能知道设计模式的变化点和稳定点;
能在已使用的设计模式中,知道如何写扩展;
能在复杂需求中,抽象出已有设计模式;
模板方法
1.定义
定义⼀个操作中的算法的骨架 ,而将⼀些步骤延迟到⼦类中。
Template Method使得子类可以不改变⼀个算法的结构即可重定义该算法的某些特定步骤
2.背景
某个品牌动物园,有⼀套固定的表演流程,但是其中有若干个表演⼦流程受欢迎程度比较低
希望将这⼏个表演流程创新,以尝试迭代更新表演流程
3.要点
- 非常常用的设计模式,子类可以复写父类的子流程,使父类的⼤流程更父类来调⽤
- 充分体现了依赖倒置原则
4.本质
通过固定算法骨架来约束⼦类的⾏为
#include <iostream>
using namespace std;
class ZooShow {
public:
// 固定流程封装到这里
void Show() {
Show0();
Show1();
Show2();
Show3();
}
protected:
// 子流程 使用protected保护起来 不被客户调用 但允许子类扩展
virtual void Show0(){
cout << "show0" << endl;
}
virtual void Show2(){
cout << "show2" << endl;
}
virtual void Show1() {
}
virtual void Show3() {
}
};
class ZooShowEx : public ZooShow {
protected:
virtual void Show1(){
cout << "show1" << endl;
}
virtual void Show3(){
cout << "show3" << endl;
}
virtual void Show4() {
//
}
};
class ZooShowEx1 : public ZooShow {
protected:
virtual void Show0(){
cout << "show1" << endl;
}
virtual void Show2(){
cout << "show3" << endl;
}
};
class ZooShowEx2 : public ZooShow {
protected:
virtual void Show1(){
cout << "show1" << endl;
}
virtual void Show2(){
cout << "show3" << endl;
}
};
/*
依赖倒置原则
单一职责原则
接口隔离原则
反向控制:应用程序 框架 应用程序(变化的)应该依赖框架(稳定的),应该是框架去调应用程序,而不是应用程序去调框架
*/
int main () {
ZooShow *zs = new ZooShowEx;
ZooShow *zs1 = new ZooShowEx1;
ZooShow *zs2 = new ZooShowEx2;
zs->Show();
zs1->Show();
return 0;
}
观察者模式
1.定义
定义对象间的⼀种⼀对多(变化)的依赖关系,以便当⼀个对象(Subject)的状态发⽣改变时,所有
依赖于它的对象都得到通知并⾃动更新
2.背景
气象站发布气象资料给数据中心,数据中心经过处理,将气象信息更新到两个不同的显示终端,分别
是A终端和B终端
3.要点
- 观察者模式使得我们可以独⽴地改变⽬标与观察者,从⽽使⼆者之间的关系松耦合
- 观察者⾃⼰决定是否订阅通知,⽬标对象并不关注谁订阅了
- 观察者不要依赖通知顺序,⽬标对象也不知道通知顺序
- 常使⽤在基于事件的ui框架中,也是MVC的组成部分
- 常使⽤在分布式系统中,actor框架中
4.本质
触发联动;
#include <vector>
class IDisplay {
public:
virtual void Show(float temperature) = 0;
virtual ~IDisplay() {}
};
class DisplayA : public IDisplay {
public:
virtual void Show(float temperature);
};
class DisplayB : public IDisplay{
public:
virtual void Show(float temperature);
};
class WeatherData {
};
class DataCenter {
public:
void Attach(IDisplay * ob);//加入
void Detach(IDisplay * ob);//删除
void Notify() {
float temper = CalcTemperature();
//通知所有加入的终端
for (auto iter = obs.begin(); iter != obs.end(); iter++) {
(*iter)->Show(temper);
}
}
private:
virtual WeatherData * GetWeatherData();
virtual float CalcTemperature() {
WeatherData * data = GetWeatherData();
// ...
float temper/* = */;
return temper;
}
std::vector<IDisplay*> obs;
};
int main() {
DataCenter *center = new DataCenter;
IDisplay *da = new DisplayA();
IDisplay *db = new DisplayB();
center->Attach(da);
center->Attach(db);
center->Notify();
//-----
center->Detach(db);
center->Notify();
return 0;
}
策略模式
1.定义
定义⼀系列算法,把它们⼀个个封装起来,并且使它们可互相替换。该模式使得算法可独⽴于使⽤
它的客户程序⽽变化。
2.背景
某商场节假⽇有固定促销活动,为了加⼤促销⼒度,现提升国庆节促销活动规格
3.要点
策略模式提供了⼀系列可重用的算法,从⽽可以使得类型在运⾏时⽅便地根据需要在各个算法
之间进⾏切换策略模式消除了条件判断语句,就是在解耦合充分体现了开闭原则,单⼀职责
4.本质
分离算法,选择实现
class Context {
//上下文 代表参数
};
class ProStategy {
public:
virtual double CalcPro(const Context &ctx) = 0;
virtual ~ProStategy();
};
// cpp
class VAC_Spring : public ProStategy {
public:
virtual double CalcPro(const Context &ctx){}
};
// cpp
class VAC_QiXi : public ProStategy {
public:
virtual double CalcPro(const Context &ctx){}
};
class VAC_QiXi1 : public VAC_QiXi {
public:
virtual double CalcPro(const Context &ctx){}
};
// cpp
class VAC_Wuyi : public ProStategy {
public:
virtual double CalcPro(const Context &ctx){}
};
// cpp
class VAC_GuoQing : public ProStategy {
public:
virtual double CalcPro(const Context &ctx){}
};
class VAC_Shengdan : public ProStategy {
public:
virtual double CalcPro(const Context &ctx){}
};
// 稳定的 变化的
class Promotion {
public:
Promotion(ProStategy *sss) : s(sss){}
~Promotion(){}
double CalcPromotion(const Context &ctx){
//统一的步骤xxxx
return s->CalcPro(ctx);
}
private:
ProStategy *s;
};
int main () {
Context ctx;
ProStategy *s = new VAC_QiXi1();
Promotion *p = new Promotion(s);
p->CalcPromotion(ctx);
return 0;
}
责任链模式
1.定义
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。
2.背景
请假流程,1天内需要主程序批准,3天内需要项⽬经理批准,3天以上需要⽼板批准
3.要点
- 解耦请求方和处理方,请求方不知道请求是如何被处理,处理方的组成是由相互独⽴的子处理
构成,子处理流程通过链表的方式连接,子处理请求可以按任意顺序组合 - 责任链请求强调请求最终由⼀个子处理流程处理;通过了各个子处理条件判断
- 责任链扩展就是功能链,功能链强调的是,⼀个请求依次经由功能链中的⼦处理流程处理
- 充分体现了单⼀职责原则;将职责以及职责顺序运行进行抽象,那么职责变化可以任意扩展, 同时职责顺序也可以任意扩展
4.本质
分离职责,动态组合
#include <string>
class Context {
public:
std::string name;
int day;
};
class IHandler {
public:
virtual ~IHandler() {}
void SetNextHandler(IHandler *next) {
next = next;
}
bool Handle(ctx) {
if (CanHandle(ctx)) {
return HandleRequest();
} else if (GetNextHandler()) {
return GetNextHandler()->Handle(ctx);//继续寻找下一个直到可以CanHandle或者没有Next
} else {
// err
}
}
protected:
virtual bool HandleRequest(const Context &ctx) = 0;
virtual bool CanHandle(const Context &ctx) =0;
IHandler * GetNextHandler() {
return next;
}
private:
IHandler *next;
};
class HandleByMainProgram : public IHandler {
protected:
virtual bool HandleRequest(const Context &ctx){
//
}
virtual bool CanHandle() {
//
}
};
class HandleByProjMgr : public IHandler {
protected:
virtual bool HandleRequest(const Context &ctx){
//
}
virtual bool CanHandle() {
//
}
};
class HandleByBoss : public IHandler {
public:
virtual bool HandleRequest(const Context &ctx){
//
}
protected:
virtual bool CanHandle() {
//
}
};
int main () {
IHandler * h1 = new MainProgram();
IHandler * h2 = new HandleByProjMgr();
IHandler * h3 = new HandleByBoss();
h1->SetNextHandler(h2);
h2->SetNextHandler(h3);
Context ctx;
h1->handle(ctx);
return 0;
}
装饰器模式
1.定义
动态地给⼀个对象增加⼀些额外的职责。就增加功能⽽⾔,装饰器模式比生成子类更为灵活
2.背景
普通员工有销售奖⾦,累计奖金,部⻔经理除此之外还有团队奖金;后面可能会添加环比增长奖金
,同时可能针对不同的职位产生不同的奖金组合。
3.要点
- 通过采用组合而非继承的⼿法, 装饰器模式实现了在运⾏时动态扩展对象功能的能力
- 而且可以根据需要扩展多个功能。避免了使⽤继承带来的“灵活性差”和“多子类衍⽣问题”
- 不是解决“多子类衍生的多继承”问题,而是解决“父类在多个方向上的扩展功能”问题
- 装饰器模式把⼀系列复杂的功能分散到每个装饰器当中,⼀般⼀个装饰器只实现⼀个功能,实现服用装饰器的功能
4.本质
动态组合
// 普通员工有销售奖金,累计奖金,部门经理除此之外还有团队奖金;后面可能会添加环比增长奖金,同时可能产生不同的奖金组合;
// 销售奖金 = 当月销售额 * 4%
// 累计奖金 = 总的回款额 * 0.2%
// 部门奖金 = 团队销售额 * 1%
// 环比奖金 = (当月销售额-上月销售额) * 1%
// 销售后面的参数可能会调整
class Context {
public:
bool isMgr;
// User user;
// double groupsale;
};
// 试着从职责出发,将职责抽象出来
class CalcBonus {
public:
CalcBonus(CalcBonus * c = nullptr) {}
virtual double Calc(Context &ctx) {
return 0.0; // 基本工资
}
virtual ~CalcBonus() {}
protected:
CalcBonus* cc;
};
class CalcMonthBonus : public CalcBonus {
public:
CalcMonthBonus(CalcBonus * c) : cc(c) {}
virtual double Calc(Context &ctx) {
double mbonus /*= 计算流程忽略*/;
return mbonus + cc->Calc(ctx);
}
};
class CalcSumBonus : public CalcBonus {
public:
CalcSumBonus(CalcBonus * c) : cc(c) {}
virtual double Calc(Context &ctx) {
double sbonus /*= 计算流程忽略*/;
return sbonus + cc->Calc(ctx);
}
};
class CalcGroupBonus : public CalcBonus {
public:
CalcGroupBonus(CalcBonus * c) : cc(c) {}
virtual double Calc(Context &ctx) {
double gbnonus /*= 计算流程忽略*/;
return gbnonus + cc->Calc(ctx);
}
};
class CalcCycleBonus : public CalcBonus {
public:
CalcGroupBonus(CalcBonus * c) : cc(c) {}
virtual double Calc(Context &ctx) {
double gbnonus /*= 计算流程忽略*/;
return gbnonus + cc->Calc(ctx);
}
};
int main() {
// 1. 普通员工
Context ctx1;
CalcBonus *base = new CalcBonus();
CalcBonus *cb1 = new CalcMonthBonus(base);
CalcBonus *cb2 = new CalcSumBonus(cb1);
cb2->Calc(ctx1);
// 2. 部门经理
Context ctx2;
CalcBonus *cb3 = new CalcGroupBonus(cb2);
cb3->Calc(ctx2);
}
单例模式
1.定义
保证⼀个类仅有⼀个实例,并提供⼀个该实例的全局访问点
3.要点
- 单例类必须自己创建自己的唯一实例。
- 单例类必须给所有其他对象提供这一实例
- 单例类必须给安全有效的释放
4.本质
static 保证只有一份同样的实例
#include <mutex>
class Singleton { // 懒汉模式 lazy load
public:
static Singleton * GetInstance() {
//std::lock_guard<std::mutex> lock(_mutex); // 3.1 切换线程 代价太大
if (_instance == nullptr) {
std::lock_guard<std::mutex> lock(_mutex);
// 3.2 下面_instance = new Singleton(); 可能会出现new 操作带来的cpu指令reorder操作
//1.new 分配内存
//2.Singleton 构造函数初始化
//3.赋值_instance 其中23顺序可能32
if (_instance == nullptr) {
_instance = new Singleton();
atexit(Destructor);
}
}
return _instance;
}
private:
static void Destructor() {
if (nullptr != _instance) {
delete _instance;
_instance = nullptr;
}
}
Singleton(){} //构造
Singleton(const Singleton &cpy){} //拷⻉构造
Singleton& operator=(const Singleton&) {}
static Singleton * _instance;
static std::mutex _mutex;
}
Singleton* Singleton::_instance = nullptr;//静态成员需要初始化
std::mutex Singleton::_mutex; //互斥锁初始化
双检查之所以有问题,是因为CPU指令重排序导致的。 这样一条一句instance_ = new Singleton; 一般人都会认为是构造一个对象并初始化后,然后赋值给instance_ 。 实际上不一定,CPU有可能先构造一个空对象 ,把这个空对象的地址赋值给instance_ , 最后才调用构造函数进行初始化。如果在初始化前,发生线程切换,另一个线程检查instance_地址发现不为null,就会返回一个未初始化的空对象,就会出错
C++ 单例模式 std::call_once实现
释放不方便
class Singleton {
public:
static Singleton* getInstance() {
static std::once_flag onceFlag; // 必须是静态的
std::call_once(onceFlag, [&] {m_instance = new Singleton(); }); // 只会调用一次
return m_instance;
}
void PrintAddress() const {
std::cout << this << std::endl;
}
private:
Singleton() {} //私有构造函数,不允许使用者自己生成对象,但是必须要实现
Singleton(const Singleton& other) = delete;
Singleton& operator = (const Singleton& other) = delete;
private:
static Singleton* m_instance; //静态成员变量
};
Singleton* Singleton::m_instance = nullptr; //静态成员需要先初始化
int main() {
Singleton& s1 = Singleton::GetInstance();
s1.PrintAddress();
return 0;
}
c++11 magic static 特性:如果当变量在初始化的时候,并发同时进⼊声明语句,并发线程将会阻塞等待初始化结束。
class Singleton
{
public:
~Singleton(){}
static Singleton& GetInstance() {
static Singleton instance;
return instance;
}
private:
Singleton(){}
Singleton(const Singleton&) {}
Singleton& operator=(const Singleton&) {}
};
// 继承 Singleton
// g++ Singleton.cpp -o singleton -std=c++11
/*该版本具备所有优点:
1. 利⽤静态局部变量特性,延迟加载;
2. 利⽤静态局部变量特性,系统⾃动回收内存,⾃动调⽤析构函数;
3. 静态局部变量初始化时,没有 new 操作带来的cpu指令reorder操作;
4. c++11 静态局部变量初始化时,具备线程安全;
*/
通过继承+友元 子类可以用直接单例
template<typename T>
class Singleton {
public:
static T& GetInstance() {
static T instance; // 这⾥要初始化DesignPattern,需要调⽤
DesignPattern 构造函数,同时会调⽤⽗类的构造函数。
return instance;
}
protected:
virtual ~Singleton() {}
Singleton() {} // protected修饰构造函数,才能让别⼈继承
Singleton(const Singleton&) {}
Singleton& operator =(const Singleton&) {}
};
class DesignPattern : public Singleton<DesignPattern> {
friend class Singleton<DesignPattern>;
// friend 能让 Singleton<T> 访问到 DesignPattern构造函数
private:
DesignPattern(){}
DesignPattern(const DesignPattern&) {}
DesignPattern& operator=(const DesignPattern&) {}
}
int main(){
DesignPattern::GetInstance(); //使用
return 0;
}
工厂方法模式
1.定义
定义⼀个用于创建对象的接口,让子类决定实例化哪⼀个类
Factory Method使得⼀个类的实例化延迟到子类
2.背景
实现⼀个导出数据的接口,让客户选择数据的导出方式
3.要点
- 解决创建过程比较复杂,希望对外隐藏这些细节
- 比如连接池,线程池
- 隐藏对象真实类型
- 对象创建会有很多参数来决定如何创建
- 创建对象有复杂的依赖关系
4.本质
延迟到⼦类来选择实现
#include <string>
// 实现导出数据的接口, 导出数据的格式包含 xml,json,文本格式txt 后面可能扩展excel格式csv
class IExport {
public:
virtual bool Export(const std::string &data) = 0;
virtual ~IExport(){}
};
class ExportXml : public IExport {
public:
virtual bool Export(const std::string &data) {
return true;
}
};
class ExportJson : public IExport {
public:
virtual bool Export(const std::string &data) {
return true;
}
};
class ExportTxt : public IExport {
public:
virtual bool Export(const std::string &data) {
return true;
}
};
//新增CSV
class ExportCSV : public IExport {
public:
virtual bool Export(const std::string &data) {
return true;
}
};
class IExportFactory {
public:
IExportFactory() {
_export = nullptr;
}
virtual ~IExportFactory() {
if (_export) {
delete _export;
_export = nullptr;
}
}
bool Export(const std::string &data) {
if (_export == nullptr) {
_export = NewExport();
}
return _export->Export(data);
}
protected:
virtual IExport * NewExport(/* ... */) = 0;
private:
IExport* _export;
};
class ExportXmlFactory : public IExportFactory {
protected:
virtual IExport * NewExport(/* ... */) {
// 可能有其它操作,或者许多参数
IExport * temp = new ExportXml();
// 可能之后有什么操作
return temp;
}
};
class ExportJsonFactory : public IExportFactory {
protected:
virtual IExport * NewExport(/* ... */) {
// 可能有其它操作,或者许多参数
IExport * temp = new ExportJson;
// 可能之后有什么操作
return temp;
}
};
class ExportTxtFactory : public IExportFactory {
protected:
IExport * NewExport(/* ... */) {
// 可能有其它操作,或者许多参数
IExport * temp = new ExportTxt;
// 可能之后有什么操作
return temp;
}
};
//新增工厂
class ExportCSVFactory : public IExportFactory {
protected:
virtual IExport * NewExport(/* ... */) {
// 可能有其它操作,或者许多参数
IExport * temp = new ExportCSV;
// 可能之后有什么操作
return temp;
}
};
int main () {
IExportFactory *factory = new ExportTxtFactory();
factory->Export("hello world");
return 0;
}
抽象工厂模式
1.定义
提供⼀个接口,让该接口负责创建⼀系列“相关或者相互依赖的对象”,无需指定它们具体的类
2.背景
实现⼀个拥有导出导入数据的接口,让客户选择数据的导出导入方式
3.要点
4.本质
//抽象工厂模式 在工厂模式上加上 每个工厂除了会生产多种商品 如:这还有导入操作
#include <string>
// 实现导出数据的接口, 导出数据的格式包含 xml,json,文本格式txt 后面可能扩展excel格式csv
class IExport {
public:
virtual bool Export(const std::string &data) = 0;
virtual ~IExport(){}
};
class ExportXml : public IExport {
public:
virtual bool Export(const std::string &data) {
return true;
}
};
class ExportJson : public IExport {
public:
virtual bool Export(const std::string &data) {
return true;
}
};
class ExportTxt : public IExport {
public:
virtual bool Export(const std::string &data) {
return true;
}
};
class ExportCSV : public IExport {
public:
virtual bool Export(const std::string &data) {
return true;
}
};
//每个工厂新增导入操作
class IImport {
public:
virtual bool Import(const std::string &data) = 0;
virtual ~IImport(){}
};
class ImportXml : public IImport {
public:
virtual bool Import(const std::string &data) {
return true;
}
};
class ImportJson : public IImport {
public:
virtual bool Import(const std::string &data) {
return true;
}
};
class ImportTxt : public IImport {
public:
virtual bool Import(const std::string &data) {
return true;
}
};
class ImportCSV : public IImport {
public:
virtual bool Import(const std::string &data) {
// ....
return true;
}
};
class IDataApiFactory {
public:
IDataApiFactory() {
_export = nullptr;
_import = nullptr;
}
virtual ~IDataApiFactory() {
if (_export) {
delete _export;
_export = nullptr;
}
if (_import) {
delete _import;
_import = nullptr;
}
}
bool Export(const std::string &data) {
if (_export == nullptr) {
_export = NewExport();
}
return _export->Export(data);
}
bool Import(const std::string &data) {
if (_import == nullptr) {
_import = NewImport();
}
return _import->Import(data);
}
protected:
virtual IExport * NewExport(/* ... */) = 0;
virtual IImport * NewImport(/* ... */) = 0;
private:
IExport *_export;
IImport *_import;
};
class XmlApiFactory : public IDataApiFactory {
protected:
virtual IExport * NewExport(/* ... */) {
// 可能有其它操作,或者许多参数
IExport * temp = new ExportXml;
// 可能之后有什么操作
return temp;
}
virtual IImport * NewImport(/* ... */) {
// 可能有其它操作,或者许多参数
IImport * temp = new ImportXml;
// 可能之后有什么操作
return temp;
}
};
class JsonApiFactory : public IDataApiFactory {
protected:
virtual IExport * NewExport(/* ... */) {
// 可能有其它操作,或者许多参数
IExport * temp = new ExportJson;
// 可能之后有什么操作
return temp;
}
virtual IImport * NewImport(/* ... */) {
// 可能有其它操作,或者许多参数
IImport * temp = new ImportJson;
// 可能之后有什么操作
return temp;
}
};
class TxtApiFactory : public IDataApiFactory {
protected:
virtual IExport * NewExport(/* ... */) {
// 可能有其它操作,或者许多参数
IExport * temp = new ExportTxt;
// 可能之后有什么操作
return temp;
}
virtual IImport * NewImport(/* ... */) {
// 可能有其它操作,或者许多参数
IImport * temp = new ImportTxt;
// 可能之后有什么操作
return temp;
}
};
class CSVApiFactory : public IDataApiFactory {
protected:
virtual IExport * NewExport(/* ... */) {
// 可能有其它操作,或者许多参数
IExport * temp = new ExportCSV;
// 可能之后有什么操作
return temp;
}
virtual IImport * NewImport(/* ... */) {
// 可能有其它操作,或者许多参数
IImport * temp = new ImportCSV;
// 可能之后有什么操作
return temp;
}
};
int main () {
IDataApiFactory *factory = new CSVApiFactory();
factory->Import("hello world");
factory->Export("hello world");
return 0;
}
适配器模式
1.定义
将⼀个类的接口转换成客户希望的另⼀个接口。Adapter模式使得原本由于接口不兼容而不能⼀起
工作的那些类可以⼀起工作
2.背景
日志系统,原来是通过写磁盘的方式进行存储,后来因为查询不便,需要额外添加往数据库写日志
的功能(写文件和数据库并存);
3.要点
- 原来的接口是稳定的,新的外来的需求是变化的,那么可以通过继承原来的接口,让原来的接
口继续保持稳定,在子类通过组合的方式来扩展功能;
4.本质
转换匹配,复⽤功能
#include <string>
#include <vector>
using namespace std;
class LogSys {
public:
LogSys() {}
void WriteLog(const vector<string> &) {
// ... 日志id 时间戳 服务器id 具体日志内容 roleid
}
vector<string>& ReadLog() {
// ...
vector<string> data /* = ...*/;
return data;
}
};
class DB; // 面向接口编程 而不是具体类 强依赖 耦合性高 mysql mongo
class LogSysEx : public LogSys {
public:
LogSysEx(DB *db) : _db(db) {}
void AddLog(const vector<string> &data) {
LogSys::WriteLog(data);
/*
这里调用 _db 的方法将 data 数据存储到数据库
*/
}
void DelLog(const int logid) {
vector<string>& data = LogSys::ReadLog();
// 从 vector<string> 中删除 logid的日志
LogSys::WriteLog(data);
// 调用 _db 的方法将 logid的日志删除
}
void UpdateLog(const int logid, const string &udt) {
vector<string>& data = LogSys::ReadLog();
// 从 vector<string> 中更新 logid的日志 udt
LogSys::WriteLog(data);
// 调用 _db 的方法将 logid的日志更改
}
string& LocateLog(const int logid) {
vector<string>& data = LogSys::ReadLog();
string log1 /* = from log file*/;
string log2 /* = from db */;
string temp = log1 + ";" + log2;
return temp;
}
private:
DB* _db;
};
代理模式
1.定义
为其他对象提供⼀种代理以控制对这对象的访问
2.背景
在有些系统中,为了某些对象的纯粹性,只进行了功能相关封装(稳定点)后期添加了其他功能,需要对该对象进行额外操作(变化点),为了隔离变化点(也就是不直接在稳定点进行修改,这样会让稳定点也变得不稳定),可以抽象⼀层代理层;
3.要点
- 远程代理(隐藏⼀个对象存在不同的地址空间的事实),虚代理(延迟加载lazyload),保护
代理(在代理前后做额外操作,权限管理,引用计数等) - 在分布式系统中,actor模型(skynet)等常用的设计模式
4.本质
控制对象访问
class ISubject {
public:
virtual void Handle() = 0;
virtual ~ISubject() {}
};
// 该类在当前进程,也可能在其他进程当中
class RealSubject : public ISubject {
public:
virtual void Handle() {
// 只完成功能相关的操作,不做其他模块的判断
}
};
// 在当前进程当中 只会在某个模块中使用
class Proxy1 : public ISubject {
public:
Proxy1(ISubject *subject) : _subject(subject) {}
virtual void Handle() {
// 在访问 RealSubject 之前做一些处理
//if (不满足条件)
// return;
_subject->Handle();
count++;
// 在访问 RealSubject 之后做一些处理
}
private:
ISubject* _subject;
static int count;
};
int Proxy1::count = 0;
// 在分布式系统当中 skynet actor
class Proxy2 : public ISubject {
public:
virtual void Handle() {
// 在访问 RealSubject 之前做一些处理
// 发送到数据到远端 网络处理 同步非阻塞 ntyco c协程
//IResult * val = rpc->call("RealSubject", "Handle");
// 在访问 RealSubject 之后做一些处理
}
private:
/*void callback(IResult * val) {
// 在访问 RealSubject 之后做一些处理
}*/
};