??xml version="1.0" encoding="utf-8" standalone="yes"?>
truelyg的设计定义:设计的过E就是将事务处理抽象成计机模型的过E?
1. 首先要明白设计远比编E重要?
2. qx注重训练自己的思维严}性和从全局考虑问题的能力。徏立冷静思考问题的处事态度?
3. 设计Ӟ其是数据库设计Ӟ不要完全被规矩约束,设计好比作诗Q懂得韵律是对的Q但完全被韵 律所束缚Q就作不出好诗了?
4. 多做设计Q经常ȝ自己的不之处和成功之处Q向他hh?
5. 专门L别h设计的漏z和不Q也是提高自p计水q的重要手段?
Q记住:q个好方法不要顺便外传,自己知道p了,d-:Q?
6. l验是重要的Q但如果观念老化而不善于ȝ提高Q所谓的l验成为束~自p步的枷锁?
7. 学好数学特别是理论数学如数学分析、运{学、数学模型等。多玩策略性经营游戏也是有益的。推?《帝国时代》和《模拟首?000》以及《大富翁4》。(但不要沉陷在里面Q?
8. Ҏ目情况和开发^台工L特点定最佳的设计Ҏ。模块化设计Ҏ和面向对象设计。两U设 计方法的l合使用?
9. 复杂无序的q程用模块化的方法进行分解,但要注重事务间的联系Qƈ且用开攄眼光去设计?
10. 设计时对严}性、灵zL、开发效率、客戯求四个方面做衡量取舍?
11. 设计时还要根据整个工E的q度安排和客户对软g的要求而决定是否设计得_灉|和严谨?
12. 复杂而无条理是最p的设计Q简单实用ƈ不一定是最好的Q但一定不是最坏的。(不要说我h哟)
13. 训练自己良好的表达能力,能用清晰明确而且单的描述表达q基本思\?
14. 在一个项目中建立l一的系l分析模式和文档模板Q同Ӟ一个项目中必须臛_有一个hҎ个系l?设计q行查和q行全局的考虑?
再谈如何成ؓ一个好的系l分析员Q?
bylsfboy
pȝ分析员基本功Q?
好的pȝ分析员都是从优秀的程序员中生的Q坚实的~程功底、丰富的l验是今后做pȝ分析的基?
没有对系l本w进行过透彻剖析q,很难领会到其中一些难以言q的_֍。但q不{于好的E序员就能够 成ؓ好的pȝ分析员?
合理的知识结构。语a能力、文字表达能力、技术的全面性等是对pȝ分析员的基本要求。比如说c/s? 层开发,如果仅仅对netscape公司的品熟悉还不够Q还需要了解比如微软等产品Qƈ且要了解他们中 生历?发展思\Q技术优劣,以应付各U穷q猛打的提问。但更重要的是,q是你ؓ应用定制技术要?的前提?
pȝ分析员思想Q?
全局观念是系l分析员必须具备的观c如果系l分析员设计时太注重l节Q往往会陷入在某个问题上纠 ~不清的泥潭。(93q_我论文指D师的一席话影响了我随后几年对Y件开发的理解----今后计算Z 来快Q多写几行代码少写代码无关紧要,最重要的是整体Q一开始就错了Q某个部份编得再好,也是 没有用的Q?
d隑ֺ的预能?
pȝ分析员要具备快速的d隑ֺ预测能力以及具备快速确定开发小lh员构成和d划分的能力。(?这条归为思想Q而不是能力)昆虫自然会长出翅膀Q而思想却需要长期的润。要做到q点Q需要大?的思考、学习。设计远比编E重要。当今Y件业的发展,各种开发工L出现Q编E已l不是什么问题, E序员的工作某种E度上讲是将别h现成的东西拼凑堆砌v来。系l分析员要清楚的认识刎ͼ现在大多?E序员没有学会怎么L体的了解一个系l,有些甚至不了解编E(q不是说他们不会写代码)。可视化 的开发工具加五花八门的控ӞE序员可以偷Ҏ了。(q可不是夸大Q我好几q的理工作Q接触过?量的E序员)Z技术,跛_框架。基于现有技术结合用户需求思考问题,设计时蟩出框架?
pȝ分析员思想Q?
pȝ分析员要有面向用L思想。系l分析员应当有能力将自己扮演成用P来了解要交付的项目看h 想什么样式,感觉想什么,从而了解用Lxq挑选出合理部䆾d发。从q个意义上说Q系l分析员 才能获得有意义的见解dg的开发组成员。系l分析员头脑中要寚w目结局有一个清楚的认识Qƈ?证项目不偏离方向。系l分析员要有Ҏ于技术,高于技术思考问题的思想。纯_的E序员通常Ҏl结 果考虑的不是很多,当一U新的技术在市场上出现时Q他们对能否按时交付的考虑比较少Q而强烈希?他们的计划能够徏立在新的技术之上。因此,pȝ分析员的x和行动要象一个用P又要能够站在技?的高度,成ؓ真正的用戗程序员之间的代a人?
pȝ分析员的关键
获得信Q。系l分析员最重要的素质是获得信QQ这是成ZUpȝ分析员的关键。成熟最为关键。成?可以为整个项目组提供正确的支?能够理解技术怎样才能解决用户的需求?
pȝ分析员的准备工作
l一的各U文档模式,q其中包括今后Y件变量、字D命名规则。我推荐用pb制定的规则做基础Q通过?造成为适合自n实用的标准。统一的文档管理。统一的分析Y件。比如说roseQuml太规范,国内的Y?理水^Ҏ用不上,只不q尽量应用,你自己对pȝ分析的理解有好处Q?Ҏ是思想的放映,在具体方法上׃多说了。我托h从u$a弄到几本书,用于面向对象pȝ开发的?用》、《面向对象的分析》、《项目管理》等都是很不错的Q推荐大家看看?
我在拙作"在中国没有h懂计机"里发了点牢骚Q听说挨了部份hQ习惯性的Q骂。其实,bbs本来是 发泄的地方,在这里从来就|有有内容的文章?
自从"l纳?登陆深圳后,大家更着g从宏观看中国的it业了。中国itq棵树Q说实在的,长到今天 实在是不Ҏ。一些h提出?反对微Y霸权"的口P不少人呼唤中?谷"的出现。微软的成功不是技 术的成功Q更多的是商业运作的成功。中国itq棵树能长多高,取决于他所植根于的土壤。而现在的事实是,q片土壤实在是太贫瘠了!如果按我们现在的思\和搞法,是长不成大树Q更别指望能l出"??Q?谷"q样丰硕的果实。如果说Q我们的软g技术落后美国十q_我们的硬件制造技术则落后国 二十q_我们的管理水q后美国至三十年。而最l决定发展速率的恰恰是我们的死I──低劣的管?水^。低劣的理水^的Ş成的原因有着深厚的背景和多方面的原因?
pȝ分析工作是解决一个问题的工作,目标是将一个对计算机应用系l的需求{化成实际的物理实?其中 复杂复杂在实际的面太多.在系l分析过E之中注意问以下的问?可能会所q行的系l分析设计工作有 帮助.
1)您所完成的系l目的是什?注意不是功能要求,而是目的.也就是ؓ什么要、ؓ什么要C?
2Q您所完成的系l有哪些斚w参与Q各斚w的初h什么?那些人可能在pȝ中v重要作用Q他?会采取什么样的态度Q你对他们有多少影响力?
3Q您的系l是否有一个明的评h标准Q最好从参与的各斚w都进行考虑?
4Q你的系l设计思想是什么?是否能够得到各方面的认可?
5Q你对参与系l设计开发的人员了解吗?他们的特长在哪里Q是否愿意与你合作,Z么?你对他们?_的媄响力吗?
6Q你的系l开发计划是否完善?你的计划表有明确的阶D吗QQ何一阶段都应该怎样完成Q如何对q一 阶段完成的情况进行评P
7Q你Ҏ采用的系l开发方法以及工h否熟悉?你的夥伴是否熟悉Q?
8Q你所完成的系l是否有原型Q计机的或者物理的?
以上的几个问题都是在pȝ分析以及pȝ规划时涉及到的,供各位参考?
pȝ分析工作是解决一个问题的工作,目标是将一个对计算机应用系l的需求{化成实际的物理实?其中 复杂复杂在实际的面太多.在系l分析过E之中注意问以下的问?可能会所q行的系l分析设计工作有帮助
1)您所完成的系l目的是什?注意不是功能要求,而是目的.也就是ؓ什么要、ؓ什么要C。在考虑pȝ目的Ӟ我更多的侧重于系l的最l目标考虑Q因Z个系l不可能一下子完美Qؓpȝ留些 余地?
2Q您所完成的系l有哪些斚w参与Q各斚w的初h什么?那些人可能在pȝ中v重要作用Q他?会采取什么样的态度Q你对他们有多少影响力?中国it行业的失败之一是?太年?Q一定要有领导的 支持Q否则完蛋。不要认己对他们会有多少影响力,即便有,也要可能的认ؓ是决{者再影响?们。在中国Q一个技术员Q你老几Q说到这里我很悲哀。哪些h在系l中起重要作用ƈ弄清楚他们的?度,q点十分关键?
3Q您的系l是否有一个明的评h标准Q最好从参与的各斚w都进行考虑。不知道q样说对不对Q在p?l徏设之前,对你的程序员、对你的领导要有臛_不同的两U评仗?
4Q你的系l设计思想是什么?是否能够得到各方面的认可。如果高明,寚w对{对E序员都采用引导Q?得到认可的最好办法,是让他们认可他们自qx。(我力图这样做Q但做得不好Q系l分析员有一 点要学会韬光LQ忍Q?
5Q你对参与系l设计开发的人员了解吗?他们的特长在哪里Q是否愿意与你合作,Z么?你对他们?_的媄响力吗?软g发展C定的E度Q不是编E,不是数学Q而是理?
6Q你的系l开发计划是否完善?你的计划表有明确的阶D吗QQ何一阶段都应该怎样完成Q如何对q一 阶段完成的情况进行评P
7Q你Ҏ采用的系l开发方法以及工h否熟悉?你的夥伴是否熟悉Q事实上Q不是每U好的工具都?使用Q也q不一定都要他们熟l掌握。提醒诸位一句,当你方案做得可以不依赖某个E序员,你在E序 员面前就无信daQ因Z此程序员受到更大的生存压力。我坚决不在公司使用rose?
8Q你所完成的系l是否有原型Q计机的或者物理的?
以上的几个问题都是在pȝ分析以及pȝ规划时涉及到的,供各位参考?
q文章很好,我的话是Q?需求分析实际应该是问题分析"。含义是pȝ要解决的是问题。而不是用h?的需求。经常发现系l完成后Q客戯"我的问题q没有解?。可是,需求分析稿上的目标都搞定了?
既然是问题分析,所以,熟悉目标pȝ的知识就是必要的。甚臻I可以_一个好的系l分析员也应该是 好的业务专家?
我很高兴在这里遇到许多分析高手,可以交流分析中的问题。我赞同从来的观炏V在中国作分析重要的?人气Q因Z国的企业U信息系l的在很大程度上可以说ƈ非确有需求,而是q于某种压力。用户在 很多时候考虑的不是系l的长远发展Q而只是短期的成果Q要求开发单位在很短的时间内完成一个很大的 pȝ的开发,没有旉对系l进行周密的分析Q在q种情况下,很多开发商׃_分析,_设计,快q?入编码阶D,q样的系l的生命周期肯定不会很长。说了这么多Q只是想_pȝ分析员确实应是业务和 理专家Qƈ且需要有很好的语al织能力Q他需要根据问题域中存在的问题d力说服用P引导用户 需求,毕竟Q我们是专家Q如果让用户늝d赎ͼpȝ不会是成功的pȝ。(当然了,q要建立在用?是可引导的前提下Q本人拙见?
在理解和分析用户的需求时Q应说服用户明白Q徏立计机应用pȝq不是简单地用计机代替手工?
作,它更应该是管理思想的一ơ革命,是现用户模式的一ơ升华和提高。如果系l不能高于现实,开发的pȝ长期陷入需求的反复修改Q其软g的生命周期也短了?
针对我对您的问题的理?试着作如下一般?理论性的回复:
需求分?您可以采用usecasedriven的方法进行需求分?在明需求分析的基础?定需要采用的pȝ分析Ҏ(l构?面向对象/构g?应用您的开发团队所定采用的分?设计Ҏ,q行pȝ分析.Ҏ您所采用的分析方?依次或反复进行系l设?建模.
]]>
MVMQ应该用。VSS、CVS、PVCS、ClearCase、CCC/Harvest、FireFly都可以。我的选择是VSS?br />
2. 你们的项目组使用~陷理pȝ了么Q?br />
MVMQ应该用。ClearQuest太复杂,我的推荐是BugZilla?br />
3. 你们的测试组q在用Word写测试用例么Q?br />
MVMQ不要用Word写测试用例(Test CaseQ。应该用一个专门的pȝQ可以是Test ManagerQ也可以是自己开发一个ASP.NET的小|站。主要目的是Track和Browse?br />
4. 你们的项目组有没有徏立一个门L站?
MVMQ要有一个门L站,用来放Contact Info、Baselined Schedule、News{等。推荐Sharepoint Portal Server 2003来实玎ͼ15分钟搞定。买不vSPS 2003可以用WSS (Windows Sharepoint Service)?br />
5. 你们的项目组用了你能买到最好的工具么?
MVMQ应该用量好的工具来工作。比如,应该用VS.NET而不是Notepad来写C#。用Notepad写程序多半只是一U炫耀。但也要考虑到经费,所以说是“你能买到最好的”?br />
6. 你们的程序员工作在安静的环境里么Q?br />
MVMQ需要安静环境。这Ҏ端重要,而且要保证每个h的空间大于一定面U?br />
7. 你们的员工每个h都有一部电话么Q?br />
MVMQ需要每Z部电话。而且电话最好是带留a功能的。当Ӟ上这么一套带留言电话pȝ开销不小。不q至每Z部电话要有,千万别搞得经常有人站h喊:“某某某电话”。《h件》里面就强烈谴责q种做法?br />
8. 你们每个人都知道Z问题应该找谁么?
MVMQ应该知道。Q何一个Feature臛_都应该有一个OwnerQ当ӞOwner可以l箋Dispatchl其他h?br />
9. 你遇到过有h说“我以ؓ…”么Q?br />
MVMQ要消灭“我以ؓ”。Never assume anything?br />
10. 你们的项目组中所有的人都坐在一起么Q?br />
MVMQ需要。我反对Virtual TeamQ也反对Dev在美国、Test在中国这U开发方式。能坐在一起就最好坐在一P好处多得不得了?br />
11. 你们的进度表是否反映最新开发进展情况?
MVMQ应该反映。但是,应该用Baseline的方法来理q度表:l护一份稳定的ScheduleQ再l护一份最新更攏VBaseline的方法也应该用于其它的Spec。Baseline是变更管理里面的一个重要手Dc?br />
12. 你们的工作量是先由每个h自己估算的么Q?br />
MVMQ应该让每个׃。要从下而上估算工作量,而不是从上往下分z。除非有其他原因Q比如政MQ务工期固定等?br />
13. 你们的开发h员从目一开始就加班么?
MVMQ不要这栗不要一开始就搞疲x。从目一开始就加班Q只能说明项目进度不合理。当Ӟ一些对日Y件外包必d天加班,那属于剥削的范畴?br />
14. 你们的项目计划中Buffer Time是加在每个小d后面的么Q?br />
MVMQ不要。Buffer Time加在每个Q务后面,很容易轻易的p消耗掉。Buffer Time要整D늚加在一个Milestone或者checkpoint前面?br />
15. 值得再多׃些时_?5%做到100%?br />
MVMQ值得Q非常值得。尤其当目后期人困马乏的时候,要坚持。这会给产品带来质的区别?br />
16. 登记新缺hQ是否写清了重现步骤Q?br />
MVMQ要。这属于Dev和Test之间的沟通手Dc面寚w沟通需要,详细填写Repro Steps也需要?br />
17. 写新代码前会把已知缺陯决么Q?br />
MVMQ要。每个h的缺陷不能超q?0个或15个,否则必须先解册的bug才能l箋写新代码?br />
18. 你们对缺L轻重~急有事先的约定么Q?br />
MVMQ必L定义。Severity要分1??Q约定好Q蓝屏和Data LostSev 1QFunction ErrorSev 2Q界面上的算Sev 3。但q种U定可以Ҏ产品质量现状适当q行调整?br />
19. 你们Ҏ见不一的缺h三国会议么?
MVMQ必要有。要有一个明的决策q程。这cM于CCB (Change Control Board)的概c?br />
20. 所有的~陷都是q记的人最后关闭的么?
MVMQBug应该由Opener关闭。Dev不能U自关闭Bug?br />
21. 你们的程序员厌恶修改老的代码么?
MVMQ厌恶是正常的。解x法是l织Code ReviewQ单独留出时间来。XP也是一个方法?br />
22. 你们目l有Team Morale Activity么?
MVMQ每个月都要搞一ơ,吃饭、唱歌、Outing、打球、开卡丁车等{,一定要有。不要剩q些钱?br />
23. 你们目l有自己的Logo么?
MVMQ要有自qLogo。至应该有自己的Codename?br />
24. 你们的员工有印有公司Logo的T-Shirt么?
MVMQ要有。能增强归属感。当ӞT-Shirt要做的好看一些,最好用80支的来做。别没穿几次q破烂烂的?br />
25. ȝ理至每月参加次目l会?br />
MVMQ要的。要让team member觉得高层xq个目?br />
26. 你们是给每个Dev开一个分支么Q?br />
MVMQ反寏VBranch的管理以及Merge的工作量太大Q而且Ҏ出错?br />
27. 有h长期不Check-In代码么?
MVMQ不可以。对大部分项目来_最多两三天应该Check-In?br />
28. 在Check-In代码旉填写注释了么Q?br />
MVMQ要写的Q至一两句话,比如“解决了Bug No.225”。如果往高处拔,q也做“配|审计”的一部分?br />
29. 有没有设定每天Check-In的最后期限?
MVMQ要的,要明Check-In Deadline。否则会Build Break?br />
30. 你们能把所有源码一下子~译成安装文件吗Q?br />
MVMQ要的。这是每日编译(Daily BuildQ的基础。而且必须要能够做成自动的?br />
31. 你们的项目组做每日编译么Q?br />
MVMQ当然要做。有三样东西是Y仉?产品开发必备的Q?. bug management; 2. source control; 3. daily build?br />
32. 你们公司有没有积累一个项目风险列表?
MVMQ要。Risk Inventory。否则,下个目开始的时候,又只能拍脑袋分析Risk了?br />
33. 设计简单越?br />
MVMQ越单越好。设计时候多一句话Q将来可能就带来无穷无尽的烦恹{应该从一开始就勇敢的砍。这叫scope management?br />
34. 量利用现有的品、技术、代?br />
MVMQ千万别什么东襉K自己Coding。BizTalk和Sharepoint是最好的例子Q有q两个作为基Q可以把L提高很多。或者可以尽量多用现成的Control之类的。或者尽量用XMLQ而不是自己去Parse一个文本文Ӟ量用RegExpQ而不是自׃头操作字W串Q等{等{。这是“Y件复用”的体现?br />
35. 你们会隔一D|间就停下来夯实代码么Q?br />
MVMQ要。最好一个月左右一ơ。传adq初Windowsl在Stevb的命令下停过一个月增强安全。BtwQ“夯”这个字念“hang”,W一声?br />
36. 你们的项目组每个人都写Daily Report么?
MVMQ要写。五分钟够了,?0句话左右Q告诉自己小l的Z天我q了什么。一则ؓ了沟通,二则鞭策自己Q要是游手好闲一天,自己都会不好意思写的)?br />
37. 你们的项目经理会发出Weekly Report么?
MVMQ要。也是ؓ了沟通。内容包括目前进度,可能的风险,质量状况Q各U工作的q展{?br />
38. 你们目l是否至每周全体开会一ơ?
MVMQ要。一定要开会。程序员讨厌开会,但每个礼拜开会时间加h臛_应该?时。包括team meeting, spec review meeting, bug triage meeting。千万别大家闷头写code?br />
39. 你们目l的会议、讨论都有记录么Q?br />
MVMQ会前发meeting request和agendaQ会中有责主持和记录Q会后有责发meeting minutesQ这都是effective meeting的要炏V而且Q每个会议都要Ş成agreements和action items?br />
40. 其他部门知道你们目l在q什么么Q?br />
MVMQ要发一些Newsflashl整个大l织。Show your team’s value。否则,当你坐在甉|里面Q其他部门的人问Q“你们在q嘛”,你回{“ABC目”的时候,别h全然不知Q那U感觉不太好?br />
41. 通过Emailq行所有正式沟?br />
MVMQEmail的好处是免得抵赖。但也要避免矫枉q正Q最好的Ҏ是先用电话和当面_然后Email来确认?br />
42. 为项目组建立多个Mailing Group
MVMQ如果在AD+Exchange里面Q就建Distribution List。比如,我会建ABC Project Core TeamQABC Project Dev TeamQABC Project All TestersQABC Project Extended Team{等。这样发起Email来方便,而且能让该收到email的h都收到、不该收C被骚扰?br />
43. 每个人都知道哪里可以扑ֈ全部的文档么Q?br />
MVMQ应该每个h都知道。这叫做知识理QKnowledge ManagementQ。最方便的就是把文档攑֜一个集中的File ShareQ更好的Ҏ是用Sharepoint?br />
44. 你做军_、做变化Ӟ告诉大家原因了么Q?br />
MVMQ要告诉大家原因。Empower team member的手D之一是提供够的informationQ这是MSF一开的几个原则之一。的如此,tell me why是h之常情,tell me why了才能有understanding。中国h做事喜欢搞限Ӟ限制信息Q似乎能够看到某一份文件的人就是有w䆾的h。大错特错。权威、权力,不在于是不是能access information/dataQ而在于是不是掌握资源?br />
45. Stay agile and expect change
MVMQ要q样。需求一定会变的Q已l写好的代码一定会被要求修改的。做好心理准备,对change不要抗拒Q而是expect change?br />
46. 你们有没有专职的软g试人员Q?br />
MVMQ要有专职测试。如果h手不够,可以peer testQ交换了试。千万别自己试自己的?br />
47. 你们的测试有一份ȝ计划来规定做什么和怎么做么Q?br />
MVMQ这是Test Plan。要不要做性能试Q要不要做Usability试Q什么时候开始测试性能Q测试通过的标准是什么?用什么手D,自动的还是手动的Q这些问题需要用Test Plan来回{?br />
48. 你是先写Test Case然后再测试的么?
MVMQ应该如此。应该先设计再编E、先test case再测试。当Ӟ事情是灵zȝ。我有时候在做第一遍测试的同时补上test case。至于先test case再开发,我不喜欢Q因Z习惯Q太ȝQ至于别人推荐,那试试看也无妨?br />
49. 你是否会为各U输入组合创建测试用例?
MVMQ不要,不要搞边界条件组合。当心组合爆炸。有很多test case工具能够自动生成各种边界条g的组??但要x楚,你是否有旉去运行那么多test case?br />
50. 你们的程序员能看到测试用例么Q?br />
MVMQ要。让Dev看到Test Case吧。我们都是ؓ了同一个目的走Ch的:提高质量?br />
51. 你们是否随便抓一些h来做易用性测试?
MVMQ要q么做。自q自己写的E序界面Q怎么看都是顺眼的。这叫做审美疲劳??臭的看久了也׃臭了Q不方便的永久了也就习惯了?br />
52. 你对自动试的期望正么Q?br />
MVMQ别期望太高。依我看Q除了性能试以外Q还是暂时先忘掉“自动测试”吧Q忘掉WinRunner和LoadRunner吧。对于国内的软g试的现状来_只能“矫枉必过正”了?br />
53. 你们的性能试是等所有功能都开发完才做的么Q?br />
MVMQ不能这栗性能试不能被归到所谓的“系l测试”阶Dc早早ҎQ早L升天?br />
54. 你注意到试中的杀虫剂效应了么Q?br />
MVMQ虫子有抗药性,Bug也有。发现的新Bug来少是正常的。这时候,最好大家交换一下测试的areaQ或者用用看其他工具和手法,又会发C些新bug了?br />
55. 你们目l中有h能说Z品的当前整体质量情况么?
MVMQ要有。当老板问vq个产品目前质量如何QTest Lead/Manager应该负责回答?br />
56. 你们有单元测试么Q?br />
MVMQ单元测试要有的。不q没有单元测试也不是不可以,我做q没有单元测试的目Q也做成功了??可能是oq,可能是大安是熟手的关系。还是那句话QY件工E是非常实践、非常工E、非常灵zȝ一套方法,某些Ҏ在某些情况下会比另一些方法好Q反之亦然?br />
57. 你们的程序员是写完代码就扔过墙的么?
MVMQ大忌。写好一块程序以后,即便不做单元试Q也应该自己先跑一跑。虽然有了专门的试人员Q做开发的Z不可以一Ҏ试都不做。微软还有Test Release Document的说法,E序太烂的话Q测试有权踢回去?br />
58. 你们的程序中所有的函数都有输入查么Q?br />
MVMQ不要。虽然说做输入检查是write secure code的要点,但不要做太多的输入检查,有些内部函数之间的参C递就不必查输入了Q省点功夫。同L道理Q未必要l所有的函数都写注释。写一部分主要的就够了?br />
59. 产品有统一的错误处理机制和报错界面么?
MVMQ要有。最好能有统一的error messageQ然后每个error message都带一个error number。这P用户可以自己Ҏerror number到user manual里面ȝ看错误的具体描述和可能原因,像SQL Server的错误那栗同PASP.NET也要有统一的Exception处理。可以参考有关的Application Block?br />
60. 你们有统一的代码书写规范么Q?br />
MVMQ要有。Code Convention很多Q搞一份来发给大家可以了。当Ӟ要是有FxCopq种工具来检查代码就更好了?br />
61. 你们的每个h都了解项目的商业意义么?
MVMQ要。这是Vision的意思。别把项目只当成工作。有时候要想着自己是在Z国某某行业的信息化作先驱者,或者时不时的告诉team memberQ这个项目能够ؓ某某某国安门每q节省多多百万的U税人的钱,q样有动力了。^凡的事情也是可以有个崇高的目标的?br />
62. 产品各部分的界面和操作习惯一致么Q?br />
MVMQ要q样。要让用戯得整个程序好像是一个h写出来的那样?br />
63. 有可以作为宣传亮点的Cool Feature么?
MVMQ要。这是增强团队凝聚力、信心的。而且Q“一俊遮百丑”,有亮点就可以掩盖一些问题。这P对于客户来说Q会感觉产品从质量角度来说还是acceptable的。或者说Qcool feature或者说亮点可以作ؓ质量问题的一个事后I补措施?br />
64. 可能羃短品的启动旉
MVMQ要q样。Y件启动时_Start-Up timeQ是客户Ҏ能好坏的第一印象?br />
65. 不要q于注重内在品质而忽视了W一眼的外在印象
MVMQ程序员Ҏ犯这个错误:太看重性能、稳定性、存储效率,但忽视了外在感受。而高层经理、客h相反。这两方面要兼顾Q协调这些是PM的工作?br />
66. 你们Ҏ详细产品功能说明书做开发么Q?br />
MVMQ要q样。要有设计才能开发,q是必须的。设计文档,应该说清楚这个品会怎么q行Q应该采取一些讲故事的方法。设计的时候千万别ȝ节,别钻到数据库、代码等具体实现里面去,那些是后面的事情Q一步步来不能着急?br />
67. 开始开发和试之前每个人都仔细审阅功能设计么?
MVMQ要做。Function Spec review是用来统一思想的。而且Qreviewq以后Ş成了一致意见,来再也没有人可以说“你看,当初我就是反对这么设计的Q现在吃苦头了吧?br />
68. 所有h都始l想着The Whole Image么?
MVMQ要q样。项目里面每个h虽然都只是在刉一片叶子,但每个h都应该知道自己在刉的那片叶子所在的树是怎么样子的。我反对软g蓝领Q反对过分的把Y件制造看成流水线、R间。参见第61条?br />
69. Dev工作的划分是单纯U向或横向的么?
MVMQ不能单U的Ҏ功能模块分,或者单U根据表现层、中间层、数据库层分。我推荐q么做:首先Ҏ功能模块分,然后每个“层”都有一个Owner来Review所有h的设计和代码Q保证consistency。?br />
70. 你们的程序员写程序设计说明文档么Q?br />
MVMQ要。不q我听说微Y的程序员1999q以前也不写。所以说Q写不写也不是绝对的Q偷懒有时候也是可以的。参见第56条?br />
71. 你在招h面试时让他写一D늨序么Q?br />
MVMQ要的。我最喜欢让h做字W串和链表一cȝ题目。这U题目有很多循环、判断、指针、递归{,既不偏向q于考算法,也不偏向q于考特定的API?br />
72. 你们有没有技术交讲座?
MVMQ要的。每一两个C拜搞一ơ内部的Tech Talk或者Chalk Talk吧。让l员之间分n技术心得,q笔花钱送到外面d训划?br />
73. 你们的程序员都能专注于一件事情么Q?br />
MVMQ要让程序员专注一件事。例如说Q一个部门有两个目?0个hQ一U方法是?0个h同时参加两个目Q每个项目上每个人都?0%旉Q另一U方法是5个h去项目AQ?个h去项目BQ每个h?00%在某一个项目上。我一定选后面一U。这个道理很多h都懂Q但很多领导实践h把属下当成可以L拆分的资源了?br />
74. 你们的程序员会夸大完成某工作所需要的旉么?
MVMQ会的,q是常见的,其会在目后期夸大做某个change所需要的旉Q以ơ来抵制change。解决的Ҏ是坐下来慢慢,掉E序员的逆反心理Q一起分析,q把估算旉的颗_度变小?br />
75. 量不要用Virtual Heads
MVMQ最好不要用Virtual Heads。Virtual heads意味着resource is not secureQshared resource会降低resource的工作效率,Ҏ增加出错的机会,会让一心二用的人没有太多时间去review spec、review design。一个dedicated的hQ要两个只能投入50%旉和精力的人。我是吃q亏的:7个part time的testerQ发现的Bug和干的活Q加hq不如两个full-time的。参见第73条?3条是针对E序员的Q?5条是针对Resource Manager的?br />
]]>
import java.io.*;
import java.util.*;
//*********创徏型模?**************
//factory method 1
//1具体的构造算法,?构造出的具体品由子类实现
interface Product {
}
//或者我也提供一个工厂的接口Q由q个抽象cLl承?br />
abstract class Factory {
abstract public Product fmd();
//我认个方Ҏ的存在是Q是对FactoryMethodҎ的补?br />//例如可以为生成的对象赋|计算为生成对象应付何|前后的日?br />//且这些都是公用的Q生成品的最主要法q是在FactoryMethod中,
//q个Ҏ只是赯助作用,q也是一U思维ҎQ将具体的算法实现在一个方法中
//而我不直接调用此ҎQ而用另外的一个方法封装它Q等C更灵zȝ效果Q?br />//子类需实现的内ҎFactoryMethod
//此方法是一个TemplateMethod
public Product creat() {
Product pd = null;
System.out.println("before operation");
pd = fmd();
System.out.println("end operation");
return pd;
}
}
class Product1 implements Product {
}
class Factory1 extends Factory {
public Product fmd() {
Product pd = new Product1();
return pd;
}
}
//FactroyMethod 2
//q种方式单实?br />interface Producta {
}
interface Factorya {
Producta create();
}
class Producta1 implements Producta {}
class Factorya1 implements Factorya {
public Producta create() {
Producta pda = null;
pda = new Producta1();
return pda;
}
}
//AbstractFactory
//AbstractFactory与FactoryMethod的不同在于AbstractFactory创徏多个产品
//感觉此模式没有什么大?br />
//当然可以q有更多的接?br />interface Apda {}
interface Apdb {}
interface Afactory {
Apda createA();
Apdb createB();
}
class Apda1 implements Apda {}
class Apdb1 implements Apdb {}
//有几个接口就有几个对应的Ҏ
class Afactory1 implements Afactory {
public Apda createA() {
Apda apda = null;
apda = new Apda1();
return apda;
}
public Apdb createB() {
Apdb apdb = null;
apdb = new Apdb1();
return apdb;
}
}
//Builder
//一个品的生成分ؓ生成部g和组装部Ӟ不同的品每个部件生成的方式不同
//而组装的方式相同Q部件的生成抽象成接口方法,而组装的Ҏ使用一个TemplateMethodҎ
interface Cpda {}
class Cpda1 implements Cpda {}
interface BuilderI {
void buildPart1();
void buildPart2();
void initPd();
Cpda getPd();
}
abstract class BuilderA implements BuilderI {
Cpda cpda;
public Cpda getPd() {
initPd();
//对对象的内容q行讄
buildPart1();
buildPart2();
return cpda;
}
}
class Builder extends BuilderA {
public void buildPart1() {
System.out.println(cpda);
}
public void buildPart2() {
System.out.println(cpda);
}
public void initPd() {
cpda = new Cpda1();
}
}
//一个简单的生成产品的实?br />//1
abstract class Fy {
public abstract void med1();
static class Fy1 extends Fy {
public void med1() {
}
}
public static Fy getInstance() {
Fy fy = new Fy1();
return fy;
//Fy fy = new Fy1() {//q种匿名内部cL静态的Q!
//public void med1() {
//}
//};
//return fy;
}
}
//2
interface Pdd {}
class Pdd1 implements Pdd {}
abstract class Fya {
public static Pdd getPd() {
Pdd pdd = new Pdd1();
return pdd;
}
}
//Prototype 在java中就是cloneQ又包含深拷贝和拷?br />class CloneObja {
public CloneObja MyClone() {
return new CloneObja();
}
}
class CloneObjb {
public CloneObjb MyClone() throws Throwable {
CloneObjb cobj = null;
cobj = (CloneObjb) pcl(this);
return cobj;
}
//深度拯法
private Object pcl(Object obj) throws Throwable {
ByteArrayOutputStream bao = new ByteArrayOutputStream(1000);
ObjectOutputStream objo = new ObjectOutputStream(bao);
objo.writeObject(obj);
ByteArrayInputStream bai = new ByteArrayInputStream(bao.toByteArray());
ObjectInputStream obji = new ObjectInputStream(bai);
Object objr = obji.readObject();
return objr;
}
}
//Singleton
//一个类只有一个对象,例如一个线E池Q一个cache
class Singleton1 {
public static Singleton1 instance = new Singleton1();
private Singleton1() {
}
public static Singleton1 getInstance() {
return instance;
}
}
class Singleton2 {
public static Singleton2 instance;
private Singleton2() {
}
//public static Singleton2 getInstance() {
//if (instance == null) {
//instance = new Singleton2();
//}
//
//return instance;
//}
public static Singleton2 getInstance() {
synchronized(Singleton2.class) {
if (instance == null) {
instance = new Singleton2();
}
}
return instance;
}
}
//**********l构型模?*********
//Adapter
//基本Ҏ有两U,一U是使用引用一U用?br />//不W合标准的接口{成符合标准的接口Q接口的修改主要是参数的增减Q?br />//q回值类?当然q有Ҏ?br />//感觉q就是封装的另一U表CŞ式,装有用Ҏ装(在方法中调用功能Ҏ)Q?br />//用类装(先传入功能方法所在的cȝ对象Q通过调用此对象的功能Ҏ)
//使用引用的Ş?br />class Adapteea {
public void kk() {}
}
interface Targeta {
String vv(int i, int k);
}
class Adaptera implements Targeta{
Adapteea ade;
public Adaptera(Adapteea ade) {
this.ade = ade;
}
public String vv(int i, int k) {
//具体的业务方法实现在Adaptee中,q个Ҏ
//只vC接口转换的作?br />//调用此方法是通过引用
ade.kk();
return null;
}
}
//使用l承形式?br />class Adapteeb {
public void kk() {}
}
interface Targetb {
String vv(int i, int k);
}
class Adapterb extends Adapteeb implements Targetb {
public String vv(int i, int k) {
//调用此方法是通过l承
kk();
return null;
}
}
//Proxy
interface Subject {
void request();
}
class realSubject implements Subject {
public void request() {
//do the real business
}
}
class Proxy implements Subject {
Subject subject;
public Proxy(Subject subject) {
this.subject = subject;
}
public void request() {
System.out.println("do something");
subject.request();
System.out.println("do something");
}
}
//Bridge
//感觉是多态的实现
interface Imp {
void operation();
}
class Cimp1 implements Imp {
public void operation() {
System.out.println("1");
}
}
class Cimp2 implements Imp {
public void operation() {
System.out.println("2");
}
}
class Invoker {
Imp imp = new Cimp1();
public void invoke() {
imp.operation();
}
}
//Composite
interface Component {
void operation();
void add(Component component);
void remove(Component component);
}
class Leaf implements Component {
public void operation() {
System.out.println("an operation");
}
public void add(Component component) {
throw new UnsupportedOperationException();
}
public void remove(Component component) {
throw new UnsupportedOperationException();
}
}
class Composite implements Component {
List components = new ArrayList();
public void operation() {
Component component = null;
Iterator it = components.iterator();
while (it.hasNext()) {
//不知道此component对象是leafq是compositeQ?br />//如果是leaf则直接实现操作,如果是composite则l递归调用
component = (Component) it.next();
component.operation();
}
}
public void add(Component component) {
components.add(component);
}
public void remove(Component component) {
components.remove(component);
}
}
//Decorator
//对一个类的功能进行扩展时Q我可以使用l承Q但是不够灵z,所以选用?br />//另外的一UŞ?引用与扉K可活得对对象的一定的使用能力Q而用引用将更灵z?br />//我们要保证是对原功能的追加而不是修改,否则只能重写ҎQ或使用新的Ҏ
//注意concrete的可以直接new出来Q?br />//而decorator的则需要用一个另外的decorator对象才能生成对象
//使用对象装Q和公用接口
//Decorator链上可以有多个元?br />
interface Componenta {
void operation();
}
class ConcreteComponent implements Componenta {
public void operation() {
System.out.println("do something");
}
}
class Decorator implements Componenta {
private Componenta component;
public Decorator(Componenta component) {
this.component = component;
}
public void operation() {
//do something before
component.operation();
//do something after
}
}
//Facade
//非常实用的一U设计模式,我可以ؓ外部提供感兴的接口
class Obj1 {
public void ope1() {}
public void ope2() {}
}
class Obj2 {
public void ope1() {}
public void ope2() {}
}
class Facade {
//我得C一个简z清晰的接口
public void fdMethod() {
Obj1 obj1 = new Obj1();
Obj2 obj2 = new Obj2();
obj1.ope1();
obj2.ope2();
}
}
//Flyweight
//I?br />//**********行ؓ型模?************
//Chain of Responsibility
//与Decorator的实现Ş式相cMQ?br />//Decorator是在原来的方法之上进行添加功能,?br />//Chain则是判断信号如果不是当前处理的则转交个下一个节点处?br />//我可以用if分支来实现相同的效果Q但是不够灵z,链上的每个节Ҏ可以替换增加的,相对
//比较灉|Q我们可以设计接口实现对节点的增删操作,而实现更方便的效?br />//q个是一个链状的l构Q有没有惌使用环状l构
interface Handler {
void handRequest(int signal);
}
class CHandler1 implements Handler {
private Handler handler;
public CHandler1(Handler handler) {
this.handler = handler;
}
public void handRequest(int signal) {
if (signal == 1) {
System.out.println("handle signal 1");
}
else {
handler.handRequest(signal);
}
}
}
class CHandler2 implements Handler {
private Handler handler;
public CHandler2(Handler handler) {
this.handler = handler;
}
public void handRequest(int signal) {
if (signal == 2) {
System.out.println("handle signal 2");
}
else {
handler.handRequest(signal);
}
}
}
class CHandler3 implements Handler {
public void handRequest(int signal) {
if (signal == 3) {
System.out.println("handle signal 3");
}
else {
throw new Error("can't handle signal");
}
}
}
class ChainClient {
public static void main(String[] args) {
Handler h3 = new CHandler3();
Handler h2 = new CHandler2(h3);
Handler h1 = new CHandler1(h2);
h1.handRequest(2);
}
}
//Interpreter
//感觉跟Composite很类|只不q他分文l结W和非终l符
//Template Method
abstract class TemplateMethod {
abstract void amd1();
abstract void amd2();
//此方法ؓ一个Template MethodҎ
public void tmd() {
amd1();
amd2();
}
}
//State
//标准?br />//状态和操作不应该耦合在一?br />class Contexta {
private State st;
public Contexta(int nst) {
changeStfromNum(nst);
}
public void changeStfromNum(int nst) {
if (nst == 1) {
st = new CStatea1();
}
else if (nst == 2) {
st = new CStatea2();
}
throw new Error("bad state");
}
void request() {
st.handle(this);
}
}
interface State {
void handle(Contexta context);
}
class CStatea1 implements State {
public void handle(Contexta context) {
System.out.println("state 1");
//也许在一个状态的处理q程中要改变状态,例如打开之后立即关闭q种效果
//context.changeStfromNum(2);
}
}
class CStatea2 implements State {
public void handle(Contexta context) {
System.out.println("state 2");
}
}
//工厂?br />//Ҏ状态不通生成不同的state
//class StateFactory {
//public static State getStateInstance(int num) {
//State st = null;
//
//if (num == 1) {
//st = new CStatea1();
//}
//else if (num == 2) {
//st = new CStatea2();
//}
//
//return st;
//}
//}
//Strategy
//跟Bridge相类|是一U多态的表示
//Visitor
//双向引用Q用另外的一个类调用自己的方?讉K自己的数据结?br />interface Visitor {
void visitElement(Elementd element);
}
class CVisitor implements Visitor {
public void visitElement(Elementd element) {
element.operation();
}
}
interface Elementd {
void accept(Visitor visitor);
void operation();
}
class CElementd implements Elementd {
public void accept(Visitor visitor) {
visitor.visitElement(this);
}
public void operation() {
//实际的操作在q里
}
}
class Clientd {
public static void main() {
Elementd elm = new CElementd();
Visitor vis = new CVisitor();
vis.visitElement(elm);
}
}
//Iteraotr
//使用q代器对一个类的数据结构进行顺序P?br />
interface Structure {
interface Iteratora {
void first();
boolean hasElement();
Object next();
}
}
class Structure1 implements Structure {
Object[] objs = new Object[100];
//使用内部cLZ对Struture1的数据结构有完全的访问权
class Iteratora1 implements Iteratora {
int index = 0;
public void first() {
index = 0;
}
public boolean hasElement() {
return index < 100;
}
public Object next() {
Object obj = null;
if (hasElement()) {
obj = objs[index];
index++;
}
return obj;
}
}
}
//Meditor
class A1 {
public void operation1() {}
public void operation2() {}
}
class A2 {
public void operation1() {}
public void operation2() {}
}
class Mediator {
A1 a1;
A2 a2;
public Mediator(A1 a1, A2 a2) {
this.a1 = a1;
this.a2 = a2;
}
//如果我想实现q个功能我可能会把他攑֜A1?br />//但是q样耦合大,我不惛_A1中出现A2对象的引用,
//所以我使用了Mediator作ؓ中介
public void mmed1() {
a1.operation1();
a2.operation2();
}
public void mmed2() {
a2.operation1();
a1.operation2();
}
}
//Command
//我认为就是将Ҏ转换成了c?br />
class Receiver {
public void action1() {}
public void action2() {}
}
interface Command {
void Execute();
}
class CCommand1 implements Command {
private Receiver receiver;
public CCommand1(Receiver receiver) {
this.receiver = receiver;
}
public void Execute() {
receiver.action1();
}
}
class CCommand2 implements Command {
private Receiver receiver;
public CCommand2(Receiver receiver) {
this.receiver = receiver;
}
public void Execute() {
receiver.action2();
}
}
//Observer
//在这里看gq个模式没有什么用
//但是如果我有一个线E监控SubjectQ如果Subject的状?br />//发生了变化,则更改Observer的状态,q出发一些操作,q样有实际的意义了
//Observer与Visitor有相似的地方Q都存在双向引用
//Subject可以注册很多Observer
interface Subjectb {
void attach(Observer observer);
void detach(Observer observer);
void mynotify();
int getState();
void setState(int state);
}
class Subjectb1 implements Subjectb {
List observers = new ArrayList();
int state;
public void attach(Observer observer) {
observers.add(observer);
}
public void detach(Observer observer) {
observers.remove(observer);
}
public void mynotify() {
Observer observer = null;
Iterator it = observers.iterator();
while (it.hasNext()) {
observer = (Observer) it.next();
observer.Update();
}
}
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
}
interface Observer {
void Update();
}
class Observer1 implements Observer {
Subjectb subject;
int state;
public Observer1(Subjectb subject) {
this.subject = subject;
}
public void Update() {
this.state = subject.getState();
}
public void operation() {
//一些基于state的操?br />}
}
//Memento
//感觉此模式没有什么大?br />
class Memento {
int state;
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
}
class Originator {
int state;
public void setMemento(Memento memento) {
state = memento.getState();
}
public Memento createMemento() {
Memento memento = new Memento();
memento.setState(1);
return memento;
}
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
}
class careTaker {
Memento memento;
public void saverMemento(Memento memento) {
this.memento = memento;
}
public Memento retrieveMemento() {
return memento;
}
}
//E序最l还是顺序执行的Q是׃通部分的操作拼接h?br />//不同类的代码拼接v来是通过引用实现的,有了引用我就
//相当于有了一定访问数据结构和Ҏ的能力,q与写在cd?br />//差不多,例如我想一个类中的一个方法抽d去,因ؓq个Ҏ依赖与此cȝ数据和其他方?br />//直接代码移走是不行的,但如果我们拥有了此类对象的引用,则与写在此类
//内部无异Q所以我们拥有了引用可以将此方法移?br />public class tt1 {
public static void main(String[] args) {
}
}
]]>
一.) 条gcd
<logic:empty name="myBean"> The bean is missing </logic:empty> <logic:notEmpty name="myBean"> The bean is not missing </logic:notEmpty> |
<logic:equal name="bean" property="doubleProperty" value="<%= doub1 %>"> equal </logic:equal> <logic:greaterEqual name="bean" property="doubleProperty" value="<%= doub1 %>"> greaterEqual </logic:greaterEqual> |
<logic:notPresent name="myBean" property="prop" scope="page"> The bean property bean.prop is present </logic:notPresent> |
<logic:match header="User-Agent" value="Mozilla"> Mozilla! </logic:match> <logic:notMatch header="User-Agent" value="Mozilla"> Not Mozilla :( </logic:notMatch> |
<logic:match name="bean" property="stringProperty" value="hello world"> Hello World! </logic:match> <logic:notMatch name="bean" property="stringProperty" value="hello world"> I'm so sad and lonely. </logic:notMatch> |
<logic:match parameter="name" value="xyz" location="1"> The parameter name is a sub-string of the string xyz from index 1 </logic:match> |
?) 循环cd
logic:iterate
重复嵌套在此标签体内的指定集合的元素Q集合必Mؓ如下cdQarray 、Collection(包括ArrayList和VectorQ、Enumeration、Iterator、Map(包括HashMap、Hashtable和TreeMap)。例如:
上面的代码P代了一个employee的集合,是department属性employees的一个集合,作用域ؓrequest?BR>
<logic:iterate id="employee" name="department" property="employees" scope= "request">
?BR> <bean:write name="employee" property="username" />
?BR> <bean:write name="employee" property="name" />
?BR> <bean:write name="employee" property="phone" />
?BR> </logic:iterate>
Z输出W五个到W十个employeeQ需要用length和offset属性:
<logic:iterate id="employee" name="department" property="employees" scope= "request" length="10" offset="5">
?BR> <bean:write name="employee" property="username" />
?BR> <bean:write name="employee" property="name" />
?BR> <bean:write name="employee" property="phone" />
?BR> </logic:iterate>
你可以定义一个变量保存当前的索引|
<ol>
<logic:iterate id="element" name="bean" property="stringArray" indexId="index">
<li>
<em>
<bean:write name="element"/>
</em>
[<bean:write name="index"/>]</li>
</logic:iterate>
</ol>
?) 转发/重定向类?/FONT>
logic:forward和logic:redirect
logic:forward标签和jsp:forward标签和相|但它可以使用global forward中的一个ActionForwardQ?/FONT>
<logic:forward name="login" />
----------------------------------------------------------------------------------------------------------------------------------
与上面相关的global forward中的代码Q?BR> <global-forwards>
<forward name="login" path="/loginForm.jsp"/>
</global-forwards>
logic:redirect标签和上面的标签cMQ但它默认调用的Ҏ是response.sendRedirect()Q取代了上面的requestDispatcher.forward()。最大的不同是它支持所有html:link标签的属性,所以你能够?/FONT>定request参数Q?/FONT>
<logic:redirect name="login" paramId="employeeId" paramName="employee" property="id" />
作ؓ一个Java Web开发h员,你可能已l对Web应用E序的目录结构很熟悉了(?A href="javascript:openWindowRes('Java/2002_09/xml2html.asp?xmlfile=RestrictAccessWebR/Figure1.xml&xslfile=../../include/xsl/Figure.xsl');">?Q。在WEB-INF/classes目录下放|了servletc,在WEB-INF/lib下是Java档案文gQ如HTML和图片文件的静态资源直接放在应用程序目录下Q或者在应用E序目录下的M子目录中Q。例如,所有图片文仉攑֜囄目录中(?A href="javascript:openWindowRes('Java/2002_09/xml2html.asp?xmlfile=RestrictAccessWebR/Figure1.xml&xslfile=../../include/xsl/Figure.xsl');">?Q。JSP面也放在应用程序目录中?/P>
![]() |
应用E序的目录结?/A> |
q就是ؓ什么许多程序员都把他们的资源文Ӟ如果它们是和应用E序攑֜一L话)攑֜WEB-INF目录中的原因。WEB-INF之外的Q何资源都可以通过输入URL来查看。HTML文g和JSP文g一般都会被调用Q所以它们通常存储在WEB-INF之外?/P>
然而,你可能想限制对WEB-INF目录之外的文件的讉K。你可以用三U方法:通过q用referer HTTP request headerQ是“referer”,不是“referrer”)Q通过查用Lsession对象中的一个属性;或者通过那些资源放在WEB-INF中,q在适当的时候参照它们。下面是q用q三U方法的指南?/P>
q用Referer HTTP Request Header
Referer HTTP request header指定了一个URI (Uniform Resource Identifier)Q该URI包含链接到被h资源的页面的地址。例如,下面是对一个叫做myPage1.jsp的JSP面的请求,该页面来自一个叫做Login.jsp的文Ӟ
http://domainName/appName/Login.jsp |
如果直接在Web览器的Address或Location中输入URL来调用myPage1.jspQ就不会有一个referer header?/FONT>
下面的例子运用了一个叫做DisplayRequestHeaders.jsp的JSP面Q?
<%@ page import="java.util.Enumeration" %> <% Enumeration headers = request.getHeaderNames(); while (headers.hasMoreElements()) { String header = (String) headers.nextElement(); out.println(header + ":" + request.getHeader(header) + "<br>"); } %> |
如果你直接将URL输入到浏览器的Address或Location来调用这个页面,l果如下Q?
accept:*/* accept-language:en-us accept-encoding:gzip, deflate user-agent:Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR 1.0.3705) host:localhost:8080?connection:Keep-Alive cookie:JSESSIONID=65D28447DFE4F58D1D806EAA933E9DD7 |
然而,如果通过点击另一个页面(如Login.jspQ中的一个链接来hDisplayRequestHeaders.jspQ你可以看到如下的结果:
accept:image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/msword, application/vnd.ms-powerpoint, */* |
注意两者的主要区别是在W二个结果中出现了referer header。因此,如果你的应用E序规定Q当你直接将URL输入到浏览器的Location或Address中来调用一个资源时Q你不能看到q个资源Q在q个例子中,是myPage1.jspQ,那么你可以把q个代码d到myPage1.jsp的顶部:
if (request.getHeader("referer")==null) response.sendRedirect(somewhereElse); |
或者,如果你想信资源来自一个特定的URLQ可以在前面的代码后面添加下面的代码Q?
if (request.getHeader("referer")==null) response.sendRedirect(somewhereElse); if (request.getHeader("referer")!=aURL) response.sendRedirect(somewhereElse); |
然而,你需要注意,q用referer header只适合于简单的控制管理。因为它依赖于来自用Lrequest headersQ所以它q不?00%的安全。了解socket programming的聪明的用户常常可以信有一个包含期望值的referer header?/P>
查一个Session对象的属?/FONT>
另一U限制对一个特定页面的讉K的方法就是通过查用Lsession对象中出现的一个特定的属性。例如,如果你想让ABC.jsp面只能被登录后的用L刎ͼ你可以在用户成功d后,在用户session对象中设|一个叫做loggedIn的属性。然后在ABC.jsp面的顶部,你可以查看loggedIn属性是否出现在用户的session对象中:
<% if (session.getAttribute("loggedIn")==null) { %> <jsp:forward page="Login.jsp"/> <% } else { %> display the page content here. |
q种Ҏ的缺Ҏ你在每一都需要额外的代码Q而且q有另外的维护工作。更重要的是Q受限制的页面需要参预session理Q而这正是你在一些应用程序中可能想避免的事情?/P>
在WEB-INF目录下存储资?/FONT>
在你不想让用戯用你的JSP面ӞW三U方法——将资源攑֜WEB-INF下——会很有用。实现Model 2l构的应用程序和Struts应用E序q用JSP面作ؓModel-View-Controller中的View。这些JSP面q不打算让用户从览器直接调用。作为替代,它们从控制器servlet内部来调用。对q些应用E序来说Q将JSP面攑֜WEB-INF目录外——就像许多程序员所做的那样——需要你d额外的代码来限制对这些资源的直接讉K?/P>
另一斚wQ把它们攑֜WEB-INF中可以保证它们只能从那个应用E序的一个servlet来访问。但如果你决定把JSP面存在WEB-INF中,你就需要知道如何参照这些页面。幸q的是,qƈ不难。做法如下?/P>
通过q用一个RequestDispatcher对象Q一个servlet可以包含QincludeQ或转送(forwardQ一个JSP面。下面的例子forwardqinclude一个叫做Included.jsp的JSP面Q?
RequestDispatcher rd = request.getRequestDispatcher("/WEB- INF/Included.jsp"); rd.forward(request, response); |
但有时侯Q你需要显C的是HTML文gQ而不是JSP面。在一些Web容器QcontainersQ中Q前面代码中的request dispatchersq不能用于静态的资源Q如果静态资源不在WEB-INF下,它可以用Q。对q些资源来说Q你可以q用javax.servlet.ServletContext接口的getResourceAsStreamҎ。该Ҏ的定义如下:
public java.io.InputStream getResourceAsStream(java.lang.String path) |
要运用这个方法,你需要把一个\径传送到你想包含的静态资源。该Ҏq回一个InputStream。然后,你可以用InputStream的readҎ来得到静态资源的内容?/P>
例如Q下面的q个JSP面包含一个叫做getResourceContent的函敎ͼ你可以用它以字符串Ş式返回一个静态资源的内容Q?
<%@ page import="java.io.InputStream"%> <%! public String getResourceContent(InputStream in) { if (in==null) return null; StringBuffer sb = new StringBuffer(2048); try { int i = in.read(); while (i != -1) { sb.append((char)i); i = in.read(); } in.close(); } catch (Exception e) { } return sb.toString(); } %> <html> <head> <title> </title> </head> <body> <br> <% String path = "/WEB-INF/header.html"; InputStream in = application.getResourceAsStream(path); out.println(getResourceContent(in)); %> </body> </html> |
注意Q在JSP面中,应用E序暗示的对象代表ServletContext对象?/P>
该JSP面昄了如何包含位于WEB-INF目录中的header.html文g的内宏V如果你把header.html文g攑֜q儿Q它׃能直接从览器调用?/P>
通过q用一些缓冲策略,你可以优化JSP面中的getResourceContentҎ。在q里Q我只是解释了得到放在WEB-INF目录下的一个静态资源的内容的方法?/P>
你可以从一个servlet内部Q也可以从一个JSP面q用getResourceContentҎ。从一个JSP面Q你可以q用指示WincludeQ如下面的代码所C:
<%@ include file="/WEB-INF/header.html" %> |
现在你应该了解了Q你有三U不同的Ҏ来保护你的资源,以及如何实现q些Ҏ?/P>
· 当你惛_gq重定向Ӟ你必运用客L重定向。服务器端重定向是立卛_现的Q因此,举例来说Q你׃能用服务器端重定向技术来创徏一个闪屏(splash screenQ?/TD> |
· 如果你没有运用诸如servlets或JSP的服务器端处理技术,那么你只能用客户端重定向来引g的网站访问者?/TD> |
关于客户端重定向Q你可以用Refresh metatag或JavaScript。下面我分别讲述q两U方法?/P>
q用Refresh Metatag
Metatags比JavaScriptҎ。在一个页面已l显CZ一D|间后Q你可以用它们来把用户从q个面引导开Q?
<META HTTP-EQUIV="Refresh" CONTENT="t;URL=differentURL"> |
t是在重定向前Q浏览器等待的U数?/P>
例如Q下面的脚本在页面显CZ三秒后,浏览器重定向到http://www.brainysoftware.com?
<HTML> <HEAD> <TITLE>Client side redirection using the meta tag </TITLE> <META HTTP-EQUIV="Refresh" CONTENT="3;URL=http://www.brainysoftware.com"> </HEAD> <BODY> You will be redirected in 3 seconds. For now, relax and enjoy. </BODY> </HTML> |
当t=0Ӟ不会出现延迟?/P>
该技术可以用于Netscape Navigator 3及更高版本,Internet Explorer 4及更高版本?BR>
q用JavaScript
在运用metatagӞ你的局限性很大,因ؓ一些老的览器读不懂它。如果你担心q个问题Q那么你可以用JavaScript。然而,如果用户览器不支持JavaScriptQ你q是会有问题?/P>
要用JavaScriptq行客户端重定向Q运用定位对象(location objectQ:
<SCRIPT LANGUAGE="JavaScript"> location=differentURL </SCRIPT> |
differentURL是浏览器被重新定向到的新的URL。下面的例子昄如何q用定位对象来重定向Q?
<HTML> <HEAD> <TITLE>Client-side redirection using JavaScript</TITLE> <SCRIPT LANGUAGE="JavaScript"> location='http://www.brainysoftware.com'; </SCRIPT> </HEAD> <BODY> You will be redirected to another page. For now, enjoy and relax. </BODY> </HTML> |
同运用metatags一P你可以用JavaScript来gq重定向。运用setTimeoutҎ来实现这一点:
setTimeout("location='differentURL'", t); |
t是用戯重定向到differentURL前的毫秒数?/P>
下面的代码在5U后用户重定向到www.brainysoftware.comQ?
<HTML> <HEAD> <TITLE>Client-side redirection using JavaScript</TITLE> <SCRIPT LANGUAGE="JavaScript"> setTimeout("location= 'http://www.brainysoftware.com'", 5000); </SCRIPT> </HEAD> <BODY> Hello, you will be redirected in 5 seconds. Start counting now... </BODY> </HTML> |
该技术可以用于Netscape Navigator 2及更高版本,Internet Explorer 3及更高版本?BR>
预见不成功的重定?/FONT>
每种技术都有缺炏Vmetatags不能用于老的览器,如果览器不支持JavaScriptQ那么JavaScript׃能用?/P>
所以在重定向用hQ你常常要预料到p|的情c当自动的重定向p|Ӟ用户必须点击一个链接,使他或她可以q入期望的地址。所以需要常发送这个链接,如下例所C:
<HTML> <HEAD> <TITLE>When redirection fails . . . </TITLE> <SCRIPT LANGUAGE="JavaScript"> setTimeout("location= 'http://www.brainysoftware.com'", 5000); </SCRIPT> </HEAD> <BODY> Click <A HREF="RedirectionURL">here</A> if you do not get redirected in 5 seconds. </BODY> </HTML> |
重定向可以在服务器端q行Q也可以在客LQ浏览器Q进行。因为在servlet/JSP面上有服务器端的代码,所以可以在服务器端q行重定向。客L的重定向可以通过发送JavaScript代码或者传送到览器的HTML面的元数据来完成。现在,让我们来看看两个服务器端重定向的技巧?/P>
在servlet/JSP~程中,服务器端重定向可以通过下面两个Ҏ来实玎ͼq用javax.servlet.RequestDispatcher接口的forwardҎQ或者运用javax.servlet.http.HttpServletResponse接口的sendRedirectҎ?/P>
q用RequestDispatcher接口的ForwardҎ
要运用RequestDispatcher接口的forwardҎQ首先要得到一个RequestDispatcher对象。servlet技术提供了三种方式来得到它Q?
1. 通过q用javax.servlet.ServletContext接口的getRequestDispatcherҎQ将一个包含\径的String传递给其它资源。该路径是相对于ServletContext的根路径的?/TD> |
2. 通过q用javax.servlet.ServletRequest接口的getRequestDispatcherҎQ将一个包含\径的String传递到其它资源。该路径是相对于当前的HTTPh的?/TD> |
3. 通过q用javax.servlet.ServletContext接口的getNamedDispatcherҎQ传递一个包含其它资源名字的String?/TD> |
在得到RequestDispatcher对象后,q用forwardҎ很Ҏ了。forwardҎ的定义如下:
public void forward(javax.servlet.ServletRequest request, javax.servlet.ServletResponse response) throws javax.servlet.ServletException, java.io.IOException |
但要注意Q你只有在客L没有输出时才可以调用forwardҎ。如果当前页面的~冲区(bufferQ不是空的,那么你在调用forwardҎ前必d清空~冲区。否则,会抛Z个IllegalStateException。forwardҎ也可以用来将h发送到一个静态的面?BR>
servlet/JSP的初学者在试得到一个RequestDispatcher对象Ӟ常感到困惑,q是因ؓServletContext接口的getRequestDispatcherҎ和ServletRequest接口的getRequestDispatcherҎ有很大的不同。接着阅读本文你就可以得到一些技巧来避免这两者淆在一起了?/P>
在运用RequestDispatcher对象的forwardҎ来将一个控件从一个叫做ABCServlet的servlet传递到另一个叫做XYZServlet的servletӞ最单的Ҏ是把ABCServlet和XYZServlet的类文g攑֜同一个目录中。通过q种ҎQ你可以从URL http://domain/VirtualDir/servlet/ABCServlet来调用ABCServletQ从URL http://domain/VirtualDir/servlet/XYZServlet来调用XYZServlet了。然后运用forwardҎ很单了。你可以从ServletRequest接口q用getRequestDispatcher ,传递第二个servlet的名字。在ABCServlet中,你可以写下面的代码:
RequestDispatcher rd = request.getRequestDispatcher("SecondServlet"); rd.forward(request, response); |
你不需要在XYZServlet前放 / W号。这U方法是最单的Q因ZҎ不需要担心两个servlets的\径?
E复杂的Ҏ是把下面这个String传递到ServletRequest的getRequestDispatcherQ?
"/servlet/XYZServlet" |
如果你必调用一个从ServletContext的getRequestDispatcher得到的RequestDispatcher对象的forwardҎQ你需要将?VirtualDir/servlet/XYZServlet”作\径参数来传递,如下Q?
RequestDispatcher rd = getServletContext().getRequestDispatcher( "/servlet/XYZServlet"); rd.forward(request, response); |
要运用getNamedDispatcherҎQ你的代码就会变成:
RequestDispatcher rd = getServletContext().getNamedDispatcher( "XYZServlet"); rd.forward(request, response); |
在运用getNamedDispatcherҎӞ你必d部v描述W中注册W二个servlet。下面是个例子:
<web-app> <servlet> <servlet-name>ABCServlet</servlet-name> <servlet-class>ABCServlet</servlet-class> </servlet> <servlet> <servlet-name>XYZServlet</servlet-name> <servlet-class>XYZServlet</servlet-class> </servlet> </web-app> |
如果你改变了所含的servletQ你需要重新启动Web container以改变生效。这是因为所包含的servlet从来都不是直接调用的。一旦加载了所包含的servletQ它的时间戳Qtime stampQ就不会改变了?/P>
如果你从一个JSP面发送控Ӟ你也可以?lt;jsp:forward>执行元素Q它会终止当前JSP面的执行,q将控g传递到另一个资源。它的语法如下:
<jsp:forward page="relativeURL"/> |
例如Q在JSP面被解析后Q?lt;jsp:forward page=”OtherPage.jsp?>在结果servlet中就转换成了下面的代码:
pageContext.forward("OtherPage.jsp"); |
q用HttpServletResponse接口的sendRedirectҎ
sendRedirectҎ比forwardҎ要容易。其定义如下Q?
public void sendRedirect(java.lang.String location) throws java.iio.IOException |
该方法把一个命令发送到览器,让浏览器对在location中指定的URL提出h。该Ҏ可以接受l对的或相对的URLs。如果传递到该方法的参数是一个相对的URLQ那么Web container在将它发送到客户端前会把它{换成一个绝对的URL。如果地址是相对的Q没有一个?’,那么Web containerp为它是相对于当前的请求URI的?/P>
例如Q你可以用下面的代码用户重定向到www.brainysoftware.com:
response.sendRedirect(http://www.brainysoftware.com); |
你应该运用哪U技巧?
Z~写最有效的代码,你应该了解这两种重定向技巧的不同。forwardҎ是在Web container内部工作的。sendRedirectҎ需要到客户端的一个往q。所以forwardҎ比sendRedirect要快。但是,q用forwardҎ有局限性,你只能重定向到同一个Web应用E序中的一个资源。而sendRedirectҎ可以让你重定向到MURL。结论:如果可以解决你的问题Q那么就用forwardҎ。只有当你不能用forwardҎ时才q用sendRedirectҎ?/U>