RSS

Monthly Archives: May 2011

请及时sign off用户故事吧

敏捷项目以迭代划分增量式工作周期,以用户故事划分可被独立开发并交付的任务,以点数衡量交付工作量的大小。

Showcase不能取代客户sign off用户故事

迭代中的用户故事会经历“分析-开发-QA认可-用户认可”的pipeline,交付团队的目标就是按期计划并完成迭代内的故事。我们常常强调的是迭代计划、迭代内按优先级完成开发、及时的QA测试、及时地沟通问题和完美的showcase,但似乎最后那点睛一笔没有落在让全部故事得到用户sign off上。我想原因有几。

  1. 迭代内任务到QA测试通过就为止了,之后的工作是用户的,时间他们看着安排;
  2. 我们完成了故事的QA测试,才能轮到用户开始signing off这个故事,常常这时已经到当前迭代的尾声了,交付团队把重心移向了下一迭代的内容;
  3. 交付团队常常在迭代结束会做详细的showcase,展示完成的故事给用户来看,用户的称赞给了交付团队定心丸,我们也就不急着让客户sign off那些故事了。

在敏捷项目中,showcase可以作为向客户展示新开发的功能和周期性交流的手段,作为对这段时间完成工作的汇报,但它不能代替用户sign off,因为后者才是用户对完成故事的正式肯定。用户在看到showcase时的点头满意和用户自己详细测试之后sign off了故事的满意是不同的。sign off是故事在pipeline上的收尾,它里程碑式地标志着故事所描述的功能被如意完成并认可,未来任何时候返回头来看,这白纸黑字都是不会变的;而showcase或平时言语的讨论,说难听了都算不上是最终定音啊。

不及时sign off故事会直接影响交付

即便是双方信任度很高的团队,不能及时由用户sign off前面迭代的用户故事都是带来未来风险的因素。迭代式开发不同于瀑布模型的开发方式,前者籍以达到及时交付用户价值的优点,在于不断得到要求与反馈并及时交付部分的用户需求;而后者则试图在最开始就通过深思熟虑谈妥用户需求,并在最终一并交付它们。可见如果不在迭代结束就及时请客户承认需求的交付,拖到后来地新需求被实现时,旧的需求就将被历史无情地掩埋,而那时开发的故事在今看来则失去了意义。好在目前为止碰到的客户都很诚信,并信赖我们的交付工作,所以一切正常地前进着。

不过,在项目即将结束时,那些没有及时被sign off的用户故事带来了新一轮问题。在即将停止迭代、进入UAT阶段前,我们发现竟然有大量故事仍没有被客户sign off,交付项目眼看就到了开发结束但无法交出手的地步,因为客户不仅要sign off这些剩余的故事,还要进行全面的UAT,这样一来测试将很花时间。而我们当初只为UAT阶段预留了一周时间,也就是说,我们做回归测试之余只有等待用户的测试结果,而这个结果客户在这有限的一周内是很难拿得出来了;即便拿了出来,我们修defect的时间也所剩无几。这就是各迭代中没有及时催促客户sign off故事的恶果,我们自己将品尝它!

不幸如此了,有何良策

当客户不得不在最后一并signing off大量历史迭代的故事时,问题又来了,如何测试?要知道,最终的feature都是随各个迭代增量式开发出来的,历史过程中完成的故事所描述的feature已经很难从当前版本的应用中看到了全貌了,难道要一个个版本回滚去测试一个个悠久的故事么?那不行,也不值得。最终交付的是当前版本,岂有回滚到从前去测试之理?最终交付的是当前版本的feature,何必回到以前验证已经被更改或移除的东西?既然已经到了如今的境地,我们要向背负着大量未sign off故事的客户提建议,及时地,建议他们按feature来测试当前版本的各个功能模块,我们提供给他们最新版feature对应的故事列表以及收集起来的feature表述和测试用例。这样为客户省下处理历史问题的时间,并确保他们将注意力集中在最终交付的那些需求上,客户当然更愿意接受。

总结一下

  • 用户sign off用户故事是对交付团队工作的正式肯定,showcase不能取代它;
  • 及时sign off故事能及时确认交付的需求,增加交付团队迎接新需求的信心;
  • 及时sign off故事能节省项目收尾时验收的工作量和难度,为UAT腾出时间;
  • 在迭代结束时应及时地催促客户去sign off该迭代完成的故事,不惜有迫切感地push客户;
  • 当大量未sign off的故事遗留到项目收尾阶段时,向客户建议以feature为粒度来sign off,集中注意力到当前版本同时也避免浪费时间。

 

 
Comments Off on 请及时sign off用户故事吧

Posted by in Uncategorized

 

Command pattern 应用特点

命令模式是一个很有活力的设计模式,它的几项特点具有很好的利用价值,我们可以通过演变该模式为许多问题找到解决方案。

先教科书式地摆出命令模式的UML图,并有请模式中各角色登场。看过之后请一定记住,不能教科书式地背诵它们或使用它们!不能拘泥于教科书式的命令模式表述,发挥其特点和思路才是正途。设计模式提炼自问题又用来交流并重复解决问题,所以为具体的问题去理解并打造它吧。

1、批量执行命令

通过实现Command接口,各实现类实例可聚合在一起并被Invoker遍历,依次执行各命令的Execute方法,达到批量执行命令的目的。如果有需要的话,每个命令执行的返回结果都可在Invoker处收集到。

这里举参数检查的例子。如筛选面试者信息时,需要一一检查各关键参数是否满足要求:email要验证格式,年龄段要符合限制,各项评分达到相应要求等等。

  • 普通写法什么样儿?若干含有“ValidatingXXX”方法的if语句形成丑陋的排比句,凡是返回值出现false,一律终止之后的检查并取消后续的业务逻辑执行。
  • 命令模式带给你清净。对着图,Client把典型验证逻辑包含在若干ConcreteCommand对象当中,形成命令集合交给Invoker,再将面试者信息对象填入,由Invoker执行各命令的Execute方法分别去验证输入对象的参数,Invoker可随时观察到返回false的命令执行结果并终止后续逻辑。Receiver在上述过程中可以不必出现,因为还没有出现需要让其他某各对象特地做点什么的时候。

2、命令对象的存在感

狭义地讲,命令就是希望完成的意图。调用方法是达到意图的过程,可惜这个过程从期望执行到开始执行是瞬间启动的,方法执行完毕命令的效果也就结束了,烟消云散。当我们将命令寄居在对象上的时候,这个意图的生命周期可见了。

  • 从发布命令到命令被执行,对象可以等待,于是时间上的耦合被解开了。我们可以为创建的命令对象设置延时,让需要深夜执行的程序在白天就发布给命令执行引擎。
  • 从参数的接收、验证到命令执行,工作可以分派,所以对象实体上的耦合被解开了。我们可以为ConcreteCommand类对象指定多个Receiver,让它们分别完成参数的验证和意图的执行。

毫无疑问,面向对象的世界里,方法也可被视为对象。Function Object被多种编程语言支持着且不说,就连我所学的第一门编程语言——C语言,也能以函数指针的方式将意图指定并保留下来,之后执行。如果情况适合的话,也可以试用它们完成命令模式的思想,不过一个命令实现类还是在提供辅助方法以及实现统一接口上有更大的操作空间。

3、命令的执行对发布者透明

命令模式最基本的模型,只有commander类和若干command实现类,对象之间是1对N的关系。其中,commander对象负责在各种情形下发出各种命令,即同步或异步地安排command对象执行Execute方法;command对象在得到执行许可后,全权接管具体执行过程。

好处是显而易见的。就像掌控全局的指挥官,了解了各种命令能达到的意图,按需发出命令就是了,不需要知道是谁又是如何让这条命令落到实处。是不是想到了手握遥控器那一刻的自己呢?

命令模式对“事件-响应”模型有良好的支持。我们常使用dispatcher将响应函数与事件特征相关联,如MVC中由router将Request映射到Controller某方法上,将对响应函数的调用换作命令的发出,就换到了命令模式的思维上。综合上一节对命令对象的阐述,

  • 我们可以在命令发出时立即调用其Execute方法,这与直接调用响应函数等效;我们也可利用命令对象的长命,以异步方式推迟执行命令的方法;
  • 我们不需要在dispatcher上就知道要调用哪个对象的哪种行为作为响应,只需要知道发出那类命令。
  • 我们可以利用命令对象对外暴露接口的一致性,借用命令对象作为Wrapper包装个性不一的响应函数,dispatcher更轻量和整洁。

4、命令的追踪和undo

命令模式以对象为粒度管理命令,命令对象创建后并未立即执行,命令执行完毕后对象却仍未销毁。我们可将未执行的命令入队变成序列,将已执行的命令保存为历史,这样来追踪命令。

命令模式中命令被实现类所定义,为其关联除Execute接口方法之外的方法很方便,其中很有意义的一个思路是为可逆命令添加undo方法。前面说到可追踪已执行的命令,那么对一个全部实现了undo方法的命令序列,将每条命令执行后入栈,即可随时通过出栈并执行undo的方式实现回滚。(如果没有undo方法,那么实现回滚就意味着要从一个起点开始将之后到记录中某点之前的所有命令重新执行。)

用两个应用实例来说明它的优势。

  • 事务。如果将事务中的执行步骤切分为若干连续的命令,那么事务的开始就是这一命令序列的执行开始,事务成功完成的标志是最后一个命令成功执行,而事务失败后回滚则是从失败点开始将前面的命令按序列倒序执行undo方法直至开始。
  • 增量型操作的redo与undo。如使用Photoshop对图片做处理,每步操作都是一个Command,由对应的工具作为其Receiver在上一操作完成后的图像上做出修改,并如实记录该Command及相关参数在历史列表中。我们可以使用undo回滚当前一步的修改,或再使用redo应用它;当点选历史列表中的某项时,该项之后的历史操作被依次undo,即回滚到指定位置。

5、命令队列与Active Object模式

命令模式满足Active Object模式的六要素:

  1. 供外部访问的代理,能构造请求并入队——Client;
  2. 所有Active Object实现了统一的接口——Command::Execute;
  3. 待处理的请求序列——CommandQueue;
  4. 维护请求序列并安排处理队首请求的引擎——ActiveObjectEngine;
  5. Active Object中处理请求的实现者或实现方法——Receiver;
  6. 一种将结果向外传达的结果返回或函数回调方式——Receiver;

实现的核心思想是:在执行中的队首命令有能力将新命令加至队列尾;等待事件或资源(如时间片)的方式是以持续查询的方式不断将仍不满足执行条件的队首命令添加至队尾。还有,CPU时钟与实时时钟是不同步的,利用延时检查作为调度条件会出现一定不确定性,这种性质正是多线程系统的特点。

Active Object模式是实现多线程调度的一种典型方式。利用延时等待线程的时间片,在时间片轮转到时开始对应线程的临界资源检查,满足运行条件时唤醒线程。延时长短的设置可反映线程的非抢占式优先级。

Active Object模式可用来以单线程模拟多线程系统。这里对模拟的线程是有要求的,需要RTC(run-to-completion)线程。这里利用到了命令一经执行就一定得完成的性质,可以看到,多个命令其实是有同步完成执行的关系。这样只需单一的工作者线程,在不同时间段执行不同的任务,避免了为各个任务分别分配各自的运行时堆栈,这在内存受限系统中是一个不错的节约。