纯干货:客户端代码框架设计!

简介: 纯干货:客户端代码框架设计!

今天小豆君以第一人称视角,讲讲我做客户端开发的重构历程,并且在文中最后给大家指出我目前认为比较好的一个代码框架。


我把我的经历分为以下6个阶段:

阶段1:界面与业务代码混合

还记得刚毕业时,自己在学校也没怎么学习,代码基础可以说是一片空白,只知道一些基本的语法,凭着面试前死记硬背,勉强算是进入了一家公司,薪资呢也算是刚够吃饱饭的。

此时,我开发的是军工行业的一款桌面应用程序。我的代码都是在单个进程中开发的,而且业务逻辑也混合在界面类中,当另一个界面类想要获取到某个业务数据时,必须将另一个窗口类暴露出来。

随着代码量的增加,我的界面类越来越多,业务数据也越来越多。这就导致所有的业务和界面代码全部混合在一起,它们就像网一样,当需求关联到这些业务数据时,我很难从代码里面把它们清晰的拉出来。一旦发生错误,就像多米诺骨牌一样串联着发生很多个错误。

这使得我很难定位到真正的问题所在,按住葫芦起了瓢,刚刚修复了一个错误,谁想过一会儿测试又发现了新的问题。这让我痛苦不堪,看着其他人下班都早早回家,我却还在苦苦改bug,真是羡慕啊。

记得当时领导跟我讲的最多的是,小伙子的态度非常好,但技术能力就很一般了,还需要我多加把劲儿。为此,我暗下决心,对代码进行重构。


阶段2:界面与业务代码分离

为解决业务代码和界面相互嵌套造成的逻辑混乱,为此,我将业务代码和界面代码进行分离,就是我们平常说的前后端分离

核心点:

  1. 前端只处理布局、控件、样式、简单计算
  2. 后端只处理前端请求,为其提供业务数据

我用一个图大概表示如下

96447814-120fc980-1245-11eb-938d-6ea408716c72.png

界面与业务分离

结合上图

针对阶段1,有以下重要改动

1)前后端彻底分离

前端只负责:数据展示,界面操作,控件布局以及简单计算;

后端只负责:和数据库交互,和窗口类发送来的请求信息交互,返回前端所需数据

2)新增一张全局信号表,当然也可以将该信号表进行详细分类,分别放在不同的文件中

该表主要定义界面与业务的信号

信号和槽的命名方法:

  • req(request的前三个字母)开头的为请求信号,以on开头的为对应槽;
  • res(response的前三个字母)开头的为响应信号,以on开头的为对应槽。

上图中我为了获取用户信息,在信号表中将get_user分为两个信号req_get_user和res_get_user,业务代码使用槽on_req_get_user来处理请求,子界面A使用槽on_res_get_user来处理响应。

有些同学可能会觉得这是多此一举,在该窗口类A中定义一个获取用户信息的函数,当点击按钮时直接调用该函数岂不方便很多。然而这种做法是错误的,这又回到了我讲的阶段1中,业务代码和界面没有分开。

例如,此时又有一个子窗口B也需要获取用户信息,这时你有两种选择,一是把子窗口A中的代码复制过来再重新实现一遍,显然代码重复不可取;方法二是直接在A中把该函数设为公有,在B中引用A的方法。如果你使用方法二,当时觉得开发很快,但随着需求的不断变化,你会发现代码会越来越难维护,所有的东西都混在一起,你只能不断的往代码中贴补丁,最终整个框架爆掉。

那么,我们如果采用前后端分离的方式,针对以上界面类B想要获取用户信息,只需要在给类中也定义一个同名槽函数on_res_get_user绑定响应信号res_get_user即可获得用户信息。这代码看起来就舒服多了。


但是当我正在为自己的重构而沾沾自喜时,新的问题又出现了,由于客户量的增加,与之对应的数据也在不断攀升,由于我的代码查询大量数据占用了过多时间,从而出现界面出现卡死的情况,为此我又开始思考代码重构。


阶段3:使用多线程

解决界面卡死的一个比较好的办法是使用多线程,多线程大家都清楚,我就不过多解释了。

为此,我继续重构代码


96447814-120fc980-1245-11eb-938d-6ea408716c72.png

增加线程管理器处理前端请求

在线程管理器类中,新增线程池,每次接收到前端请求任务时,从线程池中取出线程执行任务。

这样,客户就不会因为复杂的sql查询而在界面前苦苦等待了。


阶段4:业务代码进行分层处理

在实现了多线程的处理方式后,我并没有高兴的太久,产品的需求更加变态,虽然前端部分的代码已经分离出来,但业务部分仍是一团糟,这又迫使我不得不再次进行重构,本次主要是对业务代码进行重构。

我的思路是参考java中的分层模型,但java里面分层比较复杂,客户端没必要做的那么做,为此我将其进行了简化。

  1. 预处理层:类似于网关,主要处理路由转发、请求过滤、请求埋点、业务监控等;
  2. 服务层(Service):主要处理具体业务请求;
  3. 管理层(Manager):通用业务处理层,主要封装第三方功能接口;对Service层通用能力的下沉,如缓存方案、中间件通用处理;与DAO层交互,对多个DAO的组合复用;
  4. 数据访问层(Dao):与数据库进行交互,并返回数据模型类对象。

96447814-120fc980-1245-11eb-938d-6ea408716c72.png

业务分层


阶段5:双进程or多进程

通过以上代码重构,我渐渐取得了领导的信任,我开始担任起技术组长的职务。随着团队成员的增多,每个人的技术水平参差不齐。C++这门语言只要有个指针引用错误,就会导致整个程序崩溃,有些客户需要保存的数据也会因此而丢失。用户体验实在是太差了,长此以往,我们的客户流失率也必然会升高。为此,我不得不对代码再一次重构。

因为之前一直使用的是单进程,所以为了不让程序彻底死掉,针对我们的业务需求,我采用了双进程模式。前端一个进程,后端一个进程。当然,开发者也可以选择多进程模式,例如谷歌浏览器,一个界面就是一个进程。即使崩溃也只影响的是当前界面而已。具体的方案要根据实际业务需求来制定。

由于前面我已经做了前后端分离,本次重构很顺利的完成了。想想还是很happy的。


阶段6:加入自动化测试

尽管使用了多进程,但并没有真正意义上解决客户问题,产品质量仍然是问题。我们追求的是尽可能少的出现bug更低的缺陷率,我一定要加强测试,此时自动化测试进入了我的视线。

一般的测试方法,就是在界面上根据功能不停的点击,进行人工测试。当我们再次更新程序时,测试人员必须再次进行重复测试,回归测试等,耗时耗力。

下图是一个比较传统的测试分层:

96447814-120fc980-1245-11eb-938d-6ea408716c72.png

测试分层

测试分层的金字塔模型

  1. UiTest(塔顶):界面测试,一般测试主要集中在这一层,以人工点击方式为主。这一侧距离用户最近,距离代码最远,这就导致一旦发生错误,我们需要跟踪的链路非常长,定位问题相对比较困难,有时还很难复现问题。但由于其离客户最近,也最能体现完整的业务逻辑。
  2. UnitTest(塔底):单元测试,这一层相当于白盒测试了,主要以开发为主,自己编写测试用例脚本,对每一个函数进行测试。这一侧距离用户最远,代码最近,所以一旦发生错误,我们能够很容易定位到。如果我们能把这一层做好,客户端代码的稳健性就有了非常大的保证了。但是它需要消耗大量的时间和精力,且随着代码的变更,测试用例也必须实时更新。一般公司都不太具备这样的条件。
  3. ServiceTest(塔中):服务接口测试,这一层相对其它两层,不会像界面那样多变,也不会像单元测试那样需要巨量的测试用例。接口相对稳定,只要我能保证有足够的测试用例在,就基本能够保证我软件的稳定性。所以我选择在这一层做自动化测试,有了足够的用例后,不管是后面业务变更或是代码重构,我都有非常大的信心去实现,因为自动化测试是我质量的保证。


好了,以上基本就是我的客户端代码的框架设计之路。

欢迎关注:

微信公众号:小豆君编程分享

头条号:小豆君编程分享

相关文章
|
C++
基于Qt的简易聊天室设计与实现
基于Qt的简易聊天室设计与实现
1059 1
|
语音技术 异构计算
FunASR项目支持实时语音识别
FunASR项目支持实时语音识别【1月更文挑战第7篇】
4976 1
|
移动开发 前端开发 测试技术
关于前端AB实验,我是这么思考的
背景 大家好, 我是Fly哥, 这次分享的内容主要是关于ABtest ,我们是做用户增长的,说白了就是对应下面几个关键词。拉新、激活、留存,留存的话 又分为 次日留存、 3日留存,这些都是我们的指标, 但是产品设计一个需求的时候, 可能会有实验的性质,不确定哪一组实验,对于指标的反馈是正向的,或者是那一组实验的效果更加明显。 这时候产品就会去创建AB实验,然后拿线上的一部分流量,去做实验, 分析数据, 得出实验结论,然后看是否满足预期, 如果不满足 就暂停实验, 或者进行全量实验。 大家可以看下下面这张流程图: 图片 然后对于我们前端而言,我们关心的点只有两个 第一个就是接入ABtest,
关于前端AB实验,我是这么思考的
|
算法 搜索推荐 数据挖掘
AB实验设计
AB实验的原理、优缺点及流程
2113 0
AB实验设计
|
7月前
|
移动开发 文字识别 小程序
抖音链接跳转到微信如何实现,引流到微信端?
随着短视频平台与社交工具的深度融合,抖音(字节系)与微信(腾讯系)的生态壁垒成为流量
|
SQL 搜索推荐 Android开发
AB测试实战(一)
AB测试是一种数据驱动的产品优化方法,用于比较不同版本的网页、应用界面或营销策略的效果。
|
12月前
|
存储 弹性计算 安全
阿里云服务器ECS通用型规格族解析:实例规格、性能基准与场景化应用指南
作为ECS产品矩阵中的核心序列,通用型规格族以均衡的计算、内存、网络和存储性能著称,覆盖从基础应用到高性能计算的广泛场景。通用型规格族属于独享型云服务器,实例采用固定CPU调度模式,实例的每个CPU绑定到一个物理CPU超线程,实例间无CPU资源争抢,实例计算性能稳定且有严格的SLA保证,在性能上会更加稳定,高负载情况下也不会出现资源争夺现象。本文将深度解析阿里云ECS通用型规格族的技术架构、实例规格特性、最新价格政策及典型应用场景,为云计算选型提供参考。
|
Kubernetes 负载均衡 应用服务中间件
在k8S中,什么是负载均衡器?
在k8S中,什么是负载均衡器?
|
网络协议 Java Unix
从0到服务器开发——TinyWebServer(上)
从0到服务器开发——TinyWebServer
1125 1
|
存储 JSON 关系型数据库
MySQL与JSON的邂逅:开启大数据分析新纪元
MySQL与JSON的邂逅:开启大数据分析新纪元

热门文章

最新文章