程序之外

难得有空,写篇程序之外的文章,关于压力的,也是自己近来的亲身体会。

众所周知软件这行压力是很大的。各种各样的问题层出不穷,每天上班工作内容都是排的满满的,遇到突发问题就得加班。如果不及时进行疏解,积累到一定的程度,就可能产生一定的负面问题,比如上班精神状态差、注意力不能集中、遇事喜欢逃避等等。我就亲身经历了这样的状况,明知自己工作积极性差、效率很低,但也很难一下子找回自我。

一次偶然的出游让我从中很快走了出来。一个亲戚考上厦大的博士,我请了 2 天的假,利用周末时间顺便去厦门旅游。厦门依山傍海,的确是旅游的好去处。晚上到海边,凉风习习,光脚沿着沙滩走过,任起落的潮水在腿上脚上留下层层薄沙。内心也变得平静,能够感受到海的呼吸。天地间仿佛只剩下我和大海,在进行心灵的交流,俗世烦扰皆抛诸脑后,只剩下对海的依恋。白天去爬南普陀山,并不太高,慢慢爬到山顶,整个思明区尽收眼底,远处一艘快艇在海面掠过,留下一条美丽的浪花。然后顺山而下到植物园,途径无数奇花异草、层天老树,走得累了,找个湖边石凳休息一下,人也觉得轻松愉快。

经过大自然的洗礼,回到单位,人的精神面貌焕然一新,抱着积极的心态处理事情,很多问题迎刃而解。压力测试做的很累,经常要熬夜,但通过一轮轮的测试,逐步定位到性能问题所在,自己也学了不少相关知识,想想也就没那么烦了。

做事的方式,也有了长进。我现在信奉人一时只做一件事效率最高的原则。事情再多,也是一件件做,每天安排好近日的工作,并排个优先级,什么是要亲自处理的,什么是让别人处理的,什么是需要预先通知他人的,需要什么资源,每件事情的预计时间如何,需要如何 check 等等。做好一件事就打个勾,做到心中有数。如果事情有延误,分析是什么原因,该如何补救,而不要有太大的心理负担,自己尽力了就好,是自己的责任就要勇敢扛下,死不了人的。这其实是很简单的原则,谁都能够学会,但的确很管用。

总结:压力是无处不在的,关键在于如何应对和排解。用积极的心态和恰当的方法面对,压力也就没那么大了。感觉压力积累到一定程度,在还未影响正常工作之前就先想办法排解,出去旅游、运动等都是缓解压力的好办法。

posted @ 2006-11-06 22:27 pesome 阅读(1032) | 评论 (2)编辑 收藏

关于代码的一些比喻

最近对项目组的一些较差的代码进行了些重构,同时灵光一闪,对代码有些比较形象的比喻。

坏的代码就象揉面团,管什么接口什么实现全揉成一团,一个方法几百行,注释写再多也是面团(夹了些小纸条而已)。然后需要重用了,就是从中抓起一把面团,然后放到其它的面团里继续揉。这样重复代码一堆,什么易读性、扩展性、可维护性都是无从谈起。

好的代码就象堆积木,接口实现定义清清楚楚,每个接口只做一件事情,重复代码都是通过更细的接口来消除。重用就是把积木块往该放的地方堆,这样的代码,几个大块几个小块一目了然,只要方法命名规范,连注释都可以省去。这样耦合性低,易读性、扩展性、可维护性都可以得到保证。

把面团变成积木并不复杂,定义好模具,面团一团团往里面填充,待稳定下来,就成了一块块积木。这里关键就是模具的制作,推荐制作宝典: martin fowler 的那本重构。还得有模具的丈量工具,就非 junit 莫属了。

posted @ 2006-10-30 17:42 pesome 阅读(1304) | 评论 (0)编辑 收藏

数据库同步trigger的记录

    项目需要写了几个数据库同步用的 trigger ,就是记录用户的操作到一个 temp 表,然后每天通过 webservice 同步到其它系统,同步成功清空该 temp 表。自认为写的还行,做个记录。是 db2 的。

 

-- 用户组新增触发器

--DROP TRIGGER TG_USERG;

CREATE TRIGGER LIBING.TG_USERG AFTER INSERT ON LIBING.TM_USERG

  REFERENCING NEW AS NROW

  FOR EACH ROW

  MODE DB2SQL  

  BEGIN ATOMIC

 

  declare @groupId integer;

  declare @name varchar(30);

  declare @descn varchar(100);

  declare @syntype varchar(4);

  declare @ddlsql varchar(1024);

  declare @isprimary char(1);

  declare @updateTime timestamp;

  declare @createTime timestamp;

  declare @createBy integer;

  declare @updateBy integer;

  declare @groupType integer;

  declare @adminType integer;

  declare @appId integer;

 

  declare @oldGroupId integer;

 

  set @groupId=NROW.GROUP_ID;

  set @name=NROW.name;

  set @descn=NROW.descn;

  set @syntype=NROW.syn_type;

  set @ddlsql=NROW.ddlsql;

  set @isprimary=NROW.isprimary;

  set @updateTime=NROW.update_time;

  set @createTime=NROW.create_time;

  set @createBy=NROW.create_by;

  set @updateBy=NROW.update_by;

  set @groupType=NROW.group_type;

  set @adminType=NROW.admin_type;

  set @appId=NROW.app_id;

 

  INSERT INTO TM_USERG_TEMP(GROUP_ID,NAME,DESCN,DDLSQL,ISPRIMARY,UPDATE_TIME,CREATE_TIME,

              CREATE_BY,UPDATE_BY,GROUP_TYPE,ADMIN_TYPE,APP_ID,ACTION) VALUES (@groupId,@name,@descn,

               @ddlsql,@isprimary,@updateTime,@createTime,@createBy,@updateBy,@groupType,@adminType,@appId,'INSERT');

  END;

 

  -- 更新用户组数据的触发器

 -- DROP TRIGGER TG_USERG_UPDATE;

  CREATE TRIGGER TG_USERG_UPDATE AFTER UPDATE ON TM_USERG

              REFERENCING NEW AS NROW

               FOR EACH ROW

               MODE DB2SQL

               BEGIN ATOMIC

               

               declare @groupId integer;

              declare @name varchar(30);

              declare @descn varchar(100);

              declare @syntype varchar(4);

              declare @ddlsql varchar(1024);

              declare @isprimary char(1);

              declare @updateTime timestamp;

              declare @createTime timestamp;

              declare @createBy integer;

              declare @updateBy integer;

              declare @groupType integer;

              declare @adminType integer;

              declare @appId integer;

               

               set @groupId=NROW.GROUP_ID;

              set @name=NROW.name;

              set @descn=NROW.descn;

              set @syntype=NROW.syn_type;

              set @ddlsql=NROW.ddlsql;

              set @isprimary=NROW.isprimary;

              set @updateTime=NROW.update_time;

              set @createTime=NROW.create_time;

              set @createBy=NROW.create_by;

              set @updateBy=NROW.update_by;

              set @groupType=NROW.group_type;

              set @adminType=NROW.admin_type;

              set @appId=NROW.app_id;

               

               -- 如果已经有 update 则只记录最后一条 update

               IF EXISTS(SELECT GROUP_ID FROM TM_USERG_TEMP WHERE GROUP_ID=@groupId AND ACTION='UPDATE') THEN

                     UPDATE TM_USERG_TEMP SET GROUP_ID=@groupId,

                                   NAME=@name,DESCN=@descn,DDLSQL=@ddlsql,

                                   ISPRIMARY=@isprimary,UPDATE_TIME=@updateTime,

                                   CREATE_TIME=@createTime,CREATE_BY=@createBy,

                                   UPDATE_BY=@updateBy,GROUP_TYPE=@groupType,

                                   ADMIN_TYPE=@adminType,APP_ID=@appId,ACTION='UPDATE'

                                   where GROUP_ID=@groupId AND ACTION='UPDATE';

               -- 如果有 insert 则把后面的 update 当作 insert

              ELSEIF  EXISTS(SELECT GROUP_ID FROM TM_USERG_TEMP WHERE GROUP_ID=@groupId AND ACTION='INSERT') THEN

                     UPDATE TM_USERG_TEMP SET GROUP_ID=@groupId,

                                   NAME=@name,DESCN=@descn,DDLSQL=@ddlsql,

                                   ISPRIMARY=@isprimary,UPDATE_TIME=@updateTime,

                                   CREATE_TIME=@createTime,CREATE_BY=@createBy,

                                   UPDATE_BY=@updateBy,GROUP_TYPE=@groupType,

                                   ADMIN_TYPE=@adminType,APP_ID=@appId,ACTION='INSERT'

                                   where GROUP_ID=@groupId AND ACTION='INSERT';

              ELSE      INSERT INTO TM_USERG_TEMP(GROUP_ID,NAME,DESCN,DDLSQL,ISPRIMARY,UPDATE_TIME,CREATE_TIME,

                              CREATE_BY,UPDATE_BY,GROUP_TYPE,ADMIN_TYPE,APP_ID,ACTION) VALUES (@groupId,@name,@descn,

                               @ddlsql,@isprimary,@updateTime,@createTime,@createBy,@updateBy,@groupType,@adminType,@appId,'UPDATE');

              end if;

               END;          

 

 

-- 删除用户组触发器

--DROP TRIGGER TG_USERG_DELETE;

CREATE TRIGGER TG_USERG_DELETE AFTER DELETE ON TM_USERG

         REFERENCING OLD AS OROW

         FOR EACH ROW

         MODE DB2SQL

         BEGIN ATOMIC

        

         declare @groupId integer;

        declare @name varchar(30);

        declare @descn varchar(100);

        declare @syntype varchar(4);

        declare @ddlsql varchar(1024);

        declare @isprimary char(1);

        declare @updateTime timestamp;

        declare @createTime timestamp;

        declare @createBy integer;

        declare @updateBy integer;

        declare @groupType integer;

        declare @adminType integer;

        declare @appId integer;

               

         set @groupId=OROW.GROUP_ID;

        set @name=OROW.name;

        set @descn=OROW.descn;

        set @syntype=OROW.syn_type;

        set @ddlsql=OROW.ddlsql;

        set @isprimary=OROW.isprimary;

        set @updateTime=OROW.update_time;

        set @createTime=OROW.create_time;

        set @createBy=OROW.create_by;

        set @updateBy=OROW.update_by;

        set @groupType=OROW.group_type;

        set @adminType=OROW.admin_type;

        set @appId=OROW.app_id;

        

          -- 如果没有操作记录,则插入 delete 记录

          IF NOT EXISTS(SELECT GROUP_ID FROM TM_USERG_TEMP WHERE GROUP_ID=@groupId) THEN

              INSERT INTO TM_USERG_TEMP(GROUP_ID,NAME,DESCN,DDLSQL,ISPRIMARY,UPDATE_TIME,CREATE_TIME,

              CREATE_BY,UPDATE_BY,GROUP_TYPE,ADMIN_TYPE,APP_ID,ACTION) VALUES (@groupId,@name,@descn,

               @ddlsql,@isprimary,@updateTime,@createTime,@createBy,@updateBy,@groupType,@adminType,@appId,'DELETE');

               

               -- 如果有 insert 记录,则整体结果相当于没有进行任何操作

               ELSEIF EXISTS(SELECT GROUP_ID FROM TM_USERG_TEMP WHERE GROUP_ID=@groupId and ACTION='INSERT') THEN

                            DELETE FROM TM_USERG_TEMP WHERE GROUP_ID=@groupId and ACTION='INSERT';

               -- 如果没有 insert 记录,则只需记录最后的 delete 操作

               ELSE

                       UPDATE TM_USERG_TEMP set ACTION='DELETE' where GROUP_ID=@groupId;

          END IF;

         

          END;

posted @ 2006-10-30 17:06 pesome 阅读(1306) | 评论 (0)编辑 收藏

关于重用

在项目中碰到一些重用上的问题,有些想法,就先写一点。

重用应该是高层的复用,逻辑的复用,接口的复用,而不是具体实现的复用。 我们项目开始讲复用,就是大家把别人的代码拿过来,可用的地方就用,不同的地方改改,结果问题一堆。说到底就是接口没有定义清楚的,很多该复用的逻辑隐藏在了具体的实现中。这样导致无法进行接口的复用,转而使用具体的实现复用。从程序员的角度看,他们总会使用成本最小的方法完成任务。所以我们要时刻思考如何能让最正确的方法在他们看来同时也是成本最小。

这里有一个较为简单的办法,就是尽量使用方法封装实现,使接口的粒度最小。如果一个实现需要几百行,且其中包含多个逻辑,就最好抽取出多个方法,然后在主体接口内进行调用。这样的代码逻辑清晰易读,可重用性也高。看看大师们对代码的不断重构,很大程度上就是重构出粒度最细,复用性最高的接口。

如何达到最大程度的复用,其实是非常复杂的问题,还需要在今后的项目中不断体会。

posted @ 2006-10-15 23:13 pesome 阅读(412) | 评论 (0)编辑 收藏

最近做压力测试的总结

最近做 portal 的压力测试,一个字“累”。其中犯了不少错误,白白加了几天班,也有一些体会,就记录下来,希望对大家有所帮助。

首先讲压力测试环境。这个很是关键,我们就是在这个上面吃了苦头。我们用的 loadrunner ,原理也很简单,一台主控机,控制多台客户机,模拟并发用户访问应用。然后需要能实时监控各相关应用服务器, ldap 服务器等的性能。这里每台客户机最好能使用同样的配置,使用足够带宽的网络,给予同样的负载(模拟同样数量的用户)。同时要注意监控客户机的 cpu 和网络状况,时刻保证 cpu 和网络利用率低于 100% 。我们犯的很大错误就是使用各自的笔记本,而且都使用的是一个 10M hub 牵出的网线,这样导致实际的网络阻塞,既没有给予服务器足够的负载,又导致报告的响应时间比实际更长,从而带来了后续很多的无用测试。

然后讲测试方法。用的较多的还是持续压力测试,就是持续给予服务器一段时间的并发量(一般为 5 10 分钟),然后看平均响应时间是否在可以接受的范围内。这个“可接受”要视应用类型和实际的并发用户而定,如何估计并发就要靠经验了。对于 portal 而言,由于要与众多的应用接口,如进行 SSO ,获取数据等,有很大程度也依赖于其它应用的性能,其性能要求不会太高。我们测试首页的性能,在放上全部的 portlet 的情况下, 100 个并发的平均响应时间就在 17s 左右,这肯定是不能接受的( 10s 只能算勉强可以)。接下来就是发现性能瓶颈,并尝试进行优化了。

初步的发现瓶颈的方法也很简单,通过对 portlet 的增减,发现最影响性能的 portlet ,然后不断优化,直至达到可以接受范围。发现瓶颈所在了,就得进一步确定是什么原因:是我们本身程序的问题,还是其它应用接口性能不佳等等。这里光靠猜是不行的,要讲数据讲事实,记录时间日志就是简单有效的办法,我们对各个时间点打印了日志,比如 doview 方法的全部执行时间, jsp 的载入时间,具体接口的执行时间等。有些接口可能在压力较小的情况下性能不错,而在大压力情况下出现性能隐患,所以一定要在进行压力测试后查看日志。我们就是这样发现了性能隐患,同时更进一步对各方面进行优化,直至达到客户可以接受为止。

由于不是专门的测试人员,很多地方都是实际项目中的体会,也没什么理论基础。有什么不对的地方,大家多交流。

posted @ 2006-10-15 23:13 pesome 阅读(2371) | 评论 (1)编辑 收藏

项目应该提倡的一些做法

接着昨天的写。今天写我认为的一个 javaEE 项目中应该提倡的做法。

1.       开发流程尽量简化,采用迭代增量的模式,做适合项目需要的文档。很多时候千言不如一图,原型开发我认为也非常重要。

2.       采用成熟的框架, ssh 组合或更多 full-stack 的框架如 seam 等都是不错的选择。如果一定要用公司的框架,至少 SA 要非常熟悉这个框架,在出现问题时要能快速的解决。

3.       对业务的分析做到越细越好,如果有条件让更多的开发人员参与业务的分析,同时形成项目通用的业务语言(实在不行,精简的 user story 也可以)。对于每个达成共识的业务都要能记录下来,并能方便的进行查阅。业务模型和业务规则要始终与当前需求、代码和数据库保持一致。

4.       在团队的建设上,需要更多的投入。不要为了节约成本,让很多程序员老后面才加入团队。一个稳定、团结、有冲劲的团队能比松散而人数更多的团队,完成的更快更好。然后要加强沟通,比如每天开个小的茶话会,大家交流下各自的工作情况,有什么困惑和疑难,提出来大家一起解决,避免大家各自做相同的逻辑(很多东西经过抽象可能就是一个)。在工作之余大家一块吃吃饭,打打游戏等都是增进感情的好方法,大家彼此熟悉了,工作上也能更好的协作。

5.       对程序员要有更高的要求, SA 有责任让程序员了解更多的东西,如面向对象的 5 大原则、一些模式、 junit 、重构等,这些其实并不是什么高深的东西,仅仅是掌握一些方面也能对代码质量和开发中的愉悦度产生很大促进。要激发他们对技术的热爱和对代码质量的追求,因为最终受益的还是他们。 XP 所提倡的结对编程也是快速进行知识传递的好办法。

6.       采用 wiki 进行项目进度跟踪和一些文档的展示。这次用 excel+cvs 的方式感觉很是麻烦,在 spring 翻译中我们采用 wiki 的方式就感觉很好。

暂时先想到这么多,有更多体会,再来补充!

posted @ 2006-09-21 15:59 pesome 阅读(922) | 评论 (2)编辑 收藏

用Maven做项目管理

     摘要: 在程序员发表的一篇maven文章,跟大家共享。用 Maven 做项目管理 在 Java世界中我们很多的开发人员选择用 Ant来构建项目,一个 build.xml能够完成编译、测试、打包、部署等很多任务,但我们...  阅读全文

posted @ 2006-09-21 11:37 pesome 阅读(18424) | 评论 (10)编辑 收藏

从错误中学到更多

开发进行到尾声了,但 bug 仍然层出不穷。总的来说,算是一个比较失败的项目,原因很多,有外在因素也有我作为一个 SA 不可推卸的责任。正好借加班的时间写点总结,也算是在失败总吸取教训,从错误中感受更多吧。

首先是开发流程。我是 xp 的坚定支持者,但在项目中由于外界原因还是采用了传统的开发流程,没有迭代,就是需求 -> 设计 -> 程序员进场开发 -> bug 。由于程序员进场时间较晚,一上来就开始开发,没有时间进行培训和团队的融合。然后开发中缺少沟通,就是一个人负责一块,开发完了再做其它。结果开发到现在,还有人不清楚我们项目的全貌,到底是为了解决什么业务。

然后是开发框架。使用了公司的框架,而我们作为 SA (我们是双 SA ),都是第一次接触,程序员也就一个人用过。我们最早是达成共识采用 SSH 的组合(我至少还算是了解吧,其它人也都用过),但由于上层因素没有实施(这也导致我好长一段时间进入不了状态)。开发前期大家都在探索这个框架(的确很难用,出错机制较差,配置文件很多,耦合较强 ... ),在一堆莫名奇妙的问题中摸索前行,花费大量的精力。而比较搞笑的是,在大家开始学习这个框架之时,我作为 SA ,因为要写一堆只为应付客户的设计文档(后面就没人看过),错过了和大家共同进步的机会,后面总是感觉“低人一等”。

在业务方面也存在很多问题。很多业务逻辑并没有以很好的载体保存下来,在需求文档中很多逻辑并没有体现。我维护了一套 pd 的业务模型,从概念模型 -> 物理模型 -> 数据库,这解决了后面的一些沟通问题,但由于更多体现的是静态的实体及关联,对于一些动态的业务流程没法体现。我们 SA 之间有时在一些问题上的理解还存在分歧(讨论过也达成过共识,但没有记录下来,后面可能就忘了),程序员就更是无所适从。谈到这,我更感受到 DDD 这本书的价值,他所提倡的开发人员参加模型的讨论,形成项目的模型语言,并不断随着业务进行演化。。。好多理念都是项目经验的结晶啊。

在开发管理上我也是无所作为。 Junit 都没有推广下去,更别说 TDD 了,这也与框架相关,它就没提供写 test case 的地方,等我搞明白一堆配置文件,做出脱离 web 容器的 test 框架,都开发一大半了,说起 test 的好处,大家也表示不理解(或者表示理解但没时间 = 没理解),就让他们慢慢 debug 吧!代码的质量也没有保证,程序员不明白代码的味道,更别说理解重构的意义以及进行恰当的重构了。一个函数写上 100 多行,什么逻辑都混在一块,但由于时间较紧,我也只好睁一只眼闭一只眼“功能完成就行吧!也不是我一个人在管”,到现在很多代码混成一团,展现层直接调用 dao (又是框架惹得祸),相同的逻辑 copy n 处,我也是后悔莫及。

今天先写失误,明天写从中学到的东西,从错误中学到的也许更多!

posted @ 2006-09-20 21:24 pesome 阅读(797) | 评论 (1)编辑 收藏

对领域模型的认识

最近看了看领域模型驱动这本书,只看了前面几章,但也深切的感受到了模型的重要性。通过与代码同步的模型,能够维护一个很好的知识共享的空间,包括设计者与程序员之间,客户与设计者之间 …… 而且模型应该尽可能简单,让不同背景的人都能够很快学会,并都能对模型有所增益。

那么这个模型应该是什么样的?书我没有细看,只说说自己的体会。关于设计,很早就有数据驱动和对象驱动的提法。在 Without EJB 里, Rod 也有讲:数据驱动或者说面向数据库设计更成熟,工具更多;而对象驱动更符合面向对象程序的特性,但由于掌握的人较少,风险较大。而通过模型驱动,我认为很大程度填补了 2 种方式的鸿沟,核心是模型,具体是对象模型还是数据模型并不重要,重要的是这个模型能够与需求、代码、数据库保持一致。

说到这里,顺便谈一谈我对文档的理解。我一直是 XP 的坚定支持者,甚至有点偏执。而由于文档不易阅读和沟通,且经常会出现与设计和代码的脱节,导致其可读性更差,所以我一向对文档不大感冒,更倾向于使用代码说话。但在目前的公司项目中,由于更多采用传统的软件过程,我也写了很多的文档,包括需求规格说明书、概要设计文档、详细设计文档等等。从对项目的帮助来看,文档作用并不太大,或者说是付出收益比太低,更多的是给客户写的,而不是给程序员写的。从程序员的需要来看,他关心的是每个实体的属性和关联,核心的接口、输入和输出,页面间的跳转和数据流,然后有一个统一的框架和编程模式。我的体会是:如果以文档为核心,很难描述清楚这些东西,且难以应对变化。

而通过以模型为核心(项目现在采用的 power designer 的概念模型为基础),辅以适当的描述,既能够加快大家对项目的认识(程序员是后面才加入),又能够节省一些写文档的时间,更早投入开发。

说到 power designer ,我也比较惭愧。用了好久,一直只是把它当成看数据库的工具。项目一开始就是从物理模型入手,结果举步维艰。后面从概念模型入手,就感受到了它的好处。使用概念模型,不用考虑太多关联表、外键什么的,而是从实体出发,然后确定相互间的关联,是一对一、一对多还是多对多。然后自动转成物理模型,并直接与相应的数据库挂钩。从这点上看与从对象设计出发真的非常相似。其实这也是合情合理的,正体现了这个世界的统一性吧(物理学界不也在搞什么统一场理论的证明吗)。 Power designer 也做了 conceptual model, physical model, object-oriented model xml model 的自动转换,我现在还没全部摸熟。

openfans 则是从对象入手,并通过 hibernate 建立与数据库的联系,也体现了一定的方便灵活性。但比较糟糕的是,只有代码和配置文件,没有清晰的便于交流的模型,谁要想参与只能先去慢慢看代码。所以我先通过 together reverse 出来一个类图,然后适当加以文字进行说明。类图已经做好,但比较乱,还需要更多的图例加以说明。文字说明就是下一篇 blog 的工作了。也算是预告吧!

posted @ 2006-08-13 17:41 pesome 阅读(3462) | 评论 (3)编辑 收藏

项目感悟

近来在一个项目做 SA ,也是第一次做比较大的项目的设计,感觉比较吃力。同时又要参与 spring 文档的翻译,一直没时间写 blog 。今天终于有点时间,就写一下最近的感悟。

首先是不适应。要参与需求阶段,因为需求初期并不确定,客户都不清楚他们需要什么东西,只是有一个很模糊的概念。我们得不断调研、讨论、出方案、出原型 …… 而这都是我比较不擅长的。还好有个职务较高的老大带着我们,才能逐渐把需求理顺。我也从他身上学到不少,准备写一篇“如何做需求”,但毕竟是第一次做较大的需求,理解还不很深刻,怕贻笑大方,所以只拿 MindManager 列了个提纲。

其次还是不适应。项目开始好几个月,没写过一行代码。项目没有采用 XP 的方式,而是普通的瀑布。需求就做了几个月,然后做概设、详设。我是 XP 的支持者,所以对这种方式持反对态度,但老大不同意,没办法!写文档,我也是很不情愿,但转念一想: Rod Without EJB ,但他 ejb 的理解比谁都深,什么方式都实践下可能更好。由于同时在看 Joel on software ,他对需求规格说明书却很是强调,我也就听听大师的话,好好写需求,顺便把他的一招用上了 ------- 写的有趣点,就当写故事吧。

最后还是不适应。以前做程序员,可以好好研究很多东西,现在不行了。有个 xml bean 转换的技术要解决,我能研究不?不行,我得写文档,这种比较 detail 的事情得给程序员做。看着程序员兴高采烈的比较各种开源工具,最后选定 JIBX openfans 发挥了一定的作用),然后跟我讲这个如何如何好,我只有附和的份。

讲到这里,让我想到一则小故事:有一个学钢琴的拜一个牛人为师。牛人交给他一个曲谱,说:“回去练好,一个月再过来。”他好歹把这个曲练熟了,还想展示一下,牛人又交给他一个更难的曲谱,又是同样的话。他只好回去继续苦练,每次都感觉不适应。这样往返多次,他忍不住了,问牛人:“你是不是故意整我,每次都给我更难的,还不给我表现的机会”。牛人让他把上次的曲弹弹,他感觉不错,让弹再上次的,更是轻松,最后弹第一次,他弹的是出神入化。他明白了!

大家都明白没:只有不断的感到不适应,才能进步。如果一切感觉良好,没什么挑战,就该考虑。。。。。。(此处省略 2 字)了。

posted @ 2006-08-11 18:34 pesome 阅读(950) | 评论 (3)编辑 收藏

仅列出标题
共5页: 上一页 1 2 3 4 5 下一页 
<2019年8月>
28293031123
45678910
11121314151617
18192021222324
25262728293031
1234567

导航

统计

公告

主要记录作者在学习java中的每一步足迹。除非特别说明,所有文章均为本blog作者原创,如需转载请注明出处和原作者,如用于商业目的,需跟作者本人联系。
欢迎大家访问:

常用链接

留言簿(16)

随笔分类

随笔档案

文章分类

文章档案

相册

收藏夹

java技术

人间百态

朋友们的blog

搜索

最新评论

阅读排行榜

评论排行榜