0 bug:C/C++商用工程之道
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

1.1 系统分析初步

有关系统分析的内容,在很多教科书上都有较为详细的描述。系统分析几乎是所有程序开发行为的第一步。但是,商用工程程序员对系统分析应该有一些独特的理解,很多时候,商用系统分析与技术其实没有太多关系,更多的与沟通和合作有关。

1.1.1 需求理解和沟通

以笔者通常面临的商用数据传输工程来说,这类系统,一般都是指借助网络(局域网或互联网),通过多台计算机的协同工作、共同提供资源、共同分摊loading,最终为客户实现一个或多个服务需求的商用工程项目。

因此,商用工程的程序员在接到用户需求的时候,最忌讳的就是马上从编程的角度开始思考,这个功能如何实现、那个模块如何编写。那只会把事情越弄越糟,最后导致不可收拾。笔者一般秉持的习惯是,系统分析期间不涉及细节,先相信所有的细节是能实现的,以后再考虑风险点细节。

提示:程序界很多年以前就在争论,一个程序,自上而下编写(即先搭框架,逐步细化)和自下而上(先解决所有技术难点,做出底层模块,再来拼接)哪个好的问题。在商用工程中,笔者的经验,一定是自上而下。试想,连用什么平台和语言开发都没有确定,如何自下而上?

接到需求,程序员要做的第一件事情应该是理解需求。大家不要以为笔者在说笑话,在实际工作中,笔者就遇到程序员把需求完全理解反了的例子,还有的程序员干脆挑着看,对于自己熟悉的需求实现得很好,但不熟悉的干脆什么也没做。

因此,即使程序员接到的是一个小小的模块,也应该认真对待,在理解需求的时候,建议首先仔细看产品相关文档,如需求分析报告、系统设计书之类的文档,然后和自己的上级,可能是组长,可能是部门经理,也可能是项目经理,做一次面对面的直接沟通,讨论一下自己的模块在未来产品中究竟处于什么地位,它的优化方向是空间优先还是时间优先,有没有特定的算法需求等。

提示:现在企业都是团队合作,沟通必不可少。程序员有时候缺乏这方面的主动性,这需要调整。沟通的技巧,要主动陈述自己对模块的理解,言之有物,如“对于这个模块,我的理解是……”,请求上级予以点评,最终确保自己能完全理解,不至于做错事。

另外,沟通时要谦虚。笔者工作十几年,但在与上级沟通时,没有听懂,就老老实实说没听懂,因为不懂装懂,最后吃亏的是自己。上级一般也不会因此而生气,往往是重复再说一遍,确保沟通成功。

1.1.2 “上家”和“下家”

理解需求,还特别需要理解自己的上家是谁,下家是谁,即自己的模块从谁手里获得数据,自己产生的数据,下一步递交给谁。

商用程序模块原则上“宽进严出”,实现过滤器的效果,即不对上家提出要求,上家给出任何数据都统统吃下。内部对于自己能处理的数据做严格的校验,不符合的,做出一定的log日志之后,予以抛弃,避免错误数据被逐次传递放大,最终形成大问题。

反过来,商用程序模块,在传递给下家数据时,却十分小心,任何一点不对都可能导致该笔数据被抛弃,道理也是同上。根据经验,秉承这种原则设计的模块,一般都没有较大问题。

很多时候,我们在项目设计的最后阶段,即将开始coding的时候,还玩过“角色扮演”的游戏。即大家一起开会,每个程序员都站到前面来扮演自己所负责的模块,按照每笔业务流程,大家依次口述模块的功能和作用。

比如,从用户输入开始,UI的程序员说明,我收到用户某笔输入,这代表什么意思,我将其打成什么样的数据结构,并通过什么接口传递给下面哪个模块处理。对应模块的程序员立即接上,我收到什么数据,我需要做哪些处理,用途是什么,最终构建什么样的报文,交给哪个模块……

在项目管理中,这样做效果极佳,所有的程序员通过这一次演练,对自己负责的模块的功能、意义,与“上家”、“下家”如何交互均烂熟于心,回头做代码时,bug会比较少,尤其是基本上可以杜绝系统级逻辑错误。

1.1.3 角色“定名”

在笔者经常面对的数据传输工程中,需求基本理解之后就是网络角色的划分。这是非常重要的一步,后续的每一个动作基本上都是在使用这一步的结果。

角色划分,笔者更喜欢称之为“定名”。就是给系统中互相通信的每一台Server、Client定一个合适的名字。名字一旦定出,则每个角色负责的功能及其交互的对象,也就呼之欲出了。

举个例子,一个IM系统,即类似QQ之类的即时通信系统,通常情况下分为用户客户端、用户登录验证服务器、中心用户数据库、用户连接握手服务器等。

我们通常把用户客户端称为UA端,登录验证服务器称为CS,中心数据库称为DBS,用户业务连接握手服务器称为RS(S是Server的缩写)。

一次项目的系统分析,一旦定名,就不要更改,并且所有的项目成员都使用这些定名开始沟通,不允许再使用其他的名字,避免沟通发生不畅。

现在我们再看这个IM系统,假如一个程序员被分配写UA端,他就知道,原来他一启动,就要求客户输入用户名和密码,然后拿着这些信息,到CS上做登录验证,一旦通过,则进入等待状态,如果客户需要和另外一个客户沟通,则通过RS查找对方当前的位置,然后试图握手通信,如果成功,则通信开始。最后,客户下线,向CS发送下线请求,然后退出。

1.1.4 初步的拓扑图

可以看到,一旦定名,对于整个工作逻辑的流程就有了可描述性。此时,可以画出一个基本的网络拓扑图,如图1.1所示。

图1.1 网络拓扑图示例

其中UA1和UA2都需要到CS上做登录验证,CS和RS在服务前均需要向后台DBS验证用户信息,当UA1需要和UA2通信时,向RS查询UA2的位置,然后根据情况,做P2P直连或通过RS做Relay中继,然后聊天开始。大家看是不是清楚多了?

1.1.5 后续的模块级设计

当各个网络角色确定后,下面就是各分项模块的详细分析和设计,这个时候,才正式开始在每个角色内部划分模块。

不过话又说回来,商用数据传输的系统分析并没有跳出常规的系统分析路子,都是先划分模块,再确定关键数据及其走向,画出基本拓扑图,有必要的话,针对关键业务还要画出时序图,进一步明确动作的协调性,最终完成组织架构的设计。

1.1.6 商用设计思维

系统分析是一个大话题,业界已经有很多专著来论述如何实现系统分析,本书并不作为重点讨论,有兴趣的话可以自行参考相关的书籍。

本书在此仅仅强调一点,商用程序员应该把自己视为系统分析员,再小的模块也应该秉承系统分析的思路来解决问题。

再举个例子,笔者以前接到一个小任务,写一个ID比对的函数,ID是1~255字符长的字符串。这是应用在一个RS服务器上的验证功能。

正常的C程序员应该怎么做?strcmp,OK了,但笔者没有这样做。笔者首先询问了上级和同事,发现这个ID是有规律的:同一个组的用户,ID前4位都一样。

那么,笔者就考虑了,使用strcmp固然没有问题,但大家都知道strcmp否定优先,即不同的两个ID比较,总是比较快的;但strcmp又有个缺点,从左往右比较。这意味着,如果是同一个区的用户,前四个字符由于相同,它们的比较时间总是被浪费了的。

于是笔者设计了一个从右向左比较的函数,经过试验,效率远高于strcmp,使用效果非常好。

大家看出商用程序员的差别没有?