Back
Featured image of post 设计模式

设计模式

基础知识

设计模式

首先先上所有设计模式的原则,这些原则贯彻于每一种设计模式中,是各种设计模式的根

原则

  • 依赖倒置原则

    • 高层模块不应该依赖低层模块,⼆者都应该依赖抽象。
    • 抽象不应该依赖具体实现,具体实现应该依赖于抽象。
  • 开放封闭原则

    • ⼀个类应该对扩展开放,对修改关闭。
  • 面向接口编程

    • 不将变量类型声明为某个特定的具体类,而是声明为某个接⼝。
    • 客户程序无需获知对象的具体类型,只需要知道对象所具有的接口。
    • 减少系统中各部分的依赖关系,从而实现“高内聚、松耦合”的类型设计⽅案。
  • 封装变化点

    • 将稳定点和变化点分离,扩展修改变化点;让稳定点与变化点的实现层次分离。
  • 单一职责原则

    • ⼀个类应该仅有⼀个引起它变化的原因。
  • 里氏替换原则

    • 子类型必须能够替换掉它的父类型;主要出现在⼦类覆盖父类实现,原来使用父亲类型的程序可能出现错误;覆盖了父类方法却没实现父类方法的职责;
  • 接口隔离原则

    • 不应该强迫客户依赖于他们不用的方法。
    • ⼀般用于处理⼀个类拥有比较多的接口,而这些接口涉及到很多职责
  • 对象组合优于类继承

    • 继承耦合度⾼,组合耦合度低

模板模式

首先,先提下使用到的原则:

  1. 依赖倒置原则
  2. 单一职责原则
  3. 接口隔离原则

定义: 定义⼀个操作中的算法的骨架 ,⽽将⼀些步骤延迟到子类中。 Template Method使得子类可以不 改变⼀个算法的结构即可重定义该算法的某些特定步骤。

直接上代码,代码分两部分,第一部分为未使用模板模式时的代码,第二部分为使用模板模式的代码:

未使用模板模式:


#include <iostream>
using namespace std;

class ZooShow {
public:
    void Show0(){
        cout << "show0" << endl;
    }
    void Show2(){
        cout << "show2" << endl;
    }
};

class ZooShowEx {
public:
    void Show1(){
        cout << "show1" << endl;
    }
    void Show3(){
        cout << "show3" << endl;
    }
};

int main () {
    ZooShow *zs = new ZooShow;
    ZooShowEx *zs1 = new ZooShowEx;
    // 流程是固定的(稳定点),应该抽象出来;另外子流程是不应该暴露给客户,违反了接口隔离原则;
    zs->Show0();
    zs1->Show1();
    zs->Show2();
    zs1->Show3();
    return 0;
}

可以看到主函数中流程一览无余,应该由框架提供接口给客户调用一下走完整个流程

使用模板模式:


#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();
    return 0;
}


使用虚函数,根据不同子类做不同扩展,且流程封装在show内部,满足了接口隔离和依赖倒置原则,主类只负责展示流程,流程的具体细节由子类自己实现,职责单一

观察者模式

定义: 定义对象间的⼀种⼀对多(变化)的依赖关系,以便当⼀个对象(Subject)的状态发生改变时,所有 依赖于它的对象都得到通知并自动更新。

未使用观察者模式:



class DisplayA {
public:
    void Show(float temperature);
};

class DisplayB {
public:
    void Show(float temperature);
};

class WeatherData {
};

class DataCenter {
public:
    float CalcTemperature() {
        WeatherData * data = GetWeatherData();
        // ...
        float temper/* = */;
        return temper;
    }
private:
    WeatherData * GetWeatherData(); // 不同的方式
};

int main() {
    DataCenter *center = new DataCenter;
    DisplayA *da = new DisplayA;
    DisplayB *db = new DisplayB;
    float temper = center->CalcTemperature();
    da->Show(temper);
    db->Show(temper);
    return 0;
}
// 终端变化(增加和删除)   数据中心 不应该受终端变化的影响


使用观察者模式:


#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;
}

可以看到我们将接口统一到datacenter(气象中心)里,由气象中心来对外开放功能,同样的原材料(地点)也要交给我们设置的中心来处理,相当于我们买房需要经过房产中介,卖方也需要经过房产中介,我们都基于房产中介的原则来做事,这就是依赖倒置原则。同时,也符合面向接口编程原则。

责任链模式

定义: 使多个对象都有机会处理请求,从⽽避免请求的发送者和接收者之间的耦合关系。将这些对象连成⼀条链,并沿着这条链传递请求,直到有⼀个对象处理它为⽌。

未使用责任链模式:


#include <string>

class Context {
public:
    std::string name;
    int day;
};

class LeaveRequest {
public:
    // 随着判断的增加,LeaveRequest类变得不稳定
    bool HandleRequest(const Context &ctx) {
        if (ctx.day <= 3)
            HandleByMainProgram(ctx);
        else if (ctx.day <= 10)
            HandleByProjMgr(ctx);
        else
            HandleByBoss(ctx);
    }

private:
    bool HandleByMainProgram(const Context &ctx) {
        
    }
    bool HandleByProjMgr(const Context &ctx) {
        
    }
    bool HandleByBoss(const Context &ctx) {

    }
};

可以看到我们每次都需要在handlerequest里加一个判断,作为中间的稳定框架不应被用户修改,我们需要找出稳定的部分独立出来。不稳定的部分可以看到是判断条件和对应的处理方式,我们需要将这一部分通过子类重载,使用虚函数即可。

使用责任链模式:


#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()->HandleRequest(ctx);
        } 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);

    Context ctx;
    h1->handle(ctx);
    return 0;
}

CanHandle 是判断条件 HandleRequest 就是对应的处理方式了

我们只需要重载这两个函数就可以

责任链顾名思义就是一条链,所以需要在对象中保存链中下一个对象的指针,IHandler *next;

可以看到:依赖倒置原则、开放封闭原则比较明显

装饰器模式

定义: 使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成⼀条链,并沿着这条链传递请求,直到有一个对象处理它为止。

未使用装饰器模式:


// 普通员工有销售奖金,累计奖金,部门经理除此之外还有团队奖金;后面可能会添加环比增长奖金,同时可能产生不同的奖金组合;
// 销售奖金 = 当月销售额 * 4%
// 累计奖金 = 总的回款额 * 0.2%
// 部门奖金 = 团队销售额 * 1%
// 环比奖金 = (当月销售额-上月销售额) * 1%
// 销售后面的参数可能会调整
class Context {
public:
    bool isMgr;
    // User user;
    // double groupsale;
};

class Bonus {
public:
    double CalcBonus(Context &ctx) {
        double bonus = 0.0;
        bonus += CalcMonthBonus(ctx);
        bonus += CalcSumBonus(ctx);
        if (ctx.isMgr) {
            bonus += CalcGroupBonus(ctx);
        }
        return bonus;
    }
private:
    double CalcMonthBonus(Context &ctx) {
        double bonus/* = */;
        return bonus;
    }
    double CalcSumBonus(Context &ctx) {
        double bonus/* = */;
        return bonus;
    }
    double CalcGroupBonus(Context &ctx) {
        double bonus/* = */;
        return bonus;
    }
};

int main() {
    Context ctx;
    // 设置 ctx
    Bonus *bonus = new Bonus;
    bonus->CalcBonus(ctx);
}


使用装饰器模式:


// 普通员工有销售奖金,累计奖金,部门经理除此之外还有团队奖金;后面可能会添加环比增长奖金,同时可能产生不同的奖金组合;
// 销售奖金 = 当月销售额 * 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

简单的单例模式


// 内存栈区
// 内存堆区
// 常数区
// 静态区 系统释放
// ⼆进制代码区
class Singleton {

public:
 static Singleton * GetInstance() {
 if (_instance == nullptr) {
 _instance = new Singleton();
 }
 return _instance;
 }
private:
 Singleton(){}//构造
 Singleton(const Singleton &clone){} //拷⻉构造
 Singleton& operator=(const Singleton&) {}
 static Singleton * _instance;
}
Singleton* Singleton::_instance = nullptr;//静态成员需要初始化

问题在于我们要销毁的时候没有对应的处理函数

单例模式版本2


class Singleton {
public:
 static Singleton * GetInstance() {
 if (_instance == nullptr) {
 _instance = new Singleton();
 atexit(Destructor);
 }
 return _instance;
 }
 ~Singleton() {}
private:
 static void Destructor() {
 if (nullptr != _instance) {
 delete _instance;
 _instance = nullptr;
 }
 }
 Singleton();//构造
 Singleton(const Singleton &cpy); //拷⻉构造
 Singleton& operator=(const Singleton&) {}
 static Singleton * _instance;
}
Singleton* Singleton::_instance = nullptr;//静态成员需要初始化

加入销毁(atexit在函数退出时调用)但是存在线程安全问题,单例模式创建的对象需要全局唯一,多线程下如果同时进入创建函数内可能创建多个对象。

单例模式版本3

使用互斥锁保证线程安全


#include <mutex>
class Singleton { // 懒汉模式  lazy load  get的时候才创建
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
 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; //互斥锁初始化

这一版本还存在问题,首先在3.1处加锁的话锁的粒度太大,线程切换调用的开销巨大,试想即使另一个线程拿到锁已经初始化完成,此时有线程要来取得单例对象,可是锁已经被拿走,即使对象已经创建还是需要将这个线程挂起,开销巨大。

第二个问题,如果我们将锁加到3.2位置,多线程环境下可能有线程取到空值的单例对象,这是因为有线程已经拿到锁开始创建对象,但是从底层汇编的角度来看,对象的构建分三步:1、取得内存2、构造3、赋值给指针。但是汇编器可能进行优化(reorder,指令重排,为什么会重排呢,因为指令操作重排后可能具有更好的性能,对于指令来说可能提高分支预测的成功率等等),使得对象构建的顺序由123变成132,如果13进行完毕,代表其他线程会认为对象已经构建完成,因为指针指向了一块地址,但其实这一块内存还未进行构造就是未知的,其他线程就会取到未知的数据,产生错误。

综上,在3.1处加锁粒度太大浪费计算资源,在3.2处加锁还是无法保证线程安全。

那我们该怎么办呢? 就有了版本4:

单例模式版本4

使用编译屏障

一般来讲屏障是汇编器带有的功能,我们也可以内联汇编来实现但是比较复杂且可读性不高。

汇编:(定义一个显式屏障)

#define barrier() __asm__ __volatile__("": : :"memory")

当然显式屏障的作用远不止关闭reorder,还有:

告诉compiler内存中的值已经改变,之前对内存的缓存(缓存到寄存器)都需要抛弃,barrier()之后的内存操作需要重新从内存load,而不能使用之前寄存器缓存的值。并且可以防止compiler优化barrier()前后的内存访问顺序

可以看到,memory还具备刷新缓存的功能,所以不仅可以防止reorder优化,还可以防止其他指令优化,比如真值替换等。至于多核心的L1,L2缓存,MESI协议下的多核缓存一致性已经保持的很好了

c++11 中的编译屏障:


#include <mutex>
#include <atomic>
class Singleton {
public:
 static Singleton * GetInstance() {
 Singleton* tmp = _instance.load(std::memory_order_relaxed);
 std::atomic_thread_fence(std::memory_order_acquire);//获取内存屏障
 if (tmp == nullptr) {
 std::lock_guard<std::mutex> lock(_mutex);
 tmp = _instance.load(std::memory_order_relaxed);
 if (tmp == nullptr) {
 tmp = new Singleton;
 std::atomic_thread_fence(std::memory_order_release);//释放内存屏障
 _instance.store(tmp, std::memory_order_relaxed);
 atexit(Destructor);
 }
 }
 return tmp;
 }

private:
 static void Destructor() {
 Singleton* tmp = _instance.load(std::memory_order_relaxed);
 if (nullptr != tmp) {
 delete tmp;
 }
 }
 Singleton(){}
 Singleton(const Singleton&) {}
 Singleton& operator=(const Singleton&) {}
 static std::atomic<Singleton*> _instance;
 static std::mutex _mutex;
};
std::atomic<Singleton*> Singleton::_instance;//静态成员需要初始化
std::mutex Singleton::_mutex; //互斥锁初始化
// g++ Singleton.cpp -o singleton -std=c++11

添加内存屏障后,cpu不会reorder,因为这块内存只能在这里操作,优化器并不会进行优化,只有一个线程可以操作这一块内存,也就不存在其他线程来取用的时候取到未知值的情况了,因为其他线程都无法取用,只能等待。同时这种操作下粒度也是很小的,并不影响大部分情况下的其他线程获得未创建成功的信息。

单例模式版本5


// 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
/*该版本具备 版本5 所有优点:
1. 利⽤静态局部变量特性,延迟加载;
2. 利⽤静态局部变量特性,系统⾃动回收内存,⾃动调⽤析构函数;
3. 静态局部变量初始化时,没有 new 操作带来的cpu指令reorder操作;
4. c++11 静态局部变量初始化时,具备线程安全;
*/

这一版本优点很多,但是当其他类需要用到这个单例的时候,我们想到继承,可是构造函数为private,显然无法继承,因为继承后不能使用构造函数。

这里我们只需要在单例中添加友元类就可以。 可是扩展性极差,因为可能需要写特别多的friend,所以用版本6:

单例模式版本6

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&) {}
};

显然,使用protected来标记构造函数就可以避免继承问题了。

工厂方法模式

定义: 定义⼀个用于创建对象的接口,让子类决定实例化哪⼀个类。Factory Method使得⼀个类的实例化延迟到子类。

未使用工厂方法模式


#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;
    }
};

// =====1
int main() {
    std::string choose/* = */;
    if (choose == "txt") {
        IExport *e = new ExportTxt();
        e->Export("hello world");
    } else if (choose == "json") {
        IExport *e = new ExportJson();
        e->Export("hello world");
    } else if (choose == "xml") {
        IExport *e = new ExportXml();
        e->Export("hello world");
    }
}


使用工厂方法模式


#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 IExportFactory {
public:
    virtual IExport * NewExport(/* ... */) = 0;
};

class ExportXmlFactory : public IExportFactory {
public:
    IExport * NewExport(/* ... */) {
        // 可能有其它操作,或者许多参数
        IExport * temp = new ExportXml;
        // 可能之后有什么操作
        return temp;
    }
};

class ExportJsonFactory : public IExportFactory {
public:
    IExport * NewExport(/* ... */) {
        // 可能有其它操作,或者许多参数
        IExport * temp = new ExportJson;
        // 可能之后有什么操作
        return temp;
    }
};

class ExportTxtFactory : public IExportFactory {
public:
    IExport * NewExport(/* ... */) {
        // 可能有其它操作,或者许多参数
        IExport * temp = new ExportTxt;
        // 可能之后有什么操作
        return temp;
    }
};

class ExportData {
public:
    ExportData(IExportFactory *factory) : _factory(factory) {}
    ~ExportData() {
        if (_factory) {
            delete _factory;
            _factory = nullptr;
        }
    }
    bool Export(const std::string &data) {
        IExport * e = _factory->NewExport();
        e->Export(data);
    }
private:
    IExportFactory *_factory;
};

int main() {
    ExportData ed(new ExportTxtFactory);
    ed.Export("hello world");
    return 0;
} 

抽象工厂

定义: 提供一个接口,让该接口负责创建一系列“相关或者相互依赖的对象”,无需指定它们具体的类。

使用抽象工厂方法模式


#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;
}

适配器模式

定义: 将⼀个类的接口转换成客户希望的另一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以⼀起工作。

使用适配器模式

#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;
};


代理模式

定义: 为其他对象提供一种代理以控制对这对象的访问。

使用代理模式



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 之后做一些处理
    }*/
};

策略模式

定义: 定义一系列算法,把它们一个个封装起来,并且使它们可互相替换。该模式使得算法可独立于使用它的客户程序而变化。

未使用策略模式


enum VacationEnum {
	VAC_Spring,
    VAC_QiXi,
	VAC_Wuyi,
	VAC_GuoQing,
    //VAC_ShengDan,
};

// 稳定的  变化的
class Promotion {
    VacationEnum vac;
public:
    double CalcPromotion(){
        if (vac == VAC_Spring){
            // 春节
        }
        else if (vac == VAC_QiXi){
            // 七夕
        }
        else if (vac == VAC_Wuyi){
            // 五一
        }
		else if (vac == VAC_GuoQing){
			// 国庆
		}
     }
    
};

使用策略模式

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){
        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;
}



comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy