c中的设计模式你知多少

laokugonggao
发布于 2020-9-3 18:08
浏览
0收藏

我最近研究了设计模式,发现尽管为面向对象的语言提出了设计模式,但大多数似乎都是基于java给出的示例。 C ++的例子很少。 观看了李建中先生的GOF设计模式视频后,我回顾了各种材料并结合自己的实践总结了以下内容,希望对大家有所帮助。

 

 

设计模式简介

设计模式是主要针对面向对象语言提出的一种设计思想,主要是提高代码可复用性,抵御变化,尽量将变化所带来的影响降到最低。

 

面向对象特点

封装:隐藏内部实现
继承:复用现有的代码
多态:改写对象的行为

面向对象设计原则

依赖倒置原则:针对接口编程,依赖于抽象而不依赖于具体,抽象(稳定)不应依赖于实现细节(变化),实现细节应该依赖于抽象,因为稳定态如果依赖于变化态则会变成不稳定态。
开放封闭原则:对扩展开放,对修改关闭,业务需求是不断变化的,当程序需要扩展的时候,不要去修改原来的代码,而要灵活使用抽象和继承,增加程序的扩展性,使易于维护和升级,类、模块、函数等都是可以扩展的,但是不可修改。
单一职责原则:一个类只做一件事,一个类应该仅有一个引起它变化的原因,并且变化的方向隐含着类的责任。
里氏替换原则:子类必须能够替换父类,任何引用基类的地方必须能透明的使用其子类的对象,开放关闭原则的具体实现手段之一。
接口隔离原则:接口最小化且完备,尽量少public来减少对外交互,只把外部需要的方法暴露出来。
最少知道原则:一个实体应该尽可能少的与其他实体发生相互作用。
将变化的点进行封装,做好分界,保持一侧变化,一侧稳定,调用侧永远稳定,被调用测内部可以变化。
优先使用组合而非继承,继承为白箱操作,而组合为黑箱,继承某种程度上破坏了封装性,而且父类与子类之间耦合度比较高。
针对接口编程,而非针对实现编程,强调接口标准化。

小总结: 没有一步到位的设计模式,刚开始编程时不要把太多精力放到设计模式上,需求总是变化的,刚开始着重于实现,一般敏捷开发后为了应对变化重构再决定采取合适的设计模式。

 

模板方法

父类定义算法的骨架,而将一些步骤延迟到子类去实现,使得子类可以复用骨架,并附加特性,以开发框架举例,框架开发人员把框架调用流程定好,而将某些具体的步骤作为虚函数留给子类去重写,话不多说,上代码。

#ifndef GAME
#define GAME

#include <iostream>

class Game {
   public:
    Game() {}

    virtual ~Game() {}

    void Run() {
        InitGame();
        StartGame();
        StopGame();
    }

   protected:
    virtual void StartGame() { std::cout << "step 2: start game" << std::endl; }

   private:
    void InitGame() { std::cout << "step 1: init game" << std::endl; }
    void StopGame() { std::cout << "step 3: stop game" << std::endl; }
};

#endif
#include "game.h"

class BasketBall : public Game {
    void StartGame() override { std::cout << "start basketball game" << std::endl; }
};
#include "game.h"

class SocketBall : public Game {
    void StartGame() override { std::cout << "start socketball game" << std::endl; }
};
#include "basketball.h"
#include "socketball.h"

int main() {
    Game game = new BasketBall();
    game->Run();
    delete game;
    Game game2 = new SocketBall();
    game2->Run();
    delete game2;
    return 0;
}
g++ test.cc -std=c++11 && ./a.out
输出:
step 1: init game
start basketball game
step 3: stop game
step 1: init game
start socketball game
step 3: stop game

代码很简单,体现的是思想,游戏包含三个步骤,初始化游戏,开始游戏,停止游戏,初始化游戏和停止游戏步骤比较统一,由父类Game定义好,而开始游戏是第二个步骤,可以有打篮球和踢足球,将来也可以有羽毛球,乒乓球等等,每增加一项运动,都可以从Game父类中继承后重写开始游戏这个函数,达到不同的功能,符合模板方法的特性,即如何在确定稳定结构前提下,应对子步骤需求的变化。

 

策略模式

 

定义一系列的算法,将它们一个个封装,使得他们可以相互替换,一般为了解决多个if-else带来的复杂性,在多种算法相似的情况下,通过策略模式可减少if-else带来的复杂性和难以维护性,一般在项目中发现多个if-else并且预感将来还会在此增加if-else分支,那基本上就需要使用策略模式。 

先举一个不使用策略模式的例子,拿计算来说,下面代码定义了加法操作和减法操作,以后如果需要增加乘法除法等计算,那就需要在枚举里添加新类型,并且增加if-else分支,这违反了开放关闭原则。

 

enum class CalOperation {
    add,
    sub
};

int NoStragegy(CalOperation ope) {
    if (ope == CalOperation::add) {
        std::cout << "this is add operation" << std::endl;
    } else if (ope == CalOperation::sub) {
        std::cout << "this is sub operation" << std::endl;
    } // 如何将来需要增加乘法或者除法或者其它运算,还需要增加if-else
    return 0;
}

下例为使用策略模式,定义一个基类Calculation,包含虚函数operation()。

#ifndef CALCULATION
#define CALCULATION

#include <iostream>

class Calculation {
   public:
    Calculation() {}

    virtual ~Calculation() {}

    virtual void operation() { std::cout << "base operation" << std::endl; }
};

#endif

观察者模式

 

定义对象间的一对多关系,当一个对象状态发生改变的时候,其它依赖于它的对象都会得到广播通知并进行自定义动作,通过面向对象技术的多态技术,可以降低这种依赖关系,降低耦合度,上代码.

#ifndef OBSERVER
#define OBSERVER

#include <iostream>

class ObserverBase {
   public:
    ObserverBase() {}
    virtual ~ObserverBase() {}

    virtual void Update() {}
};

#endif
#ifndef _OBSERVERFIRSTCHILD
#define OBSERVERFIRSTCHILD_

#include "observer.h"

class ObserverFirstChild : public ObserverBase {
    void Update() override {
        std::cout << "first child receive notify" << std::endl;
    }
};

#endif

通过观察者模式可以灵活的控制依赖的对象,动态的增加和删除需要得到通知的对象。

 

装饰器模式

动态的给一个对象添加一些额外的职责,扩展一个类的功能,就增加功能来说,使用装饰器模式比单纯的继承子类更加灵活,不一定非要疯狂使用继承方式。 

举个例子,有游戏这个大类,扩展这个类的功能,有打篮球,踢足球,玩lol,玩卡丁车,可以分别定义继承游戏基类的四个子类,但是如果想组合这几个功能呢,一个对象既会打篮球又会玩卡丁车,既会打篮球又会玩lol,再定义几个类继承游戏基类显然不是好的做法,装饰器模式可以很好的解决这个问题,上代码:

首先定义一个Game基类

#ifndef GAME
#define GAME

#include <iostream>

class Game {
   public:
    Game() {}

    virtual ~Game() {}

    virtual void Skill() { std::cout << "game skill" << std::endl; }
};

#endif

定义卡丁车子类和lol子类

 

#ifndef _CARGAME
#define CARGAME_

#include "game.h"

class CarGame : public Game {
   public:
    void Skill() override { std::cout << "car game" << std::endl; }
};

#endif
#ifndef _LOLGAME
#define LOLGAME_

#include "game.h"

class LolGame : public Game {
   public:
    void Skill() override { std::cout << "lol game" << std::endl; }
};

#endif

 

适配器模式
太常见了,每个人都会用,两个不兼容的接口之间的桥梁,就像耳机转换头,充电器转换头等等都是适配器模式,将一个类的接口转换为客户希望的另一种接口的形式,使得原本由于接口不兼容而不能一起工作的类可以一起工作。

 

中介模式
使用一个中介对象来封装一系列的对象交互,当多个对象间互相引用且操作比较复杂时可以考虑使用中介模式。如下图所示:左侧五个对象互相依赖,通过中介这个桥梁就可以减少这个依赖。

 

状态模式
当一个对象的行为依赖于它的状态并且其有很多种状态而且将来还会有更多状态时,如果使用简单的if-else来增加新状态就违反了面向对象的开闭原则,这时可以考虑使用状态模式,将具体的状态做出一个抽象类,也类似于工厂模式,将具体的状态分散于各个子类中,避免了更多的if-else分支,上代码:

 

#include <iostream>
using namespace std;

class Context;
class State {
   public:
    virtual void Handle(Context *context) = 0;
};

class Context {
   public:
    Context(State *state) : state_(state) {}
    void Request() {
        if (state_) {
            state_->Handle(this);
        }
    }
    void ChangeState(State *pState) { state_ = pState; }
   private:
    State *state_;
};

class ConcreteStateA : public State {
   public:
    void Handle(Context *context) override { cout << "I am state a" << endl; }
};

class ConcreteStateB : public State {
   public:
    void Handle(Context *context) override { cout << "I am state b" << endl; }
};

int main() {
    State *state_a = new ConcreteStateA();
    State *state_b = new ConcreteStateB();
    Context *context = new Context(state_a);
    context->Request();
    context->ChangeState(state_b);
    context->Request();

    delete context;
    delete state_b;
    delete state_a;

    return 0;
}

 


 

分类
已于2020-9-3 18:08:16修改
收藏
回复
举报
回复