过度工程,最初我知道这个词是在 Rod Johnson 的《J2EE Development without EJB》,随着阅历地增长,渐渐发现书中熟悉的场景也在身边再现了。
敏捷、还有设计模式,给一个团队带来了什么?
我之所以把这两个词放在一起讲,是因为我要说一件显而易见的事情,可是这样一件事情很多人又不愿承认。
团队,是有风格个性的;团队,也是有能力强弱的。不管你承认不承认,整体来说,我见过的绝大多数团队都还远不是精英团队,因此相对于某些公司成功的案例来说,我们有很多事是不适合做的。
敏捷强调了主动性,强调了沟通,事实上并不是身边所有的团队都能做好敏捷管理的,譬如一支过于年轻化的团队、一支基本由外包人员构成的团队,甚至一支连转 SDV 版本都要熬一个通宵的团队,我认为是不适合实施敏捷流程的,至少,大部分所谓的敏捷实践,都是走样的。
关于设计模式,我要说什么?
我可以在板书上用 Java 和 C++写出 GoF 23 种设计模式中的每个例子,也学习过 J2EE Core Pattern,可即便这样,又能代表什么?我的设计能力比起某些牛人来说还是差之许多,我依然在实际的软件开发中的某些情况下,避免使用它们。因为即便一个设计再过精巧,它依然是要和团队的风格和接受能力想匹配的,如果大家都很专业,我们自然可以把事情处理得非常漂亮;如果团队不能达到那么高的要求,那么,记得我们都是工程商人,公司也不以追求完美的代码为己任,需要的是一个一个项目坚实地交付,可以踏踏实实地赚到钱。
我觉得,“ 某一些精巧的设计,恰恰是以降低代码的可维护性为代价的”,当然这一句话在不同的团队场景中是不尽相同的。举例来说,一些回调方法、闭包的使用,还有庞大的职责链,都给代码的识别和单步调试带来困难。成熟的开发者,能够克服总想要引入新技术的冲动,并且不会在项目中去写只有少数人才能理解的奥义代码。
我相信会有很多人不认可我的观点,不过我相信并不一定要谁说服谁,有不同的想法经常是好事。
分布式对象,我们遇到了什么问题?
为什么使用分布式对象?看起来根本似乎是因为整个解决方案过于庞大了,软件工程中一切不好解决的问题都是由问题复杂引起的。分布式对象在 SOA 架构中有着不可替代的作用,也许遗憾的是我并不在这里强调问题的严重性,然后“ 啪” 扔出一个传说中的最佳解决之道。
不过开发人员天生的本事就是善于抱怨,分布式对象确确实实带来了许许多多的麻烦,最典型的“ 阻抗不匹配”,是说无论是人力成本还是性能成本,都把精力耗费在传输对象和领域对象之间的数据交换,甚至数据同步上面。
我还记得 Martin Fowlor 提到:“ 分布式对象的第一条准则:不要使用分布式对象”。
举一个产品的例子,我们所有的产品处理逻辑全部都要通过 SOAP 接口中调用别的部件来实现,本地是没有持久化了的产品对象的,随着这一堆的接口愈加复杂和庞大,许许多多开发人员都希望有一天产品能够同步到本地来,落到存储中,一些流程上的接口性能问题就迎刃而解了,而这些问题,都是自己给自己造的绊脚石。
是否有过度的架构和过多的框架代码?
架构的价值在于为常见的问题找到好的解决方案,而不是一心想要解决更复杂也更罕见的问题。
这里遇到一个矛盾,产品的发展过程中,系统架构确实是在不断调整的,这些事情现在就是由开发人员完成的。问题是这过程中,到底应该分析到怎样的粒度?
问题不是分解得越细致、考虑得越多越好。在细节上挣扎得越久,就越难以设计出简洁和清晰的方案来。复杂,是软件的唯一天敌。可是简单的设计,又会令人不住地担心,是否会带来不易解决的问题。
这是一个不可调和的矛盾。其中一个好的办法是做纵向切面,设计一个系统架构时,做一个合适位置的纵向切面,能发现一些潜在的问题,作为可行性分析的重要依据。最典型的例子是性能问题,这些问题到了项目中后期很容易就能将一个工程陷入挣扎的境地。
框架代码是没有净生产力代码的一种。产品的代码,就像一只羽翼丰满的鸟,当这只鸟儿的骨架过于庞大,便难以飞远、飞高。简单的框架代码,意味着较高的可理解性,整个工程对新员工来说,是清晰的,易于业务技能的传递。
糟糕的可选性功能
我们经常听到这样的对话:
—— 请确认这个功能需要实现吗?
—— 这个功能目前需要实现,但必须考虑到未来 xxx 的情形,保持灵活性,请做成配置项:1 表示开启;0 表示关闭。
于是,我们就有了漫山遍野的配置项。
我认为,这是最糟糕的回答了。我们不是在做一个刚好交付的产品,而是在做一个超级“ 灵活” 的变形金刚,代码里面布满了配置项判断逻辑。
又是框架、又是平台……
框架、平台,这是很多学习 J2EE 的朋友最害怕听到的两个词语。身边似乎有无数这样的东西,产品能不能简单一些?使用硕大的框架和平台确实能够具备一些便利的功能…… 可是,我真的需要那么多吗?
开始理解代码了,有数种不同格式的模板、不同类型的标签,甚至连会话,都经常需要操作多种类型的会话获取接口。一个问题出现,我们从一个模块定位到另一个模块,从一个抽象层定位到另一个抽象层,最后吐血地发现,不是我们的代码问题,是框架或者平台的问题……
另一个同类产品,就开始尝试采用简洁的框架来实现,也不打算搬迁到什么复杂的平台上了。框架和平台的使用,一定要评估好必要性和影响,能简化的实现就不要作茧自缚,软件的本身,WEB 应用的本身,是否就该追求简单的美?
又见可扩展性,到底要扩展到什么时候?
见过一些同事,写代码处处考虑灵活、兼容,已经易扩展,一个简简单单的类实现,硬生生地被拆成从接口到抽象类到策略接口到策略实现类到辅助类到工具类十几个类来完成,和可是结果呢?还没等扩展开始,项目就黄了;或者某心态居高的程序员看到了,看着不爽,想不明白,给重写了。
这类程序员拥有负责任的心态和充满热血的心肠,可他们需要健康的引导,否则这样的人会写出危害深重的代码,成为“ 简单问题复杂化” 最常见的一种表现形式。
我们需要优秀的代码结构,需要考虑未来的一二三,可是倘若能让简洁的代码刚刚好交付,让这些兴许成为庸人自扰的可扩展性等到需要的时候再重构完成,岂不更快哉?
引入新技术的纠结
程序员对技术的狂热本质是一件好事,但是在新技术的引进上,无论小大,必须有清醒的头脑。至少需要考虑这些问题:
(1)License?
(2)是否能对短期内项目带来有效提升,或者对可以预见的时期内给项目带来价值?如果提升不大,或者价值太遥远,宁愿放弃它。
(3)学习成本?
(4)问题定位成本?比如,我希望新技术有广泛的社区支持,有成熟的团队支持,有既有的成功应用,我可以拿到开源代码等等。
(5)性能、体验、可维护性等方面是否带来不可接受的负向效应?
……
分层、分层、再分层
一个通用的解决 WEB 应用的办法是分层,把 MVC 扩展,M 可以归类到不同的区域中,V 可以分摊到各类模板集合中,C 可以拆分成 Action-Facade-Service;还可以继续拆!这只是最简单的拆分,拆分永无止境,总想把这个项目所有的问题统一到一个通用的结构中去。
结果看到了诸多这样的代码:
class UserFacade { public User getUser(XXX) { return userService.getUser(XXX); } public void setUser(User user) { userService.setUser(user); } }
到处是这样的代码(往往伴随着尚无用的接口出现),薄薄的一层,没做什么有价值的事情,直接调用下一层的代码,除了“ 啰嗦” 和“ 多此一举” 我不知道还能用什么来形容它。
转测试的艰辛
ST、SDV 转测试发布版本,通用的瀑布模型,但是头轻脚重。设计阶段草草了事,编码阶段不断加班+延期,到了 ST、SDV 阶段,拼了命地改 bug,过基础功能测试用例、转测试发布,从 ST 两轮,SDV1 再到 SDV6,问题仍不见收敛,程序员大量的时间用来进行测试验证,走问题单的流程,可质量依然、似乎永远是个痛,可怜的程序员,怎么总要折腾在发布版本的边缘?
程序员还在没日没夜的干活,故事就不会停歇,也许有一天,大家都突然想,做项目应该是一件创造性的活动啊,不应该是这个样子的……
文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《四火的唠叨》
去掉下列项中不必要的部份:
注释、测试覆盖、分层、接口、帮助文档、需求文、框架、扩展、会议,
本来可以很美。
这如同一个女人,身高 170,如果体重在 110 斤以下,又不太瘦,是比较美的。不幸禁不住口,又不运动,结果长到 200 斤以上,纵有构成美丽身材的骨骼,却因赘肉让人怎么也联想不到美这个字。
那种薄薄的一层确实常见,但是如果有人反对它,估计很快被包括老大在内的多数人很快压下去。
因为大家容易相信教条而不愿意相信思考;愿意相信从来没见过的心中的大神却不相信自己。
愿意接受多而不喜欢少,不明白原来退步是向前。