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

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

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


我把我的经历分为以下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(塔中):服务接口测试,这一层相对其它两层,不会像界面那样多变,也不会像单元测试那样需要巨量的测试用例。接口相对稳定,只要我能保证有足够的测试用例在,就基本能够保证我软件的稳定性。所以我选择在这一层做自动化测试,有了足够的用例后,不管是后面业务变更或是代码重构,我都有非常大的信心去实现,因为自动化测试是我质量的保证。


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

欢迎关注:

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

头条号:小豆君编程分享

相关文章
|
8月前
|
存储 JSON API
带你掌握开发者必备的WebStorageAPI,客户端案例细讲
带你掌握开发者必备的WebStorageAPI,客户端案例细讲
|
11月前
|
前端开发 算法
前端开发常用的方法封装
截取地址栏里携带的参数、时间转换工具、字符串的截取等......
57 0
|
11月前
|
前端开发 数据安全/隐私保护
前端开发常用的方法封装(二)
将阿拉伯数字翻译成中文的大写数字、将数字转换为大写金额、 判断一个元素是否在数组中和数组排序等......
70 0
|
前端开发
前端学习案例14-代码的封装1
前端学习案例14-代码的封装1
65 0
前端学习案例14-代码的封装1
|
编解码 分布式计算 Java
基于 netty 封装的超简单通俗易用 服务端客户端交互框架 《net-framework》原理,源码和使用说明,开箱即用,只需要开发业务逻辑,完全自定义无限扩充 [结尾附github源码]
基于 netty 封装的超简单通俗易用 服务端客户端交互框架 《net-framework》原理,源码和使用说明,开箱即用,只需要开发业务逻辑,完全自定义无限扩充 [结尾附github源码]
基于 netty 封装的超简单通俗易用 服务端客户端交互框架 《net-framework》原理,源码和使用说明,开箱即用,只需要开发业务逻辑,完全自定义无限扩充 [结尾附github源码]
|
JSON 前端开发 JavaScript
封装库/工具库中重要概念之mock数据
在前端开发中,Mock数据是一个非常重要的概念。它能够帮助我们在没有后端API支持的情况下,模拟数据源并进行开发测试。随着前端技术的发展,越来越多的封装库和工具库提供了Mock数据的功能。在本文中,我们将探讨前端中封装库和工具库在Mock数据方面的作用。
195 0
|
TensorFlow 算法框架/工具 图计算
如何实现一个图化框架?代码已开源!
大家好,我是不会写代码的纯序员——Chunel Feng[3]。俗话说,人生如码,码如人生。人生中,有些事情是可以同时进行的,有些事情又必须是前后依次进行的;有些事情是可以刚开始就做的,有些事情又必须等待某个时机成熟了才可以开始。
323 0
如何实现一个图化框架?代码已开源!
|
缓存 NoSQL 数据库
3.NetDh框架之缓存操作类和二次开发模式简单设计(附源码和示例代码)
前言 NetDh框架适用于C/S、B/S的服务端框架,可用于项目开发和学习。目前包含以下四个模块 1.数据库操作层封装Dapper,支持多种数据库类型、多库实例,简单强大; 此部分具体说明可参考博客: https://www.
1266 0
|
JavaScript 前端开发 数据安全/隐私保护