分类目录归档:工作

前端代码的阻抗失配

Impedance Mismatch(阻抗失配)经常被用来比喻服务器端面向对象代码和关系型数据库存储模型之间的关系,先有ORM技术,后有NoSQL(Document based,Key-value store),都是解决这个阻抗失配的方案。

最近在做的一个项目,具有丰富的前端UI,简单的后台逻辑。应客户的要求,采用ExtJS4作为前端技术,利用它极具交互性的UI和号称优雅的MVC框架,来构造应用的所有页面。

不落俗套,在应用最初搭建阶段,是和ExtJS相处的蜜月期,“优雅”的MVC框架和清晰的封装让人眼前一亮。但是随着项目的进展,在功能需求没有显著增加的同时,客户对于前端交互友好性的需求却与日俱增,不可避免地让前端构造UI及交互的代码日益复杂。

出于面向对象和封装的考虑,对于页面上大大小小的控件,我们基本上都做了封装,而且封装还是嵌套的,在大控件内部还封装着小空间。每一个控件基本上都是满足ExtJS的MVC模式,即对应至少View、Controller两个“类”。

同样出于对架构腐化的担心和对它的不断思考和优化,我们在不断地调整不同类的封装,对事件和行为做一一调整,力争做到合适的封装、减少重复代码、易于理解,从而达到更好的可维护性。

这样最直接的后果就是,在每次我们需要深入代码去解决一些问题时,要费上好一番功夫,从大控件找到内部的小控件,从一个事件发送者搜索到事件的响应者,才能确定出问题的代码可能藏在某个犄角旮旯。

对于封装如此良好、层次如此清晰的前端“面向对象”代码,我的反应开始是:这是否就是解决前端实现或者说架构最优的选择呢?可惜的是,我在Stackoverflow和Delicious上没有找到相似的讨论,倒是发现了很多对于JavaScript MVC的讨论。

以下是我自己的一点想法:

前端代码用ExtJS或者Dojo这样的rich library构建,用程序员惯常的面向对象思维和成熟的MVC架构来实现,无可厚非。但实现的过程与前端页面的渲染并无简单直接的对应关系。程序员在用熟悉的建模和框架时,不会简单地推演出到正在实现的控件及其行为在页面渲染后将以怎样的结果出现。这里需要框架自身的技术支撑和程序员对框架的理解,框架加载脚本的顺序、对MVC实现的风格,不一而足。

浏览器是从页面的开头到结束完成页面的渲染,中间包括脚本的同步或异步的加载,包括框架对于富UI控件的渲染。在页面初始渲染成功后,由框架接手完成未来发生的所有的UI交互,直至页面重新刷新。

如果把浏览器页面的渲染结果,或浏览器内存中的DOM看作是结构、样式和数据的存储,类似于关系型数据中逐行的数据,这与前端实现所采用的面向对象和MVC相对应,同样存在模式的不匹配,即阻抗失配。

不同的是,程序员不用太关心数据库如何存储数据,但不得不清楚不同浏览器如何渲染页面,及框架如何完成帮助后续的交互。

IFrame带来的Session问题

客户原来有个Web App系统A,我们要基于A开发一个系统B,但不希望B对A依赖太重,所以B被实现为一个独立的Web App(war)。A和B部署在同一个Weblogic server上,在A中可以导航到B,两个系统看起来像是一个系统。

有个小需求是,在B中希望显示一个页面,根据参数能展示出不同的信息。这个页面在A中已经存在,所以自然而然最快的方法就是在B中创建一个iframe来指向那个A中的页面,配以不同的参数,多快好省!

问题很快出来了,QA发现那个iframe中的A页面很快就会session timeout,然后会提示用户输入登陆用户名和密码,而iframe外的B session并未过期的。

问题在于A和B实际上还是两个Web App,各自有各自的session。因为不能简单地修改A的页面,让它保持session不会timeout,所以剩下的办法只好是控制B的session timeout时间要早于A了。还有什么更优雅的方法呢?

如果给我一次重新实现的机会,我宁愿自己画一个属于B的页面,而扔掉iframe。

另外一个需求是,A和B是各自独立的Web App对于用户是透明的,用A的用户登陆进来可以直接进入B,反之则不可以。解决办法有点tricky,A的contextRoot设为/,B的contextRoot设为/XXX,这样二者的servletContext为同一个,即可实现A把自己的loginToken放到servletContext中后由B访问到,从而在请求B页面时,检查是否能取到servletContext中的loginToken,如果不能则转向到A的登陆页面,这点由Filter来实现。

事关挪卡(Moving Story)

最近我的team频繁遇到关于挪卡与否的事情。

可能会包括下面其中一种情况:

  1. 一个Story在前一个迭代做完了,但是客户没有及时sign-off,导致后一迭代中客户发现Sotry实现有问题,不是自己想要的东西,需要重新实现。
  2. 一个Story在前一个迭代做完了,客户已经sign-off,但在后一迭代中我们发现实现中设计有问题,会引发其他的bug。
  3. 一个Story在迭代中实现完毕,进入In QA,但发现有重要的AC没有事先提出来,导致需要补充实现。

可能还会包括其他可能的情况,不一一列举了。

我们team的对策分别是:

  1. 新开一个Story卡,对有问题的部分重新实现。这里的问题在于没有及时从客户那里取得反馈,原因自然有多种,你懂的。
  2. 新开Defect卡,对发现的问题进行修复。
  3. 直接在当前迭代中,把Story卡挪回In Dev,补充实现。但不会修改点数。

Pair时的心态

为什么我们需要pair programming,以及pair有什么好处,不是这篇文章要说的内容,关于这些方面,网上能找到的太多太多。这篇只是想说说对于pair中心态的问题,我在pair时的体会。

对于有过开发经验的人来说,离开几年来一直专属自己的那个格子间,坐到一张开放会议桌边写代码,旁边还坐着自己的pair,度过半天甚至一天的开发旅程,并不见得都是一帆风顺的过程。

每个人有自己的开发经验、背景和知识,成为pair就要为Story的代码质量负责。怎样合力以解决问题产生代码,是需要彼此Open心态,针对问题解决问题,而不是:

  1. 妄自尊大,容纳不下对方的观点
  2. 对对方不闻不问自顾自操作机器
  3. 炫耀自己的知识面而鄙视对方
  4. 甚至出言不逊,伤了对方还浑然不知

这是一个成长的过程,也是一个自我剖析让自己更open的过程。所以我们一般会安排由具有丰富pair经验的老同事带着新同事一块来pair,还会有乒乓和keyboard-mouse方式来控制pair的节奏。在知识传递的同时,逐渐打开每个人闭锁自我的心态,逐步提高pair的效率从而提高team的velocity。

从小处看,这是个人心态改变的过程,从大处看,这是开发文化不同导致的诉求。

Automation Training For TW QA

今天作为酱油党之一(徐X语,还有小刀和LeoShi同行)参加了徐X为TW QA开的Automation Training。

对于Ruby小白的我,跟着QA一起学了一把。

  • Ruby的包机制,应用的配置环境
  • Selenium RC的原理,包的四个变种。
  • 在07年就提到过的Page Object

自动化测试具备怎样的特质,才能最大限度的减少QA手工测试的工作量?

  1. Effective
  2. QA manageable

最后,徐X介绍了几个月前他研究出的Data Flow大法。简言之,测试不是要去测试事情本身,而是要测试事情结果的确发生。听着象废话,但那张精美手绘图却惊艳得不行。用以作为切分并归纳测试案例的依据。很受用,争取撺掇一篇文章出来。

OO训练营第一课

今天下班前,开始第三届OO训练营的第一课。

本来觉得应该没什么,结果课堂上大家窘态尽出,会写代码不代表面向对象代码写的好,事先不写测试,直接上实现,情何以堪呐。

一些重点:

  • 代码是写给人看的,不是给机器的。代码是用来表达和传递知识的。
  • 对于可度量的对象,有几个属性(比如相等性和可描述性),可以作为分解任务的依据,并据此编写测试。比如长度(Lenght)的Equality,如何判定是同一个长度对象,如何比较不同的长度对象。
  • 测试优先开发,划分出可测的任务是关键,即所谓Testability Driven Tasking。
  • 模块化最重要的是封装实现的细节,对类细节的访问要封装在类上下文中,这是最最容易忽视的bad smell。

TWI is Over

Today, we finished 3-day TWI(ThoughtWorks Immension) in ThoughtWorks China, which is also the sixth TWI here.

4 people from Xi’an office also came with us, and we spent a good time together. I have now a deep impression on the company culture, Agile Methodology, and a more closer observation on the story things and xp practices. All of what we saw and heard differentiate a lot from what we did before in a more traditional way. This introduced too much dicussion and questions, which even blocked the progress of courses.

Good to see this happened, when TW China grows up and attract more and more experienced people, it has to embrace and absorb those who are willing to join after survived from bad software engineering career, with its own special culture. I believe these people are here not wishing to get satisfying salary or anything, but to see how and why software engineering works here and people are happy at the same time.

I am shocked totally when teacher told ThoughtWorks company is actually a social experiment by Roy Singham, founder of company. I believe I will be shocked time and time when I understand more about company.

I am very pleasure to be the witness for this experiment is ongoing, and hopefully it will be successful.

Pair小记3——争论

Team的人员组成各不相同,有可能是经验丰富的团队,也有可能有将近半数的新人。这样对于有交付压力的项目来说,采用怎样的技术栈会有不同的观点。有保守起见,不愿意引入复杂框架的观点,也有要努力利用成熟框架学习新技能的想法,何去何从?

Well,这是一个team,自组织的团队,区别在于这样的争论可以让整个team的人来加入来选择,来承担可能好可能坏的结果。

更有帮助的是,可以咨询具备丰富实践经验的来自团队外的专家。然后还是由团队自己来决定。

自组织的团队让我很消受,Who is Boss? Who care!

Pair小记2

今天Pair遇到的问题是,我跟我的Pair在某一个问题上纠结太久,没能找到解决的方案。这是一个很典型的问题。

浏览器兼容的问题,历来让前端开发头疼。再加上是遗留系统,页面上引入了太多了三方和自己的js导致IE下页面无法显示。

看得出,我的Pair对前端开发经验不多,而我又不能一下子指出问题所在(可恶的IE),Pair自信心比较高,会很坚持用自己的方式来解决,他试图修改一些JS的内容来减少业务逻辑的涉入,以测试能否在IE下显示完整。而根据我的经验,这种IE特有的问题多半是某些JS语法的(不兼容)问题,但办法只有从上到下一个一个JS的排除,直到找到问题JS。Pair的做法引入了业务逻辑变化的不确定性,背离问题本身,反而容易引入更复杂的变化。

可惜我对Pair时所应有的态度和精神体会不深,尝试过说服Pair,但见他坚持,不忍冲突,结果是时间白白浪费。

回家路上跟彦辉君聊到此事,他的建议是:
1. 寻求技术帮助
2. 正义提出自己的想法
3. 如果2不可行,寻找更具说服力的力量

还是需要一种Open的态度。

Pair小记

今天跟凡哥pair,遭遇CXF的蹂躏。客户遗留系统糅杂了JAXB,CXF诸多Java EE和web service“高端”特性,调试很难,好不容易找到负责(反)序列化的XmlAdapter实现,才看到遗留系统工程师编写的奇怪正则表达式,过滤了本不该过滤的字符串,导致web service接收端接收数据不完整。

一边pair,一边感叹,Java的企业应用现在如此复杂,Java技术发展已经背离了最初解决问题的初衷。Java出路在哪里呢?