当前位置: 首页 > news >正文

软件设计:“度”、“裁剪”与“变通”

    最近一阵子比较清闲,所以又温习了一下《敏捷软件开发:原则,模式与实践》这本经典的著作,并且结合了前一个项目的流程,对号入座,反思了一下我们的不足和有待改进的地方。我发现在真正项目开发的过程中,敏捷的许多过程,我们在使用的过程中是有很多实际困难的,比如严格的TDD开发,结对编程,客户(Customer/Client,有时指的是客户代表)参与,开放的环境等等。而且在这个项目中,与其说我们使用了敏捷开发的过程,倒不如说我们的过程更像是RUP(统一过程)的敏捷版本。经过认真的反思和总结,我发现开发中有几个问题是值得思考的:

1、架构是演化而来的,那么基础架构(架构的起点)需要提前选定么?

    这个是一个容易让人困惑的问题,问题来源于敏捷开发与RUP开发的不同主张。前者主张根据用例分析软件的核心模型,然后直接编写代码并通过不断测试与重构,逐步演化获得核心架构;而后者讲究的水到渠成,通过用例分析,得到分析模型(核心模型),然后选择架构和框架,再实施编码。两者我都实践过,这里我只说说我的体会。敏捷强调重构到模式和架构,起点要求不是太苛刻的,但是按照这么一直走下去,我发现开发的过程中,代码可以很干净,但是架构的走向比较混乱,走着走着有时就偏离了方向,需要设计和开发者具有高超的重构和抽象功力;RUP过程比较自然,分析模型确定以后,直接选择架构和框架,然后编码,方向明确,目的性比较强,但是一旦后期某些需求(可能是功能上的,也可能是重构的需求)导致选择的架构需要扩充的时候,所做的工作可能就会比较多。

    当我屡屡在这个问题上苦苦纠缠,难以抉择,不断翻书思索的时候?我突然看到一本软件工程书上的一个词:“裁剪”,瞬间,我释然了。敏捷软件开发,RUP开发不都是成熟的开发思想吗?思想不同于准则,具体的执行步骤不是严格按照书上规定的走的,这个实践的过程是需要对过程进行修剪和重组,乃至融合的。就像设计模式,思路是相同的,但是同一个模式实现起来的结果可能是大不相同的。想到这里,我真的觉得以前太过执着,太过追求模仿、追全完美,最终是本末倒置,丢了西瓜,捡了芝麻!

2、TDD的粒度问题,如何写好测试?

     伟大的哲学家王守仁一生提倡知行合一,从这个层面上说,TDD是一个卓越的思想,也是一次伟大的实践,至少我是这么认为的。TDD是敏捷开发的绝佳组合,更是单元测试最忠诚的伙伴(TDD并不等同于单元测试的)。TDD带来了设计上的低耦合性和可测试性,保证了架构的灵活性和扩展性,也从而保证了产品的质量;而且,测试文件和测试结果也成为了最好的功能说明书和验收文档。

    当TDD带来这么多好处的同时,不可否认的是,它也为开发过程引入了很多复杂的因素:大量的Mock/Stub对象充斥在代码中,而且无法保证Mock对象与真正的对象实现上是可替换的;通常为了达到Mock对象与代码对象可以互换,Mock对象需要实现很多负责的模仿行为;很多的测试就是为了单纯的测试,不在是从代码的使用角度真正的去测试代码;通常测试的过程与真正编写对象的过程是一致的,这无疑造成了一些浪费;测试本身也是代码,也是有Bug的,这个该如何保证呢?关于测试代码的粒度选取上,每个人认识并不一致,这也造成了测试代码比较难以掌控;很多的数据访问组件都是有成熟的技术的,比如Linq,Hibernate, EF,这些如何去测试...

    真正实践TDD的时候,我才发觉这个真的是太难了,也许是经验不足的原因,很多的时候,我只是在关键的业务规则部分使用TDD。大家呢?   

3、组件是从上而下自然构造的,还是从下而上分析得到的?

    这个又是一个敏捷与RUP互相对立的地方。

    敏捷的实践一般是从类开始,即是从用例中分析核心模型,得到核心的对象,编写测试代码并实现对象,通过不断的迭代逐步构造完整的系统;等到需要分包的时候再根据分包的粒度原则(发布重用原则,共同重用原则,共同封闭原则)和依赖性原则(无环状依赖,稳定抽象原则,稳定依赖原则)来分配对象到不同的组件中,得到最终的软件层次架构。

    而RUP的过程则是在需求和功能分析设计后,从选择基点架构开始,比如常见MVC架构以及各种变体,多层架构以及各种变体,管道架构,分布式架构,一些新的如REST架构等等。选定架构以后,不同的组或者人员分配不同的部分,设计并完成各自的功能。通过不断的迭代和重构,完成各个版本和发布,最终得到软件的层次和架构。

    这里,组件的形成过程其实与架构的形成过程是息息相关的,从上面两种截然相反的过程中,我们又有哪些感悟呢?这个只能说是仁者见仁,智者见智了。我比较认同的观点是:这取决于项目的类型,大小。小型的项目用敏捷是不错的,合作愉快,你好我也好,真的是大家好才是真的好。对于大型的项目,通常是要选定多种过程融合执行(比如RUP+敏捷),一般是应该选定基础的架构,分好模块,初步定义接口结构,然后再不断重构和迭代。

4、分解开发的规模,独立运行的模块,真的降低了软件变质的风险?

    很多人赞同一个观点:问题以及解决这个问题的人员规模,是导致软件开发架构逐渐变质的直接因素。所以他们认为缩小问题的规模(通常是拆分解决方案),提炼独立运行的模块是相当不错的一种方案。

    独立运行的模块,固然是短小精悍,维护难度大为降低,但是这个过程中,复用的程度似乎是不高的。而且对于很多公用的组件,为了达到让多个进程使用并且不会干涉互相的正常运行,常常是需要将这个模块部署到GAC中,或者是拷贝多个版本,放到各自的运行目录中。在开发的过程中,部署到GAC似乎不是可取的方式;连微软都搞出来一套延迟签名的机制,显然Release之前部署组件到GAC是不好的。那么拷贝多个版本到不同的目录中呢?缺点也很明显,每次组件的更新,都需要更新每个调用的程序目录,当独立程序的数量大幅上升的时候,这个过程变的相当脆弱,容易出错,毕竟这个时候你是无法使用FAR(Find All Reference)来找到所有使用的地方的,当然了,使用Windows的查找功能也许还是可行的,但是这样还是比较麻烦的,最后连脚本都会被用上,用于简单的替换任务。那么如何是好呢?大家有什么好的做法呢?

    我曾经的经历是这样的:支持团队代码开发的是源码管理工具。代码分支(Branch)是经常采用的手段。不同的组工作在不同的Branch上,等到集成的时候,由集成工程师或类似的角色完成整合工作。这样不同分工的团队就可以工作在不同的分支上互不干扰,等集成完成以后,各自就能获得多其他组最新的程序。这个工作是有一个前提的,就是各个组之间,更深一步说,是各个组负责的模块之间,通信必须是畅通无阻或者是通信无关的;或者说通信必须是提前约定好的。这样解决方案的规模其实没有缩小,只是出现很多分支,而且有专门的人负责这方面相关的工作,比如集成和编译程序。这种做法说不上是好是坏,而且实施成本比较高,但是对我们来说效果还是有一点的。

    郑重声明:以上只是个人的感慨与总结,并不是每个问题的标准答案,更不是推荐给大家的成功经验和知识。限于能力,每个想法总是有对与错,如果观众们有不同的意见或者是自己的看法,那也是很正常,大家姑且当个故事,看看罢了!偶尔想到相关的问题,批判的瞅瞅,如果能产生自己的想法,那就更好了!

相关文章:

  • mantis 汉化的有效方法
  • windows 2003活动目录如何选择dns类型
  • oracle 类型转换函数 oracle 隐式转换规则总结
  • Java 控制台调用备份恢复 mysql数据库
  • 艾伟_转载:对于C#中b=a的N种情况分析
  • 一起谈.NET技术,WPF 动态模拟CPU 使用率曲线图
  • axis2学习——axis2的安装
  • 自上而下的语法分析
  • Proxmox VE 复制虚拟机
  • js页面传变量值
  • linux文件字符集转化
  • 雅虎徘徊,巴茨歇菜
  • 【百度地图API】如何实现信息窗口轮询
  • 新篇章
  • 深入剖析WCF的可靠会话[实例篇](内含美女图片,定力差者慎入)
  • android百种动画侧滑库、步骤视图、TextView效果、社交、搜房、K线图等源码
  • C++类中的特殊成员函数
  • CSS中外联样式表代表的含义
  • express如何解决request entity too large问题
  • jquery cookie
  • Spark RDD学习: aggregate函数
  • 基于Volley网络库实现加载多种网络图片(包括GIF动态图片、圆形图片、普通图片)...
  • 解析带emoji和链接的聊天系统消息
  • 开源SQL-on-Hadoop系统一览
  • 利用jquery编写加法运算验证码
  • 前端每日实战 2018 年 7 月份项目汇总(共 29 个项目)
  • 设计模式(12)迭代器模式(讲解+应用)
  • 收藏好这篇,别再只说“数据劫持”了
  • 体验javascript之美-第五课 匿名函数自执行和闭包是一回事儿吗?
  • 我有几个粽子,和一个故事
  • MPAndroidChart 教程:Y轴 YAxis
  • Spark2.4.0源码分析之WorldCount 默认shuffling并行度为200(九) ...
  • 阿里云IoT边缘计算助力企业零改造实现远程运维 ...
  • ​虚拟化系列介绍(十)
  • ###51单片机学习(1)-----单片机烧录软件的使用,以及如何建立一个工程项目
  • #includecmath
  • ( )的作用是将计算机中的信息传送给用户,计算机应用基础 吉大15春学期《计算机应用基础》在线作业二及答案...
  • (1/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序
  • (14)学习笔记:动手深度学习(Pytorch神经网络基础)
  • (175)FPGA门控时钟技术
  • (42)STM32——LCD显示屏实验笔记
  • (附源码)spring boot基于Java的电影院售票与管理系统毕业设计 011449
  • (机器学习-深度学习快速入门)第一章第一节:Python环境和数据分析
  • (每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理第3章 信息系统治理(一)
  • .desktop 桌面快捷_Linux桌面环境那么多,这几款优秀的任你选
  • .net Application的目录
  • .NET CF命令行调试器MDbg入门(二) 设备模拟器
  • .NET 动态调用WebService + WSE + UsernameToken
  • .NET/C# 使用反射注册事件
  • .net实现头像缩放截取功能 -----转载自accp教程网
  • .NET使用存储过程实现对数据库的增删改查
  • @RequestMapping 的作用是什么?
  • [ C++ ] STL---stack与queue
  • [2016.7 day.5] T2
  • [AMQP Connection 127.0.0.1:5672] An unexpected connection driver error occured