设计模式(3)--从零设计跨平台C/C++服务器框架

github源码:https://github.com/skynetlua/design_cross_platform_server
技术框架实质就是建立一种标准的工作流,让更多的人参与,更低成本地实现目标。
目前我们所规定的标准:

  1. 使用CMake管理项目,以Linux开源系统为标准,如果在windows上缺少的库,就模仿Linux造一个,实现一份代码跑全部平台。
  2. 采用utf8字符编码,调用win32接口,需要把unicode转utf8。
  3. 采用骆驼峰形式代码风格
class DemoClass //类名首字母大写,骆驼峰
{
    int parramArg_; //变量名末尾有_,非静态变量首字母小写
    static int ParramArg_;//变量名末尾有_,静态变量首字母大写
  static void FuncA(){}//函数名末尾无有_,静态函数首字母小写
    void funcA(){}//函数名末尾无有_,非静态函数首字母小写
};
  1. 设计模式众多,选择人脑比较容易接受的设计模式:面向对象、状态机和组件设计等。模块用组件实现,可达到组件通用化,避免重复造轮子。

一、面向对象设计

编程设计多种多样,计算机只是人脑的工具,作为人脑的工具,那么这个工具要更容易被人脑使用,而不是人脑无法驾驭,否则,这个工具就会被抛弃。我们希望做出的工具,有更多的人愿意去使用,这样我们才有机会拿来卖钱买饭吃。

面向对象设计,把业务逻辑封装类似实物世界,以一种人脑熟悉的方式,让人脑更容易接受和使用。制作工具一定要符合用户习惯。

我们设计的服务器框架,服务器主要功能是加工数据、提供数据和数据通讯,就像一座数据工厂,里面有各种各样的数据加工机器人。我们的第一个对象就是机器人,机器人有最简单的两点:属性和行为。

我们用变量记录和描述属性,用函数描述行为。

机器人
  属性:编号,名称,类型
    行为:
   行走()
   {
      //执行行走
   }
   工作()
   {
      // 执行工作任务
   }

如果有资金支持,我们可以开发一个汉语编译器工具,把上述内容编译成二进制程序,就可以在电脑上执行。很可惜,排得上号的编译器(gcc、cl、llvm、ndk等)都不是国产的。
从上面可以看出,面向对象只是一种逻辑,跟编程语言无关,我们可以用C语言和C++语言各实现一个robot对象。
C语言版本:

struct Robot
{
  int id;
  const char* name;
    int type;
}
void Robot_move(Robot* this)
{
  printf("move:id = %d\n", this->id);
}
void Robot_work(Robot* this)
{
  printf("work:id = %d\n", this->id);
}
int main()
{
  Robot robot;
  Robot_move(&robot);
  Robot_work(&robot);
  return 0;
}

C++版本:

struct Robot
{
  int id_;
    std::string name_;
    int type_;
    void move()
   {
      std::cout << "move: id = " << id_ << std::endl;
    }
   void work()
   {
      std::cout << "work: id = " << id_ << std::endl;
    }
}
int main()
{
  Robot robot;
  robot.move();
  robot.work();
}

通过两个版本的分析,可以看出C++编译器对面向对象提供了更好的语法便利。这就是C++语法糖,代价就是损失一些性能,好处就是提高写代码效率,代码更少,更容易维护。

二、状态机设计

计算机可以算是一种状态机,通过输入设备输入操作,就会改变状态。
比如HTTP服务器的状态:
1.监听到客户端连接,执行accept,建立链接状态;
2.解析HTTP头状态;
3.解析Cookie,处理session状态
4.验证modify和etag缓存状态
5.路由到具体url业务状态
6.获取到数据进行文件渲染状态
7.向客户端发送数据状态
8.最后关闭连接状态
上述把HTTP服务器复杂的请求过程,细分成各种具体的状态,大幅降低了问题的复杂度,转换人脑更加容易熟悉的状态。程序发生bug,可以快速定位;对性能调优,监测每个状态的消耗时间,带来极大便利性。
1. 简单状态机
我们对数据机器人建立状态机。两个对象Machine和State。Machine管理和控制状态State,一个State状态有三个方法,进入状态,状态刷新,退出状态。

在./src目录创建源文件simplemachine.cpp

struct Machine;
struct State
{
    void enter(Machine* machine)
    {
      printf("enter State\n");
    }
    void update(Machine* machine)
    {
      printf("update State\n");
    }
    void exit(Machine* machine)
    {
        printf("exit State\n");
    }
};
struct Machine
{
  State* curState_;
  Machine():curState_(0){}
  ~Machine()
  {
      if(curState_ != 0) curState_->exit(this);
      curState_ = 0;
  }
  void enterState(State* state)
  {
    if(curState_ != 0) curState_->exit(this);
    curState_ = state;
    curState_ ->enter(this);
  }
};
int main()
{
    State state1;
    State state2;
    Machine machine;
    machine.enterState(&state1);
    machine.enterState(&state2);
  return 0;
}

CmakeLists.txt文件为

CMAKE_MINIMUM_REQUIRED(VERSION 3.12.1)
PROJECT(server)
add_executable(simplemachine src/simplemachine.cpp)

最后在./build目录执行:

cmake ../
make
simplemachine

具体操作,请看工程构建章节。

2. 状态机简单例子
robot工作状态:接收消息状态,查询数据库状态,返回消息状态,等待消息状态。

  1. robot一开始,处在等待状态:EStateWaitMsg
  2. 有消息来,进入到接收消息状态:EStateWaitMsg=>EStateReceiveMsg
  3. 获取数据库数据,进入查询数据库状态:EStateReceiveMsg=>EStateQueryDB
  4. 拿到数据后,进入发送消息状态:EStateQueryDB=>EStateSendMsg
  5. 消息发送完毕,又回到等待状态:EStateSendMsg=>EStateWaitMsg
    把复杂的逻辑,划分成多种状态,简化问题,让人脑更容易理解和接受。
    新建src/demo.cpp源文件,实现代码如下:
//等待消息状态
struct Machine;
struct State
{
    virtual void enter(Machine* machine);
    virtual void update(Machine* machine);
    virtual void exit(Machine* machine);
};
enum EState
{
    EStateNone,
    EStateWaitMsg,
    EStateReceiveMsg,
    EStateQueryDB,
    EStateSendMsg,
};

struct Machine
{
    State* curState_;
    Machine();
    ~Machine();
    void update();
    void enterState(EState estate);
};

//等待消息状态
struct StateWaitMsg:public State
{
    int index_;
    virtual void enter(Machine* machine)
    {
        printf("enter StateWaitMsg\n");
        index_ = 0;
    }
    virtual void update(Machine* machine)
    {
        printf("update StateWaitMsg index_ = %d\n", index_);
        if (index_++ > 1)
        {
            machine->enterState(EStateReceiveMsg);
        }
    }
    virtual void exit(Machine* machine)
    {
        printf("exit StateWaitMsg\n");
    }
};
//接收消息状态
struct StateReceiveMsg:public State
{
    virtual void enter(Machine* machine)
    {
        printf("enter StateReceiveMsg\n");
    }
    virtual void update(Machine* machine)
    {
        printf("update StateReceiveMsg\n");
        machine->enterState(EStateQueryDB);
    }
    virtual void exit(Machine* machine)
    {
        printf("exit StateReceiveMsg\n");
    }
};
//查询数据库状态
struct StateQueryDB:public State
{
    int index_;
    virtual void enter(Machine* machine)
    {
        printf("enter StateQueryDB\n");
        index_ = 0;
    }
    virtual void update(Machine* machine)
    {
        printf("update StateQueryDB index_ = %d\n", index_);
        if (index_++ > 1)
        {
            machine->enterState(EStateSendMsg);
        }
    }
    virtual void exit(Machine* machine)
    {
        printf("exit StateQueryDB\n");
    }
};
//查询数据库状态
struct StateSendMsg:public State
{
    virtual void enter(Machine* machine)
    {
        printf("enter StateSendMsg\n");
    }
    virtual void update(Machine* machine)
    {
        printf("update StateSendMsg\n");
        machine->enterState(EStateWaitMsg);
    }
    virtual void exit(Machine* machine)
    {
        printf("exit StateSendMsg\n");
    }
};

Machine::Machine():curState_(0){}
Machine::~Machine()
{
    enterState(EStateNone);
}
void Machine::update()
{
    if(curState_ != 0)
        curState_->update(this);
}
void Machine::enterState(EState estate)
{
    if(curState_ != 0)
    {
        curState_->exit(this);
        delete curState_;
        curState_ = 0;
    }
    switch(estate)
    {
        case EStateWaitMsg:
            curState_ = new StateWaitMsg;
            break;
        case EStateReceiveMsg:
            curState_ = new StateReceiveMsg;
            break;
        case EStateQueryDB:
            curState_ = new StateQueryDB;
            break;
        case EStateSendMsg:
            curState_ = new StateSendMsg;
            break;
        default:
            return;
    }
    curState_ ->enter(this);
}
int main()
{
    Machine machine;
    machine.enterState(EStateWaitMsg);
    int count = 9;
    while(count--)
    {
        machine.update();
    }
  return 0;
}

在CMakeFile.txt文件加上add_executable(demo src/demo.cpp)。
在./build目录执行cmake..,可以用IDE打开工程,执行编译运行。

三、组件设计

我们的机器人需要接收消息,接收到消息以后,需要进行加工,然后把加工的数据,通过消息发出去。
我们有成千上万种数据加工业务,我们需要设计各种各样的机器人。
如果按照继承的方式,robot作为父类,需要实现各种各样的子类,这种方式虽然也可以做到业务分离,但是复用和共享很困难。
我们希望像汽车一样,标准化各种零部件,想要什么价位和性能的汽车,就选择组装什么样的零件。这个就是组件设计。
设计一个类Com,作为组件基类,把单一功能的逻辑,作为一个组件。Robot作为集合类,需要什么样的Robot,就组装什么样的组件。

由于篇幅,详细请见下一节。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,843评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,538评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,187评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,264评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,289评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,231评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,116评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,945评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,367评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,581评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,754评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,458评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,068评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,692评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,842评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,797评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,654评论 2 354

推荐阅读更多精彩内容