常用设计模式这样学

  如果你也跟笔者一样,初学模式的时候,20多种设计模式让你眼花缭乱,不知道如何下手,可以试试这里提出的方法。无论面试,或者重构代码的时候,都能知道相应设计模式的使用场景。简而言之,就是案例记忆法。通过记住每个设计模式的典型案例,达到对常用的几个设计模式了然于胸的境界。这里列举常用的5种设计模式,一般面试和简单的使用场景均能覆盖。如果希望掌握全部的设计模式,可以用这种方法总结剩下的模式。

模板方法模式 – 动物园表演流程

  模板模式的关键词是,流程固定。这个设计模式适合流程固定的情况。比如动物园的表演流程有5个步骤(稳定点),其中某些步骤的表演内容需要改变(变化点)。

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

// 反向调用,框架是固定的,仅可复写不同的show()子函数
int main () {
    ZooShow *zs1 = new ZooShowEx1;
    ZooShow *zs2 = new ZooShowEx2;
    zs1->Show();
    return 0;
}

观察者模式 – 数据中心显示

  观察者模式,或者称之为发布/订阅模式,关键词是组装(attach)和拆卸(detach)。比如数据中心需要发送同一种数据(稳定点)到不同的显示终端显示,可以增加新的显示终端,可以删除旧的显示终端(变化点)。

// 不同的显示终端定义统一接口
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;
}

策略模式 – 节日促销策略

  策略模式的关键词是策略批量化。比如不同的节日做不同的促销活动。策略批量化就是每一种节日有一份自己的促销策略,用节日策略去构造促销对象。做促销本身是稳定的(即根据不同的节日有对应的促销活动),节日是变化的(可以有不同类型的节日),具体实现促销的方式也是变化的。

class ProStategy {
public:
    virtual double CalcPro(const Context &ctx) = 0;
    virtual ~ProStategy();
};
//策略批量化:
// 1.cpp
class VAC_Spring : public ProStategy {
public:
    virtual double CalcPro(const Context &ctx){}
};
// 2.cpp
class VAC_Wuyi : 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;
}

责任链模式 – 请假流程

  责任链模式用一句话替代,仅链条中的某一环有责任处理。比如请假流程,1天内是主管批准,3天内需要项目经理批准,3天以上老板批准。

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

装饰器模式 – 公司奖金

  装饰器模式的关键词是,一种功能作为一种装饰器,动态组合。对应的案例就是公司奖金,普通员工有销售奖金,累计奖金等。部门经理还有团队奖金。可能还会增加新人奖,老人奖,年度奖等等。

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

  本来准备将工厂方法,抽象工厂,适配器,代理模式一起整理出来,但没有发现特别适合的案例。目前整理的这5种设计模式都是最常用,也最好理解的。此外设计模式的八个原则,什么依赖倒置原则,里式替换原则也要熟记,面试很可能会让背。当然熟悉这几种设计模式也更能理解那些原则。以上。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值