常用设计模式这样学

简介: 常用设计模式这样学

  如果你也跟笔者一样,初学模式的时候,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种设计模式都是最常用,也最好理解的。此外设计模式的八个原则,什么依赖倒置原则,里式替换原则也要熟记,面试很可能会让背。当然熟悉这几种设计模式也更能理解那些原则。以上。

相关文章
|
2月前
|
设计模式 安全 Java
24种设计模式详解(下)
24种设计模式详解(下)
37 0
|
2月前
|
设计模式 Java 程序员
23种设计模式详解(上)
23种设计模式详解(上)
35 0
|
设计模式 存储 安全
【设计模式】五种创建者模式
创建者模式 创建型模式的主要关注点是“怎样创建对象?”,它的主要特点是“将对象的创建与使用分离”。 这样可以降低系统的耦合度,使用者不需要关注对象的创建细节。 创建型模式分为: 单例模式 工厂方法模式 抽象工程模式 原型模式 建造者模式 单例设计模式 单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。 这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。 单例模式的结构 单例模式的
73 0
|
设计模式
设计模式
设计模式是指在软件设计中,经过总结和提炼出来的、被广泛认可的、可重用的解决特定问题的设计思路和方法。设计模式可以帮助软件设计师更好地解决一些常见的设计问题,提高代码的可维护性、可扩展性和可重用性。
82 1
|
7月前
|
设计模式 自动驾驶 NoSQL
设计模式总结(一)
设计模式总结(一)
|
设计模式
设计模式——里氏替换
设计模式——里氏替换
|
设计模式 存储 NoSQL
为什么我们需要设计模式?
设计模式解决什么问题设计模式一直被认为是一门难以学习的课程。究其原因是因为我们不清楚设计模式在解决哪些问题方面发挥作用。简言之,设计是为了实现特定的目标,基于不断更新的局部知识来获得解决方案的过程。我们通常熟悉的范式是在几乎掌握所有知识的情况下解决问题,例如解数学题、物理题等。然而,在软件编程过程中,我们掌握的知识往往不充分,而且会不断更新,因此需要我们关注有哪些知识,没有哪些知识,可以获取哪些知
9338 1
|
设计模式 存储 缓存
|
设计模式
设计模式之其他设计模式(7-1)
设计模式之其他设计模式(7-1)
140 0
|
设计模式
设计模式总结与对比
设计模式总结与对比
169 0
设计模式总结与对比