架构之路——穿行在产品和业务之间
篱笆作为一家起源于社区的电子商务公司,反映到技术层面就是同时要面对产品和业务,以及经营战略的变化调整。如何在产品和业务的夹缝之间完成技术架构的抽象与平衡,寻找更有效的价值定位,这当中有些经验教训和个人感悟愿与众人分享。
本次演讲视频录制于QCon杭州2011。
该内容已经被标记书签!
标记书签错误,请重试!
作者 Niclas Nilsson 译者 张晓庆 发布于 2009年2月23日
过去几周中, Joel Spolsky 和 Robert C Martin (又称Bob大叔)之间有一场论战。它起源于 Jeff Atwood 和Joel Spolsky在 Stack Overflow广播的第38期播客 ,Joel提到别人经常给他这样的建议,说他应该把单元测试加到 Joel测试:改进代码的12个步骤 之中,成为第13步。而Joel不同意这样,他解释说:
对测试驱动开发有一些争论……应该给所有东西都写单元测试吗,诸如此类的东西……许多人读了Joel测试之后向我写信,说:“你应该有第13步:单元测试,所有代码要有100%的单元测试。”
对我来说这有点太空洞,好像你根本不需要的一些东西。敏捷编程总的观点就是不需要就别做,需要时再引入。我觉得很多时候对所有东西写自动化测试并不能对你有所帮助。
Joel基本上有两个异议,第一个是你把时间花在什么上面了:
但我觉得即使团队的单元测试覆盖率达到100%,依然会有一些问题。他们可能花费了相当多的时间编写单元测试,然而质量未必能得到相应的提高。我是说,质量可能提高一些,他们也有能力修改一些代码并有把握不破坏任何东西,但仅此而已。
Joel的第二个异议是测试套件非常脆弱:
但是我发现单元测试真正的问题在于,随着代码改进,你想修改的内容可能会破坏一定比例的单元测试。有时你修改了代码,不知何故,有10%的单元测试 失败了。因为你修改了某些设计...你移动了一个菜单,现在所有依赖菜单的东西仍然存在...而菜单移到了别处。所以这些测试都失败了。你必须能够重写这 些测试,以反应代码新的实现。
讨论继而转向了Bob大叔搜集的面向对象设计的SOLID原则,Bob大叔和Scott Hanselman在Hanselminutes最近的一期节目中对此做了回顾。Joel再次说道:
这是面向对象的设计,而他们把它叫做敏捷设计,实际上它真的,真的不是。这只不过是一些怎样设计类,类应该怎样工作的原则。坦白来讲,当我听到这些原则时,感觉就像官僚之极的设计,一定是没写过多少代码的家伙想出来的。
但是Jeff和Joel显然没有了解清楚Bob大叔和他与敏捷的关系。Bob大叔是达成敏捷宣言那次大会的发起者,几乎从Jeff和Joel出生时他就靠写代码谋生了。他还写了相当多公开的代码,所以Bob针对博客上的内容发表了他的想法。
我不认为TDD很神圣。我认为它是一个值得遵守的规则。我不会事先编写所有的测试。有一小部分测试在编码后再写只会更方便。甚至有一些代码我根本不会写测试,因为不值得写。但是这些都是例外情况。大多数代码我会先写测试。(不,Joel,这没有浪费我的时间。)
Bob也决定在敏捷上盖过Joel:
Joel说SOLID原则不是“敏捷”。(唉)。任何一个人和他的大叔都认为自己知道单词“敏捷”是什么意思。但是是我召集了那次会议,挑选了单词 “敏捷”。自从词组敏捷开发被创建以来,我一直在写关于敏捷开发的内容。我认为我知道什么是敏捷,什么不是。我也认为我有权力在这方面盖过Joel。 Joel,SOLID原则是敏捷的。
对Joel经历的修改代码会破坏10%的测试的情况,Bob大叔写道:
Joel继续抱怨测试脆弱的问题,说一旦修改了代码,一些测试自然失败了。Joel还引用了统计数据10%。这太傻x了,说明Joel对TDD了解 浮浅,根本没有学到任何东西(典型失败的书呆子)。如果你设计的测试有10%因为单点变化而失败,那你应该换个工作了。测试设计,与软件设计的任何其它部分一样,也需要解耦。
随着辩论继续,Atwood写了一篇博客,他称之为The Ferengi Programmer,他说即使他赞成SOLID原则从表面上看没有异议,但是确实有这样一种危险,就是过于依赖规则。
规则、准则和原则是从实践中提炼出来的宝藏,值得研究和尊重。但是它们决不能替代对你工作的认真思考。
Jeff所写的无可置疑。不仔细思考事情就不会有好的软件,大家写书、规则或者方法通常是意识到这样一种风险,大家奴隶般遵守你的建议,而没有意识到环境的不同,但是避免这些风险并不容易。总结起来就是http://www.infoq.com/cn/articles/better-best-practices,以及读者阅读建议时达到的技能级别。听从Jeff的建议意味着如果以德莱弗斯的衡量标准,你是想在那项技术上从高级初学者或者精通级别转到胜任。这种变化可以认为是你必须有意去做一些事情才能达到,并非随着时间流失就能够自然达到。
在更多公开的争论之后,大家决定Bob大叔应该去Stack Overflow播客广播,该期内容已于最近广播。 Bob大叔得以有机会描述SOLID原则背后的想法,Joel问了一些关于它们什么时候有用的问题。当Joel问道为什么该原则重要的时候,事情有了小小 的转机,Bob举例说能够分开部署组件,Joel继续问这个原则是否只在那些需要分开部署的大项目中才有用时,Bob声称他在这一点上同意Joel。
当谈到质量和TDD时,他们的分歧更加清晰起来(即使好像大家都接受测试像文档一样有价值这个观点)。Bob解释了创建测试套件的重要性,这些测试套件不能像Jeol经历中的那样脆弱,不要一点修改就破坏10%的测试,Bob还介绍了怎样做到这一点,比如通过逻辑和呈现层的分离、在界面显示内容之下 正确地测试。Jeff提出Unix社区通常是这么做的,首先写一个命令行程序,然后在其上封装一个UI来驱动它。
播客的最后,Jeff、Joel和Bob再次谈到了遵守建议的风险,以及编程社区的技术水平,他们一致认为太多的开发者根本不关心自己在做什么。总之,他们都认为多动脑筋是很有必要的,但感觉他们对开发软件的方式尚未有一致意见。他们三人都承认对变化做出响应很重要,但是要想做到响应变化,需要对设 计和测试投入多少是他们分歧的核心,也是整个行业普遍存在的分歧。变化是永恒的,我们如何处理变化却差别甚大。
这次讨论自然而然地在博客圈内引起了大量的讨论,论坛上的信噪比也下降很多。Dhananjay Neneto发表了与学习相关的一个想法深刻的评论,用以回应Jeff的这个帖子──The Ferengi Programmer,Dhananjay说道:
我认为这个帖子带来的大麻烦是,它的建议对初级程序员带来的害处要比好处更多。它可能鼓励他们做出取舍──甚至在学会做取舍的代价和含义之前。它也可能让他们远离此旅──仔细而明智的应用(需要在早期投入相当多的精力),并能帮助他们内化吸收那些原则。
这是一场可能永远持续下去的争论,也是一场杂音太多没什么意思的争论,但是仍然有一些有趣的东西,这些东西不仅仅是爱好的问题。它还引出了这样几个问题:
有些东西经验丰富的人已经了然于心,我们怎样让新丁学习,并且学习时不要过于艰难呢?
我们怎样应对变化,从哪一点开始为应对变化而预做准备的投入会大过产出,界限在那里?
SOLID原则只适用于复杂项目吗?什么时候该用,什么时候不该用呢?用和过度使用之间的分界线是什么呢?
查看英文原文:Spolsky vs Uncle Bob
译者 张晓庆 有多年的软件开发经验,主要是J2EE项目、Web应用和分布式系统等等,在电信网管开发方面经验丰富。
有些东西经验丰富的人已经了然于心,我们怎样让新丁学习,并且学习时不要过于艰难呢?
Bob 大叔的回答也是不错的:
我不认为TDD很神圣。我认为它是一个值得遵守的规则。我不会事先编写所有的测试。有一小部分测试在编码后再写只会更方便。甚至有一些代码我根本不会写测试,因为不值得写。但是这些都是例外情况。大多数代码我会先写测试。(不,Joel,这没有浪费我的时间。)
前半部分,我很赞同,TDD 不应该成为神圣的教条。Bob 提出的做法其实是一种改良过的 TDD,放宽了原先 TDD 对开发步骤顺序的严格限制。可见,我们(至少在 Bob 所说的例外情况下)并不需要针对所有实现代码,都必须先写测试代码。一个问题是,如果你不是每次都先写测试代码,后写实现代码,怎么叫 test driven 呢?
Uncle Bob 说,大多数情况下,他会先写测试。具体他没说,是那种类型的测试,估计是单元测试。和 Uncle Bob 不同的是,一般我肯定不会首先去写单元测试,除非有绝对的必要,比方这些单元都很重要,而且它们的外部接口、使用方式和依赖关系已经相对稳定了,需要对外发布这些单元等等。
尤其在开发阶段的早中期,在需求和架构都还不稳定的时候,我更不会首先把精力放在写单元测试上。我会尽量根据功能需求(use case)的基本流、扩展流以及架构的重要性等因素来设计测试,即便要做更小粒度的单元测试,也会首先考虑对一组、一群对象联合进行粒度更大一些的测试。我觉得这种方式效率可能会更高。
UDD 认为需求、架构是软件开发的主要矛盾,每个单元相对于整体和全局来说是次要矛盾,当然这是一对辩证关系。所以,由于存在软件开发哲学观点的差异,TDD 更像是 bottom up 的开发,先保证每个单元积木块的正确,再保证整体、全局的正确;UDD 则更像是 top down(architecure-driven)的开发,先保证整体、全局的稳定和正确,再来完善局部细节。在实践中,其实这两者都是可行的。架构重要?还是单元重要?答案是在运动、变化的。
由于任何带有边界的“系统”(包括类、单元在内)都存在 use case,所以 UDD 的单元测试,也借鉴了 use case/scenario 的设计方法。
针对我所了解的国内的情况,UDD 认为:TDD 对开发步骤顺序的刚性规定(“必须”先写单元测试代码,后写实现代码)在实践中是没有必要的,而且很多时候没有必要从单元测试开始写起。另外,大多数代码都应该努力配上自动系统功能测试,以减少人工测试的麻烦。系统测试的完备性比单元测试的完备性更重要。
资深敏捷 OO 教练
www.zhangxun.com
当然,比较简单:Joel Spolsky is wrong about my work
参见www.threeriversinstitute.org/blog/?p=29
否则就成为了教条。
两种思维方式应该结合起来。
篱笆作为一家起源于社区的电子商务公司,反映到技术层面就是同时要面对产品和业务,以及经营战略的变化调整。如何在产品和业务的夹缝之间完成技术架构的抽象与平衡,寻找更有效的价值定位,这当中有些经验教训和个人感悟愿与众人分享。
本次演讲视频录制于QCon杭州2011。
本文将对特性注入以及相关方法做一个扫盲性的介绍。我们会解释这个框架的关键要素,并附上实例来证实它们。为了让文章保持相对较短,我们不会深入到某个工具或方法中,而是会给出一些参考资料,以便大家做进一步的研究。
随着JDK 7的发布,字节码指令集终于迎来了第一位新成员——invokedynamic指令。这条新增加的指令是JDK 7实现“动态类型语言(Dynamically Typed Language)”支持而进行的改进之一,也是为JDK 8可以顺利实现Lambda表达式做技术准备。在这篇文章中,我们将去了解JDK 7这项新特性的出现前因后果和它的意义。
随着互联网应用的发展,Java分布式远程服务技术受到越来越多的关注,本文将对各种相关实现以示例的形式逐一介绍,并总结其中的优缺点,使读者能够在技术选型时有所准备。这是文章的下篇。
《精通HTML5和CSS3设计模式》一书记录了目前HTML5应用程序的许多常见设计模式。InfoQ对该书作者之一Dionysios Synodinos进行了采访,谈到了该书以及HTML5应用的相关内容。
本次将与大家分享B2B在构建生态化分布式数据库架构体系的摸索和实践,介绍B2B为解决海量数据实时访问,数据按需流转等业务场景开发的一系列技术产品,以及各个技术产品之间如何进行协调一致。这些产品将在不久的将来会出现在B2B的开源站点,希望给大家带来一些帮助。
本次演讲视频录制于QCon杭州2011。
淘宝无线Android客户端架构设计思路汲取了移动平台上大型跨平台应用开发的经验,同时借鉴于大型网站的web开发框架思路。且看淘宝客户端如何通过 Component Model, Web Plus来面对挑战。
4 条回复
关注此讨论 回复