一个小程序能够反映的能力

程序员小郑刚步入岗位,但是在公司编码过程中没有受到专业的编码规范的培训,编写出来的程序虽然能够完成指定的功能但是比较不统一,偶尔会别出心裁的设计出自己的简化方法。老王这是从事了软件编码十多年了,现在都快到不惑的年龄了,在软件行业摸爬滚打十多年从事过多个行业,接触过不同公司的编码的规范,在软件代码编写中有独到的认识。

有一天有一个小功能的改动,由于这是一个非常重要的基础系统的功能变动,所以即便是一个小的功能变动公司上上下下都投入了非常高的重视程度。这天老王找到小郑告诉了需要修改这个系统并详细的描述了变动需求,他们两花了一个多小时进行了设计,老王并把需要修改的地方都详细的讲述给小郑,做了非常仔细的设计了。小郑按照老王的设计进行类设计编码设计,单元测试最终实现了功能。以下是他的第一个版本,同样把程序分为了经典的三层基业务逻辑层,数据服务层。

这是业务逻辑层

   <span style="white-space:pre">	</span>/// <summary>
<span style="white-space:pre">	</span>/// xxxxx
        /// </summary>
        /// <param name="qsc">xx队列</param>
        /// <param name="route">xx队列</param>
        /// <returns>返回联系人的电话号码</returns>
        public static string ReplaceVirtualTel(CQueue_SC qsc, CQueue_SCRoute route)
        {
            CooperateServicesDAL dal = new CooperateServicesDAL();
            return dal.GetOrderContactMobilePhone(qsc, route);
        }

这时数据逻辑层,

        /// <summary>
<span style="white-space:pre">	</span>/// xxxxxx
        /// </summary>
        /// <param name="qsc">乘客队列</param>
        /// <param name="route">航班队列</param>
        /// <returns>返回联系人的电话号码</returns>
        public string GetOrderContactMobilePhone(CQueue_SC qsc, CQueue_SCRoute route)
        {
            try
            {
                //判断接口返回的数据里面的电话是不是虚拟电话
                if (IsTelViturl(qsc.Tel))
                {
                    //如果是虚拟电话调用接口返回数据
                    XmlDocument result = InvokeGetRealTel(qsc, route);
                    //判断接口中联系人是否正确
                    if (IsContactPersonRight(result, qsc))
                    {
                        return result.SelectSingleNode(@"/Root/Return/ContactMobilePhone").InnerText.Trim();
                    }
                }
                return qsc.Tel;
            }
            catch(Exception ex)
            {
                return qsc.Tel;
            }
        }

        /// <summary>
        /// 判断条件:只要联系人1和联系人2有一个人相同那么就认为是正确的。如果没有相同的姓名就认为是错误的。
        /// </summary>
        /// <param name="result">调用联系人1数据</param>
        /// <param name="qsc">来自联系人2的数据</param>
        /// <returns>返回true,乘客一致。返回false,乘客不一致</returns>
        private bool IsContactPersonRight(XmlDocument result, CQueue_SC qsc)
        {
            XmlNodeList nodes = result.SelectNodes(@"/Root/Return/PassengerList/Passenger");
            List<string> name = qsc.Name.ToString().Split(' ').ToList();
            List<string> nameReturn = new List<string>();
            foreach (XmlNode xn in nodes)
            {
                string namert = xn.SelectSingleNode("PsgName").InnerText.Trim();
                nameReturn.Add(namert);
            }
            var commonNumbers = name.Intersect(nameReturn);
            if (commonNumbers.Count() > 0)
            {
                return true;
            }
            return false;
        }

        /// <summary>
        /// 判断返回的旅客电话是不是就是虚拟的电话
        /// </summary>
        /// <param name="tel">旅客电话</param>
        /// <returns>如果是虚拟电话返回true,否则返回false</returns>
        private bool IsTelViturl(string tel)
        {
            List<string> virtualTel = ConfigurationManager.AppSettings["VirtualTel"].ToString().Split(' ').ToList();
            if (virtualTel.Contains(tel)) {
                return true;
            }
            return false;
        }

        /// <summary>
        /// 调用旅客的真实电话号码
        /// </summary>
        /// <param name="qsc">队列信息</param>
        /// <param name="route">队列信息</param>
        /// <returns>返回调用接口返回的数据</returns>
        private XmlDocument InvokeGetRealTel(CQueue_SC qsc, CQueue_SCRoute route)
        {
            try
            {
                SCAirlinesZoneService.Services service = new SCAirlinesZoneService.Services();
                string command = string.Format(requestXML);
                string result = service.GetOrderContactMobilePhone(command);
                XmlDocument xmldoc = new XmlDocument();
                xmldoc.LoadXml(result);
                if (xmldoc.SelectSingleNode(@"/Root/Error/Code").InnerText.Trim() != "0000")
                {
                    throw new Exception("系统异常或未找到相应数据");
                }
                return xmldoc;
            }
            catch(Exception ex)
            {
                throw ex;
            }
        }

小郑第一版本的实现过程,他将逻辑层做了非常简单的封装几乎将所有的业务都放入了数据层里面,是不合理的,这对一个有刚入职的员工来说,一不小心图方便就会把代码业务不严格按照分层的原理进行分成编写。老王看见小郑的第一个版本给小郑提出了一些修改的意见,主要思想是让小郑把程序严格按照程序的分层原理将程序严格分层,业务逻辑都放入逻辑层面,数据访问都放入数据层。这样做的好处在哪里呢,如果按照小郑第一个版本那样做,虽然能够实现功能但是如果接口一变化那么从头到尾几乎所有的程序都不再适用新的变化了。需要重新开发一套流程来让新的业务符合条件,程序的可扩展性非常局限。

小郑按照老王的建议进行了第二个版本的编写:

业务逻辑层

#region 内部变量定义
        //虚拟号码存储
        private static string[] _virtualTels = null;
        #endregion

        #region 方法定义
        /// <summary>
        ///xxxxxx
        /// </summary>
        /// <param name="qsc">队列1</param>
        /// <param name="route">队列2</param>
        /// <returns>返回联系人的电话号码</returns>
        public static string ReplaceContactTel(CQueue_SC qsc, CQueue_SCRoute route)
        {
            try
            {
                CQueuePassenger pnrQPsg = new CQueuePassenger();
                pnrQPsg.NUM = qsc.NUM;
                pnrQPsg.ContactTel = qsc.Tel;
                pnrQPsg.PassengerNames = qsc.Name.Split(' ');

                //判断输入值中的联系人电话,是否虚拟电话。
                if (!IsVirtualTel(pnrQPsg.ContactTel))
                    return pnrQPsg.ContactTel; //如果不是虚拟电话,那么返回(原联系电话)。

                //获取原始订单中的数据
                CQueuePassenger orderQPsg = CooperateServicesDAL.InvokeGetRealPassenger(qsc, route);

                //因此通过比对旅客姓名来决定是否同一份数据。
                if (pnrQPsg.AreSame(orderQPsg))
                    return orderQPsg.ContactTel; //如果是同一份数据,那么返回从订单提取的联系人电话。

                //返回真实联系人电话。
                return qsc.Tel;
            }
            catch (Exception ex)
            {
                //可添加日志记录服务器连接失败
                return qsc.Tel;
            }
        }

        /// <summary>
        /// 判旅客电话是不是就是虚拟的电话
        /// </summary>
        /// <param name="tel">数据的旅客电话</param>
        /// <returns>如果是虚拟电话返回true,否则返回false</returns>
        public static bool IsVirtualTel(string tel)
        {
            if (_virtualTels == null)
            {
                //ConfigurationManager.AppSettings["VirtualTel"].ToString().Split(' ');
                _virtualTels = ConfigurationManager.AppSettings["VirtualTels"].ToString().Split(','); //从配置文件获取虚拟电话列表
            }

            return _virtualTels.Contains(tel.Trim());
        }
        #endregion
    }

数据层的代码

/// <summary>
        /// 调用方法获取旅客的电话号码
        /// </summary>
        /// <param name="qsc">队列信息1</param>
        /// <param name="route">队列信息2</param>
        /// <returns>返回调用接口返回的数据CQueuePassenger对象</returns>
        public static CQueuePassenger InvokeGetRealPassenger(CQueue_SC qsc, CQueue_SCRoute route)
        {
            try
            {
                CQueuePassenger queuePassenger = new CQueuePassenger();
                SCAirlinesZoneService.Services service = new SCAirlinesZoneService.Services();
                service.Timeout = 2000;
                string command = string.Format(RequestXML);
                  string result = service.GetOrderContactMobilePhone(command);
           <span style="white-space:pre">	</span>XmlDocument xmldoc = new XmlDocument();
                xmldoc.LoadXml(result);
                if (xmldoc.SelectSingleNode(@"/Root/Error/Code").InnerText.Trim() != "0000")
                {
                    throw new Exception(xmldoc.SelectSingleNode(@"/Root/Error/Message").InnerText.Trim());
                }

                XmlNodeList nodes = xmldoc.SelectNodes(@"/Root/Return/PassengerList/Passenger");
                string passengers = "";
                foreach (XmlNode xn in nodes)
                {
                    passengers += xn.SelectSingleNode("PsgName").InnerText.Trim();

                    passengers += " ";

                }
                if (!string.IsNullOrEmpty(passengers))
                {
                    passengers = passengers.Trim();
                    queuePassenger.PassengerNames = passengers.Split(' ');
                }
                queuePassenger.ContactTel = xmldoc.SelectSingleNode(@"/Root/Return/ContactMobilePhone").InnerText.Trim();
                queuePassenger.Num = qsc.Num;
                return queuePassenger;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

这里个版本我们可以明显看出业务逻辑判断都封装到了业务逻辑层了,然而数据层只负责获取数据,然后把数据封装成为一个对象。这样做的目的在于如果需要替换接口我们只需要更换数据层里面的获取数据的方法,业务逻辑层可以使用工厂方法进行相应逻辑判断,一定程度上面我们获得了程序的重用性,并在程序的可扩展性上面获得了提升。带来的另外一个好处就是提升了代码可测试性。

基本上可以满足老王的要求了,其实我们发现变动并不是很大,最大的问题就是我们有没有意识将自己的程序编写得足够灵活,这样的代码还有修改的余地,还可以编写得更加的灵活通用,理想的情况可以做到非常的通用,比如使用泛型以及反射进行扩展。到了这一步我们的代码编写完了,我们需要考虑如何去测试这个功能的正确性,为了测试我们编写的代码,我们需要拿出当初当初设计这个逻辑的逻辑处理图。


从这幅流程图里面我们可以明确测试点,以增量方式测试我们的程序是否正确。

1.测试接口完整性以及正确性。

2.测试我们的程序是否满足了要求。

根据这两个要求我们的可以得出测试点如下有7点:

1.调用接口成功的情况。

2.调用接口失败的情况。

3.调用接口超时的情况。

4.有虚拟号且姓名匹配的测试。

5.有虚拟号但姓名不匹配的测试。

6.无虚拟号码。

7.配置了多个虚拟号码的情况。

在这里我们的测试用例的设计是按照系统流程图来的,覆盖流程图的每一个分支,而且我们的覆盖到每一个可能出现的异常。比如接口我们就考虑测试他的三个方面,目的是为了保证接口这个部件经过测试之后能够被我们完全信任。第二,顺序性测试,先测试了接口然后在测试调用的代码,因为我们的代码建立在接口调用所获取的数据,如果接口未被测试通过,我们可以模拟接口返回的数据进行代码逻辑的测试。

所以小郑在这一次的收获就是“如何以最小的代价做到代码的易扩展性和可测试性”。

一个小程序能够反映的能力,布布扣,bubuko.com

时间: 2024-08-27 05:00:52

一个小程序能够反映的能力的相关文章

先做一个“小程序”——关于微信应用号的六大猜想

先做一个“小程序”——关于微信应用号的六大猜想 9月 21 日,苦等了9个多月的时间,应用号终于与我们见面了,命名为「小程序」. 01 为什么推出小程序? 考虑到小程序对整个APP市场的影响,毫无疑问会对现有的APP生态带来一定的冲击.但是,之所以推出小程序,最直接的原因可能是为了构建和扩充微信生态链,让微信更具开放性. 如我们所知,目前微信公众号分为三类: • 服务号,连接人和商品,目前很多电商企业,以及在微信端提供产品和服务的企业都用服务号. • 订阅号,微信官方的定位是阅读,连接人和资讯的

关于链表的一个小程序

关于链表的一个小程序: /**************************链表*****************************//* 具备功能 *//* 链表按元素位置插入 *//* 链表按元素位置删除 *//* 链表全表遍历 *//* 链表整表创建(头插法) *//* 链表整表创建(尾插法) *//* 链表整表删除 *//**************************链表*****************************/ #include<stdio.h>#in

原创观点:还在认为企业或个人只能申请一个小程序吗?

今天第一次体验了整个小程序注册流程,使用的是我自己的这个简单的教程(个人无appid如何进行申请开发者权限,简单操作步骤),教程虽然是我的,我自己却没有真的实践过,真是误人子弟,自己也不一定能知道:今天亲自测试了一下:首先走完邮箱验证: 验证完毕后,开始进入信息登录,这里,有一些关键信息,是本文观点的主要支撑: 请输入管理员的身份证号码,一个身份证号码只能注册5个小程序. 然后填写完毕后,到了第二个关键点: 为了验证你的身份,请用绑定了管理员本人银行卡的微信扫描二维码. 这个点,透露了两个信息,

Python 练习册,每天一个小程序

Python 练习册,每天一个小程序 说明: Python 练习册,每天一个小程序.注:将 Python 换成其他语言,大多数题目也适用 不会出现诸如「打印九九乘法表」.「打印水仙花」之类的题目 点此链接,会看到每个题目的代码, 欢迎大家 Pull Request 出题目,贴代码(Gist.Blog皆可):-) 本文本文由@史江歌([email protected] QQ:499065469)根据互联网资料收集整理而成,感谢互联网,感谢各位的分享.鸣谢!本文会不断更新. Talk is chea

Python练习册,每天一个小程序

Python练习册,每天一个小程序 精选评论关注该公众号可参与评论 写评论 加载中 以上评论由公众帐号筛选后显示 Python练习册,每天一个小程序 提交 我的评论 已评论 Python练习册,每天一个小程序 2014-12-15 程序猿 说明: ●Python 练习册,每天一个小程序.注:将 Python 换成其他语言,大多数题目也试用 ●不会出现诸如「打印九九乘法表」.「打印水仙花」之类的题目 ●欢迎大家 Pull Request 出题目,贴代码(Gist.Blog皆可):-) ●访问链接h

Python 练习冊,每天一个小程序

Python 练习冊,每天一个小程序 说明: Github 原文地址: 点击打开链接 Python 练习冊.每天一个小程序.注:将 Python 换成其它语言,大多数题目也试用 不会出现诸如「打印九九乘法表」.「打印水仙花」之类的题目 欢迎大家 Pull Request 出题目.贴代码(Gist.Blog皆可):-) Talk is cheap. Show me the code.--Linus Torvalds 第 0000 题:将你的 QQ 头像(或者微博头像)右上角加上红色的数字.类似于微

mono for Android 环境配置(环境搭建+破解方法+运行一个小程序)

一.准备工作:下载相关软件工具,为搭建环境做准备. 哎!百度网盘无法分享,根据名称下载. 二.首先安装 jdk-6u10-rc2-bin-b32-windows-i586-p-12_sep_2008 这里选择安装jdk6,网上看到一篇文章说jdk7及以上,mono还没有支持,但是那篇文字一年前写的,目前 支不支持,不清楚.这里保险起见,安装jdk6,mono是支持的.    注意环境变量的配置,这里不详细描述.主要配置java_home,path,class_path. 三.安装mono-2.1

c++学习笔记---02---从一个小程序说起

从一个小程序说起 这一讲的主要目的是帮助大家在C语言的背景知识上与C++建立联系. 问题探索 问题:对一个整型数组求和. 要求:定义一个存储着 n 个元素的数组,要求用C语言完成这个任务. 赶紧的:大家一起动手! 参考: #include <stdio.h> int addArray( int array[], int n ); int main() { int data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; int size = sizeof(data) /

c++学习笔记---03---从一个小程序说起2

从一个小程序说起2 要求:编写一个程序,要求用户输入一串整数和任意数目的空格,这些整数必须位于同一行中,但允许出现在该行中的任何位置.当用户按下键盘上的"Enter"键时,数据输入结束.程序自动对所有的整数进行求和并打印出结果. C语言版本: 1 #include <stdio.h> 2 #include <stdlib.h> 3 4 void main() 5 { 6 int i; 7 int sum = 0; 8 char ch; 9 10 printf(&