拥抱变化—— 可扩展性杂谈

拥抱变化—— 可扩展性杂谈

                                                                                      杨小华

作为软件开发人员最担心的就是变化,因为一旦变化,意味着自己的开发任务加重, 轻则修改代码,重则修改框架,如果不用做任何修改,则皆大欢喜,现实告诉我们,这是小概率事件,但比买彩票中大奖的概率还是大很多。于是各种讨论开始,开发人员开始讲述修改如何的大,进度如何紧张,架构师也在一旁不停的唠叨这个修改点的重要性,以及对整个系统带来的好处。

在业界曾经有一句很经典的话: “在软件开发领域中,唯一的不变就是变化 ” 。一旦变化,就有人遭殃,不是开发人员,就是设计师或架构师。无论谁遭殃,都不得不拥抱变化。

拥抱变化是极限编程( eXtreme Programming)里面一个非常重要的概念,代表了敏捷阵营对于变化的一种态度,那就是不拒绝,而且还主动求变。本文不想探讨敏捷方面的知识,如何去拥抱变化,而是想要探讨程序的可扩展性,如何在编码过程中,以最小的代价来应对程序未来的变化。

关于可扩展性, 其本身就是一个多方面的概念集合 。有人说程序的可扩展性必须建立在对未来需求的准确把握上,也有人说程序的可扩展性必须建立在能够对需求变化快速响应上。 不论 孰是孰非,其最终目的都是要求, 能在需求 发 生 变 化的 时 候以最小的代价去 应 付 变 化 。

可以从两个纬度对可扩展性进行讨论,一是设计可扩展性,二是编码可扩展性,前者从宏观上考虑,后者从微观上考虑,当然编码也是一种设计活动。本文重点论述编码的可扩展性,对于设计可扩展性,是一个系统性工程,由于作者还没有达到那个高度和境界,所以不敢瞎写,本文基本上不做介绍。

《 UNIX 编程艺术》一书中有一条关于扩展原则的描述:设计要着眼于未来,未来总比预想快。 关于设计可扩展性, 对于系统架构师或者系统工程师不仅仅要考虑在实现用户需求的基础上如何构建系统,还要考虑计算资源的可扩展、应用规模的可扩展,以及对技术换代的可扩展和性能等。

近期发生的干旱和水灾,每次都能找到人为的因素。本文开头提到的场景,如果进行代码回溯,也能找到一些人为的因素。如果当时的编码者在写代码时充分考虑了代码可扩展性,在一定条件下,可以达到用最小的代价去应对变化。如果当时只是为了完成任务,交差,后续的维护者可能面对的不是拥抱变化,而是拥抱痛苦!

场景一:在某嵌入式电信级设备整框分布式环境中,有 NEMI板(管理板), SWF板(业务板), STU板(业务板)和 LC板(业务板),每块板上都有 CPU,运行着各自的程序。目前的架构仅仅对 NEMI/SWF/STU板支持了 HA(High Available)功能,在 SWF卡上运行的某个业务,需要关注 SWF卡的主备倒换事件。 运行在 SWF卡上的程序可以收到来自 NEMI和 SWF卡的主备倒换事件,于是进行了如下编码:

void processSwitchEvent(GenMsg *pMsg) { 一些合法性判断语句 if(NEMI_SWITCH_EVENT == pMsg->getSwitchEventGrp()) { MSG_INFO(“Received NEMI Switch Event……”); return ; } //process SWF Switch Event 业务处理代码 }

可能开发人员在进行 if条件语句编码时,可能还考虑了另外一种写法:

void processSwitchEvent(GenMsg *pMsg) { 一些合法性判断语句 if(SWF_SWITCH_EVENT == pMsg->getSwitchEventGrp()) { MSG_INFO(“Received SWF Switch Event……”); 业务处理代码 } }

在最初的需求中,上下两种写法都是合适的,但是不是都合理呢?如果一旦需求发生变更, SWF卡上的另外一个业务需要关注 STU板的倒换事件,那么 STU板的倒换事件也会被广播到 SWF卡上,最糟糕的是,这两个业务都订阅了倒换事件(通过消息里面的内容来判断是哪块板发生了倒换),那么上下两种写法的区别就体现出来了,一个能正确运行,而另外一个会把 STU的倒换事件当作 SWF的倒换事件。不难看出,下面一种写法更具有可扩展性,达到了以最小的代价去应对变化。正是这样小的修改,往往会被忽略,隐藏一个很深的 bug,导致花大量的时间去定位。

对于上述的场景,大家编码时常会碰到,觉得这样写也合适,那样写也合适。虽然在一定条件下都很合适,但不一定都合理,那么此时就需要从其他方面加以考虑,如可扩展性,可维护性,可测试性等方面,从而确定哪种写法更合理。

场景二:假定存在如下一个消息类,最初类中只有一个成员变量,消息类的定义和实现如下:

class FsmFileTransferRequest : public GenMsgHdr { public: FsmFileTransferRequest (void) { memset (&mFileTransferReq, 0, sizeof(mFileTransferReq)); setMsgType (MTYPE_REQUEST); setMsgTypeQual (MQUAL_FSM_FILE_TRANSFER_REQUEST); setPayloadLen (sizeof(mFileTransferReq)); } // get/set operation …… private: SysPkg::FileTransferRequest mFileTransferReq; };

对于该消息长度,基类提供了两种接口,一个接口是 setPayloadLen (),另外一个接口是 setMsgLen (),该接口是更高一级的封装,为所传入参数减去基类消息的长度,最终结果还是消息的净荷长度。也许有人会说,基类就不应该提供两套函数,让人迷惑,出错在所难免。

由于场景变化或者需求变更,需要在该类中添加其他的成员变量,维护者可能是这个系统中的另外一个模块的开发者(自己所负责的模块中,构造函数里都是用消息总长度函数,默认其他开发者跟他一样),添加了成员变量和实现后,忘记修改消息的净荷长度,编译并运行,结果与预想的大相径庭,于是开始不停的打断点调试,不断的在怀疑消息是不是丢了,或者没有用修改的代码进行编译,总之,一切该怀疑的都在脑海中闪现了一遍。

或者,意识到要修改消息净荷长度,于是修改成:

setPayloadLen (sizeof(mFileTransferReq)+sizeof(mSuccessfulFlag));

如果只是一两个成员变量,还能忍受。需求一再变更,又增加了几个成员变量,继续修改, setPayloadLen()里面的代码会越来越长,只是代码写的难看而已。

如果类的实现者,在编写代码时,考虑一下可扩展性,采用消息的总长度函数,那么不论怎么添加成员变量,都不用修改消息长度,一劳永逸。如果确认这个消息不会被扩展,采用 setPayloadLen()也是合理的 。

通过以上两个例子可以发现,如果在编码时,充分考虑了编码可扩展性,即使需求发生变更,有时也可以达到事半功倍的效果。关键问题是如何识别出这样的场景,这个只能靠经验了,没有捷径可走!

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏数据派THU

独家 | 2种数据科学编程中的思维模式,了解一下(附代码)

通常而言,在同一个文件中覆盖完整的流程将会导致Jupyter Notebook、脚本变成一团乱麻。此外,大多数的数据科学问题都要求我们在数据收集、数据清洗、数据...

903
来自专栏java达人

通过人工智能编写自修改/自完善的程序

作者:Kory Becker 译者: Mr派 来源:http://www.primaryobjects.com/2013/01/27/using-artific...

2168
来自专栏java工会

学java就两个问题

1978
来自专栏牛客网

百度智能云提前批面经

【每日一语】人类的努力应该是没有边界的,我们千差万别,不管生活看上去有多糟糕,总有你能够做的事情,并且能够成功。有生命的地方,就有希望。——《万物理论》

1804
来自专栏斑斓

敏捷实践 | 代码是如何腐烂的

代码是如何腐烂的?这是一个很大的命题,因为这种腐化的代码样本可能会体现不同的特征。若要彻底总结,可能会又是一本《重构》。我自然没有这个能力和知识。好在有一个简便...

36210
来自专栏架构专栏

Java程序员年薪40W,他1年走了别人5年的路(技术提炼建议收藏)

介绍一下,这一次笔者笔下的这位大牛,lison,复旦大学工程硕士,专注技术十年,产品控、代码控,拥有丰富的项目经验,主持研发了多个成功上线的大型互联网项目。热爱...

1521
来自专栏程序员八阿哥

王老板Python面试(3): 一个初级python web后端开发工程师的面试总结

之前一直在做C++的MFC软件界面开发工作。公司为某不景气的国企研究所。(喏,我的工作经验很水:1是方向不对;2是行业有偏差)。

1422
来自专栏云计算D1net

干货:如何计算用户行为大数据

用户行为类数据是最常见的大数据形式,比如电信的通话记录、网站的访问日志、应用商店的app下载记录、银行的账户信息、机顶盒的观看记录、股票的交易记录、保险业的...

3405
来自专栏人工智能LeadAI

数据清洗经验

平时习惯了在某些特定的数据集合上做实验,简单的tokenization、预处理等步骤就足够了。但是在数据越来越大的年代,数据清洗越来越重要,也越来越复杂。看到P...

3534
来自专栏CDA数据分析师

分享 | 8条数据清洗经验,收藏备用!

文 | Philip Guo 来自Chaoslog 平时习惯了在某些特定的数据集合上做实验,简单的tokenization、预处理等步骤就足够了。但是在数据...

2375

扫码关注云+社区