随笔- 48  文章- 3  评论- 578 

  这个话题来源于下午和同事们的讨论,背景是这样的:他们在一个小项目上工作,周期是4周。第一周是第0个迭代,主要的工作为写Story,做Spike等。而此后便开始开发,前几天开发的速度比较慢。因为他们经常在写了一些代码后又觉得,恩,结构设计的不够漂亮,中间应该加一层之类的。所以大部分时间都在推翻设计,修改以及添加测试,重构代码,分解依赖,运行Build脚本,提交代码。

  问题就出现了,敏捷开发强调的是简单设计,Pair拿到一个Story的时候,分析业务需求,简单的讨论设计(大概15-20分钟),然后进行开发。在开发过程中发现有重复代码,进行重构,一切重复代码都得重构,包括测试。最终的结果就是,完成Story的时候,没有重复代码,这就是优良的设计。但是优秀的设计又要求容易扩展,需求变化的时候尽量少的修改。当代码能够很好完成它的功能,它就是有价值的,客户并不关心你的代码是否是低耦合,高内聚,也不关心它是否是多层架构的。花了很多时间做了高扩展的架构,到项目结束也没有要扩展的需求,这是否值得呢?

  适当的设计是必要的,过多的设计是浪费,关键是要把握设计的度。怎样才是必要的呢?当你知道某地方以后会变化,该地方就得对扩展开放。举个例子,如果要你实现解析文件,然后把信息存入数据库这样一个功能,当前只要求解析Xml,你可能会毫不犹豫的用XmlDocument。但是,后面的需求是必须支持更多的文件类型,比如Excel,Csv等,你就得考虑需要引入Strategy模式。我觉得这就是适当的设计,为以后的扩展提供支持。如果你不考虑也可以,只是后面的开发过程中会大量的重构,修改测试,花更多时间,别人会说你设计不好。那什么是过多的设计呢?很明显客户只需要支持Sql Server,那我在DAO中就使用SqlConnection等,而不必要引入Provider或者Sql Dialect以支持多数据库的变化。这样的设计就是浪费。

  良好设计的不二法则,面向接口编程。接口减少了类和类之间的依赖,提高了可测试性,很方便的引入DI框架。但是如果你看到我们项目的代码会发现,类之间全都是与接口交互,几乎所有类都会抽出接口,尽管它只可能有一种实现。这样做的目的是为了更好的使用Mock工具进行单元测试,当前还做不到Mock具体类而只能Mock接口。大量的接口伴随着唯一的实现,为了测试而设计,也是不小的浪费。我想肯定有更好的办法,比如将某些耦合紧密的类看作一个组合,对它进行单元测试。针对这些问题,我还需要更多的学习。

  希望大家都来讨论,你们在开发过程中是如何设计的,如何把握设计的度呢?

 posted on 2008-06-10 22:57 紫色阴影 阅读(2610) 评论(30)  编辑 收藏 网摘 所属分类: Design

#1楼    回复  引用  查看    
 caidehui       | 2008-06-10 23:41
这个不是设计的尺度的问题,而是设计能力的问题。

不管是敏捷也好,非常正规的设计也好都是一样的,要求设计本身是比较准确的。

敏捷的设计,是文档少,直奔主题,讲究快而准,那么就要求我们的设计人员水平要比较高、团队成员的能力要相当、大家心态都比较开放。

设计的最重要的原则还是隔离变化、要分清楚变化,面向接口编程可以换成一句话,叫面向使用编程,没有使用就没有接口了,而使用环境有可能变,所以接口要多多考虑使用的变化,从而在设计中将使用的变化能够容纳进来。

看看你介绍的问题,还有一个致命的问题,就是敏捷设计也需要架构和框架,没有这两个东西,就没有敏捷。将系统构架在一个稳定的基础上是非常关键的。尽管这个基础在项目开发过程中还会发生变化,但这个变化应该是可控的、频率很小的。

有了架构与框架,后面的问题自然就迎刃而解了。

敏捷开发如何设计架构与开发框架,当然要在最开始的迭代里面考虑,要使用1-2天的时间来分析所有的use story,如果没有所有的use story 也要将涉及到的业务讨论清楚,分析将来的扩展情况,分析这样的架构在各种情况下的变化与应对,再花上2-3天时间,实现这个框架,并能够多方验证与证实。

后面的迭代只要做好每次迭代要求的Use story 能够在框架下按时、按量完成,那么基本上就成功了


#2楼    回复  引用  查看    
 Justin       | 2008-06-10 23:53
文中讨论的情景似乎太敏捷了吧,是不是一开始整体要确定下来架构和开发框架,在此基础上“敏捷”地进行开发更好些
#3楼    回复  引用  查看    
 Jeffrey Zhao       | 2008-06-11 00:28
一定程度上一步到位,尽可能利用起现有框架/组件/代码。
#4楼    回复  引用    
 wyrover[未注册用户] | 2008-06-11 00:42
从来不认为接口是设计出来的,接口是抽象出来的,是重构出来的,我们要设计的只是对象而已。面向对象的系统,对象与对象传递消息,接口是提取出来的,对象通过接口对另一个对象发送消息。

面向接口编程而不是面向接口设计,所以大胆的重构吧。

#5楼    回复  引用    
 wwwwwwwwww[未注册用户] | 2008-06-11 01:00
sssssssssssssss
#6楼[楼主]    回复  引用  查看    
 紫色阴影       | 2008-06-11 02:08
@caidehui
我觉得还是设计尺度的问题,如何把握扩展性,不管从是整体架构来说还是设计。
文档少不是设计的特点,而是开发流程中的特点。你感觉没有文档,但是需要什么东西都可以找得到,所以说敏捷开发并不是不要文档,而只是保证最少最需要的文档,不用文档来驱动
任何开发方法都需要架构和框架的,这些是开发的基础。敏捷开发只是快速开发出原型系统,无需太多考虑。
变化是不可避免的,架构也是随之精化,当项目结束的时候,从整体上来看,也许和最初的时候很相似,但细看往往大不一样。


#7楼[楼主]    回复  引用  查看    
 紫色阴影       | 2008-06-11 02:09
@Justin
不是太敏捷,我们就是这样工作的
开发框架必然要确定,但是系统架构是慢慢精化出来的。

#8楼[楼主]    回复  引用  查看    
 紫色阴影       | 2008-06-11 02:09
@Jeffrey Zhao
请问老赵一定程度如何把握呢?

#9楼[楼主]    回复  引用  查看    
 紫色阴影       | 2008-06-11 02:13
@wyrover
可能理解不同,我相信接口是设计出来的,为将来的变化提供扩展性。即使重构要抽出接口,这也是重新设计的一个过程。
况且如果有必要的变化,为什么不一开始就决定使用接口,而一定要重构再提取呢?
还有一个问题,接口我更倾向于看作行为的交互,而不是消息的传递

#10楼    回复  引用  查看    
 戏水       | 2008-06-11 08:49
设计是为了应对变化,架构是为了易于扩展
敏捷的手和敏捷的心同样重要

#11楼    回复  引用  查看    
 小寒       | 2008-06-11 09:08
如何把握设计的尺度,这恐怕要要求设计人员要有丰富的设计经验
同时还要求设计人员要充分理解业务,为将来可能出现的新需求留出扩展的余地
但是,这样的人太少了,

同时还存在一个问题,诚然软件开发人员的流动性是很大的
就是也许在软件开发一期时一批人,二期又是一批人
这样一期的人可能在设计的时候,他只考虑自己先阶段的工作
他根本不会过多地去考虑二期的东西,这样到最后只能采用打补丁的方式

再有就是理论与实际应用的问题
道理大家都懂,但实际开发的时候,是否每个人都可以遵守呢

#12楼    回复  引用    
 htqx[未注册用户] | 2008-06-11 09:10
夸夸其谈。

#13楼    回复  引用  查看    
 坏人       | 2008-06-11 09:11
不断的推翻设计,或许可能是起初过于草率,敏捷!=草率。
#14楼    回复  引用  查看    
 坏人       | 2008-06-11 09:11
至于对数据库的PROVIDER,需要分情况,并非都不需要,假如你需要一个易于整合的PASSPORT就显得十分有必要,主体业务这样做基本就是过度设计。
#15楼    回复  引用  查看    
 alisx       | 2008-06-11 10:01
我没有敏捷开发的经验,只是觉得,敏捷开发应该是建立在一个成熟模型上的开发,所以开发的关键可能是基本模型的选择。
#16楼    回复  引用    
 wyrover[未注册用户] | 2008-06-11 10:08
@楼主
首先在面向对象的系统中应该考虑的是对象,万物皆对象,应该从对象入手。比如你在设计之前是要花uml类图的,画图的时候要先考虑抽象出对象,设计出类,然后看类有什么共同的行为,然后抽象出接口。

如果系统够简单,不需要扩展,就根本不用接口,是对象组成了系统,而不是接口组成了系统。如果是在有什么变化然后重构出接口,其实这个跟在设计uml类图时是一样的。

另外说,设计类图的时候一般都先考虑实体类,然后才考虑交互行为。



引用“
接口我更倾向于看作行为的交互,而不是消息的传递”

交互还是消息传递都是一样的,教科书上的东西,都是对象的一个方法,你调用对象的一个方法可以说是把消息发送给了一个对象,对象做出反应。

#17楼    回复  引用  查看    
 xiao_p       | 2008-06-11 10:09
有了架构与框架,后面的问题自然就迎刃而解了。

----->

现在也很赞同这个观点,如果没有成熟完整的框架的话,什么开发都是空中楼阁了。

基础的framework 还是很重要的,对于开发来说,如果没有一个稳定的framework,那么假设在上面的设计就是水中楼阁了。。。


#18楼    回复  引用    
 wyrover[未注册用户] | 2008-06-11 10:12
如果是敏捷开发,那么做到心中有对象,快速的设计类,然后用重构工具提取接口,不要一上来就设计接口。然后针对接口写测试代码。
#19楼    回复  引用    
 wyrover[未注册用户] | 2008-06-11 10:18
有人提到框架,框架这东西用来解决通用性问题,一般都能满足需要,比如spring这种东西,用了之后你会更感受到要设计对象,而不是接口。装配对象的活由spring负责。
#20楼    回复  引用  查看    
 Edward.Net       | 2008-06-11 11:27
过度设计和设计不足的确是我们开发过程中普遍存在的问题,设计不足的情况暂且不论,我一直使用的评价过度设计的标准只有一条:为了实现这个设计所要付出的代价与预期中所获得的收益相比较哪个更大。如果收益更大的话那么这个过度设计就是值得的,而且这种情况下就可以成为提高软件可扩展性的功能,否则的话只能提高软件开发的成本而收效甚微。
#21楼[楼主]    回复  引用  查看    
 紫色阴影       | 2008-06-11 12:12
@Edward.Net
强烈同意这一点,一切都归于哪个带来的价值更大

#22楼    回复  引用  查看    
 怪怪       | 2008-06-11 14:00
我同意一楼的, 虽然有些问题是所谓的尺度问题, 但更多的其实是能力问题, 而我们往往不承认这一点(谁也不愿意承认这点)。 很多问题就是这样: 在能力之外的, 就被看作尺度, 在能力之内的, 就被视为理所当然。

比如,重复代码之所以出现, 并非一定意味着不好的味道, 关键是判断为什么重复: 偶然产生的相似性, 或者真的是同一个流程; 尤其是随着设计和实现的不同, 也许在一个选择下同一个流程, 在另一个选择下就成了合理的相似性。

在这之外是工程的考量: 我们观察微软的产品、 框架、 工具箱, 也常常看到相似的东西被不同的小组实现了好几遍。 这不能说是对现实的妥协, 而是从生产效率来看, 什么样的做法更有助于分而治之。

另外, 既然你们是测试驱动的, 可以贯彻到底, 除了DI之外, 再使用或实现一个对象和接口的Mixin框架, 会方便许多。

最后, 能力不足往往实际上是客观条件, 最不恰当和不切实际的方式, 很可能是要求提高能力, 无论是个人的, 还是团队的。 认识到这一点, 就没什么不好意思的了, 而是可以根据我们的条件, 更好做出选择。

而拒绝承认这一点, 把一切问题都归结于“具体问题具体分析”, 由于问题的产生正是因为判断和分析的能力不足, 反而有可能掉进阴沟。

#23楼    回复  引用  查看    
 kiler       | 2008-06-11 17:43
我觉得设计这个东西一定要量力而行,如果你可以保证你使用新的设计不会很影响项目进度你就用,不行就不用。
#24楼    回复  引用  查看    
 金色海洋(jyk)       | 2008-06-11 18:25
设计是一种能力,
编码是一种“偷懒”,去掉重复的代码,从另一种角度来说就是避免以后修改的时候要修改多份。如果勤快的话,多修改几回又能如何?

当然我很懒,我才不想修改代码的时候,去修改重复的地方,呵呵。

#25楼    回复  引用  查看    
 RicCC       | 2008-06-11 19:05
@Edward.Net
收益这个东西是很难衡量的,系统的某种扩展性设计可能根本用不到,也可能2年5年之后派上用场,而最初设计时人们总是想着每个都用的着,这样得出的过渡设计概念也是没有太多依据的

#26楼[楼主]    回复  引用  查看    
 紫色阴影       | 2008-06-11 20:50
@怪怪
的确,大多数问题都是能力不足引起的,能力够的人一下就可以指出重点

#27楼[楼主]    回复  引用  查看    
 紫色阴影       | 2008-06-11 20:51
@金色海洋(jyk)
谁也不愿意修改很多地方。。
ms以前在csdn经常看到你

#28楼    回复  引用    
 wyrover[未注册用户] | 2008-06-12 12:33
如果要开发一个新的系统,是先设计接口还是对象,你清楚接口中有多少行为是系统需要的?而只有我们把系统看成对象之间的交互后,才发现这个对象需要被替换扩展,然后才抽象出了接口。发现有些人总把一些项目的经验用在另一个项目上,然后先考虑设计接口,然后让下面的程序员去实现对象。然后说这是经验和能力。
#29楼    回复  引用  查看    
 flyingfish       | 2008-06-13 15:18
@wyrover
有些道理,不过接口和对象谁先谁后并不一定要辩哪么清楚,实际中也是交替重构的。

就个人认为接口之所以存在主要为了消除依赖,用到设计模式时必然大量依赖接口,软件的主体是实体对象,但是灵魂是接口体系。

具体操作中完全依赖个人能力,很难有不变的教条可以参考,像那个大牛说的,设计就像悟道,很难直白的表达出来。

一楼的评论比较有说服力。

不管敏捷与否,如果做业务应用,对业务的把握是关键中的关键,各种设计技巧和思想并不是一定要一起施展。解决问题问题为准,否则就是过度设计,而解决问题与否,与对业务的把握程度更为关系密切,设计本身也只是工具而已。

#30楼    回复  引用  查看    
 金色海洋(jyk)       | 2008-06-15 17:55
是呀,现在不去csdn了,打不开,慢,IE还总挂。
开始混 cnblogs了,呵呵。

http://www.cnblogs.com/jyk/archive/2008/06/15/1222637.html" target="_new">http://www.cnblogs.com/jyk/archive/2008/06/15/1222637.html

我就是为了少改点东东,所以才费这么大的劲写了这个数据访问函数库,来减少以后可能的修改。

两外还有查询控件、表单控件、分页控件等来简化代码,当然也就是减少了修改的工作量。




发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 1217036




相关文章:

相关链接:
我要啦免费统计