??xml version="1.0" encoding="utf-8" standalone="yes"?> ----------Arthur J.Riel (2)cȝ使用者必M赖类的共有接口,但类不能依赖它的使用者?/P>
(3)量减少cȝ协议中的消息?/P>
(4)实现所有类都理解的最基本公有接口[例如Q拷贝操?深拷贝和拷?、相{性判断、正输出内宏V从ASCII描述解析{等]?/P>
(5)不要把实现细?例如攄q代码的私有函?攑ֈcȝ公有接口中?BR>如果cȝ两个Ҏ有一D公׃码,那么可以创Z个防止这些公׃码的U有函数? (6)不要以用h法用或不感兴趣的东西扰q的公有接口?/P>
(7)cM间应该零耦合Q或者只有导合关系。也卻I一个类要么同另一个类毫无关系Q要么只使用另一个类的公有接口中的操作?/P>
(8)cd该只表示一个关键抽象?BR>包中的所有类对于同一cL质的变化应该是共同闭的。一个变化若对一个包影响Q则对包中的所有类产生影响Q而对其他的包不造成M影响 . (9)把相关的数据和行为集中放|?BR>设计者应当留意那些通过get之类操作从别的对象中获取数据的对象。这U类型的行ؓ暗示着q条l验原则被违反了? (10)把不相关的信息放在另一个类?也即Q互不沟通的行ؓ)?BR>朝着E_的方向进行依? (11)保你ؓ之徏模的抽象概念是类Q而不只是对象扮演的角艌Ӏ?/P>
(12)在水qx向上可能统一地分布系l功能,也即Q按照设计,层cd当统一地共享工作?/P>
(13)在你的系l中不要创徏全能c?对象。对名字包含Driver、Manager、System、Susystem的类要特别多加小心?BR>规划一个接口而不是实C个接口? (14)对公共接口中定义了大量访问方法的cd加小心。大量访问方法意味着相关数据和行为没有集中存放?/P>
(15)对包含太多互不沟通的行ؓ的类多加心?BR>q个问题的另一表现是在你的应用E序中的cȝ公有接口中创Z很多的get和set函数? (16)在由同用L面交互的面向对象模型构成的应用程序中Q模型不应该依赖于界面,界面则应当依赖于模型?/P>
(17)可能地按照现实世界建模(我们常常Z遵守pȝ功能分布原则、避免全能类原则以及集中攄相关数据和行为的原则而违背这条原? ?/P>
(18)从你的设计中去除不需要的cR?BR>一般来_我们会把q个c降U成一个属性? (19)去除pȝ外的cR?BR>pȝ外的cȝ特点是,抽象地看它们只往pȝ领域发送消息但q不接受pȝ领域内其他类发出的消息? (20)不要把操作变成类。质疑Q何名字是动词或者派生自动词的类Q特别是只有一个有意义行ؓ的类。考虑一下那个有意义的行为是否应当迁Ud已经存在或者尚未发现的某个cM?/P>
(21)我们在创建应用程序的分析模型时常常引入代理类。在设计阶段Q我们常会发现很多代理没有用的,应当去除?/P>
(22)量减少cȝ协作者的数量?BR>一个类用到的其他类的数目应当尽量少? (23)量减少cd协作者之间传递的消息的数量?/P>
(24)量减少cd协作者之间的协作量,也即Q减类和协作者之间传递的不同消息的数量?/P>
(25)量减少cȝ扇出Q也卻I减少cd义的消息数和发送的消息数的乘积?/P>
(26)如果cd含另一个类的对象,那么包含cd当给被包含的对象发送消息。也卻I包含关系L意味着使用关系?/P>
(27)cM定义的大多数Ҏ都应当在大多数时间里使用大多数数据成员?/P>
(28)cd含的对象数目不应当超q开发者短期记忆的定w。这个数目常常是6?BR>当类包含多于6个数据成员时Q可以把逻辑相关的数据成员划分ؓ一l,然后用一个新的包含类d含这一l成员? (29)让系l功能在H而深的承体pM垂直分布?/P>
(30)在实现语义约束时Q最好根据类定义来实现。这常常会导致类泛滥成灾Q在q种情况下,U束应当在类的行Z实现Q通常是在构造函C实现Q但不是必须如此?/P>
(31)在类的构造函C实现语义U束Ӟ把约束测试放在构造函数领域所允许的尽量深的包含层ơ中?/P>
(32)U束所依赖的语义信息如果经常改变,那么最好放在一个集中式的第3方对象中?/P>
(33)U束所依赖的语义信息如果很改变,那么最好分布在U束所涉及的各个类中?/P>
(34)cdȝ道它包含什么,但是不能知道谁包含它?/P>
(35)׃n字面范围(也就是被同一个类所包含)的对象相互之间不应当有用关pR?/P>
(36)l承只应被用来ؓ特化层次l构建模?/P>
(37)zcdȝ道基c,基类不应该知道关于它们的zcȝM信息?/P>
(38)基类中的所有数据都应当是私有的Q不要用保护数据?BR>cȝ设计者永q都不应该把cȝ使用者不需要的东西攑֜公有接口中? (39)在理ZQ承层ơ体pd当深一点,深好?/P>
(40)在实践中Q承层ơ体pȝ深度不应当超Z个普通h的短期记忆能力。一个广为接受的深度值是6?/P>
(41)所有的抽象c都应当是基cR?/P>
(42)所有的基类都应当是抽象cR?/P>
(43)把数据、行为和/或接口的共性尽可能地放到承层ơ体pȝ高端?/P>
(44)如果两个或更多个cd享公共数?但没有公p?Q那么应当把公共数据攑֜一个类中,每个׃nq个数据的类都包含这个类?/P>
(45)如果两个或更多个cL共同的数据和行ؓ(是Ҏ)Q那么这些类的每一个都应当从一个表CZq些数据和方法的公共基类l承?/P>
(46)如果两个或更多个cd享公共接?指的是消息,而不是方?Q那么只有他们需要被多态地使用Ӟ他们才应当从一个公共基cȝѝ?/P>
(47)对对象类型的昄的分情况分析一般是错误的。在大多数这L情况下,设计者应当用多态?/P>
(48)对属性值的昄的分情况分析常常是错误的。类应当解耦合成一个承层ơ结构,每个属性值都被变换成一个派生类?/P>
(49)不要通过l承关系来ؓcȝ动态语义徏模。试囄静态语义关pL为动态语义徏模会D在运行时切换cd?/P>
(50)不要把类的对象变成派生类。对M只有一个实例的zc都要多加小心?/P>
(51)如果你觉得需要在q行时刻创徏新的c,那么退后一步以认清你要创徏的是对象。现在,把这些对象概括成一个类?/P>
(52)在派生类中用I方?也就是什么也不做的方?来覆写基cM的方法应当是非法的?/P>
(53)不要把可选包含同对承的需要相h。把可选包含徏模成l承会带来泛滥成灄cR?/P>
(54)在创建承层ơ时Q试着创徏可复用的框架Q而不是可复用的组件?/P>
(55)如果你在设计中用了多重l承Q先假设你犯了错误。如果没犯错误,你需要设法证明?/P>
(56)只要在面向对象设计中用到了承,问自׃个问题:(1)zcL否是它承的那个东西的一个特D类型?(2)基类是不是派生类的一部分Q? (57)如果你在一个面向对象设计中发现多重l承关系Q确保没有哪个基cd际上是另一个基cȝzcR?/P>
(58)在面向对象设计中如果你需要在包含关系和关联关p间作出选择Q请选择包含关系?/P>
(59)不要把全局数据或全局函数用于cȝ对象的薄记工作。应当用类变量或类Ҏ?/P>
(60)面向对象设计者不应当让物理设计准则来破坏他们的逻辑设计。但是,在对逻辑设计作出决策的过E中我们l常用到物理设计准则?/P>
(61)不要l开公共接口M改对象的状态?/P>
“你不必严格遵守q些原则Q违背它们也不会被处以宗教刑|。但你应当把q些原则看成警铃Q若q背了其中的一条,那么警铃׃响v。?
(1)所有数据都应该隐藏在所在的cȝ内部?/P>
介绍
首先我要解释一下ؓ什么会写这公开信。这g已经成了一U习惯,但这个步骤还是需要的。过? q中Q?我曾l无数次地在饭店、酒吧、旅店大厅等各种地方以同一U方式度q愉快而O长的夜晚Q和同样q求真理、光明和智慧的伙伴一h讨面向对象的真谛。现在,我已l可以回{很多当q我遇到的问题。这些同L问题也在困扰着我的一位新同事Q在一安店里Q我׃整整一个晚上和他讨些问题。结果第二天Q他的同事又来问q些问题Qƈ把我们的谈话内容记录下来Q这样他可以拿去l他的同事看。考虑到还有很多和他的同事一栯问这些同样问题的人,我决定写下这文章?
主要的问题是Q?
z Z么只要稍有一点不是严格或U面向对象的做法、说法,OO 专家大惊小怪的Quse case q不是面向对象的Qؓ什么还q么行Q而且QOO 建模g和数据徏模非常相|
z 数据建模Qdata modelQ得到的模型和对象模型的l构部分会不会很像?程建模Qprocess modelQ和对象模型的行为部分呢Q?
z 业务l构建模中ؓ什么要用use case 和场景(scenarioQ?
OO 的新手们反复问这些问题,但实际上Q只有在日常工作中坚持应用面向对象的思维q行工作Q积累一定的l验Q才能得到满意的{案。ؓ什么只要稍有一点不是严格或U面向对象的做法、说法,OO 专家大惊小怪的Quse case q不是面向对象的Qؓ什么还q么行Q而且QOO 建模g和数据徏模非常相|
我想分三步来回答q个问题?
首先Q我举我和BobQ著名的OO 专家Q一起工作的例子Q?当我们讨论OO 的时候,彼此都有一个共识,知道Ҏ拥有面向对象工作的丰富经验ƈ且是q项技术的坚定支持者。而且Q对诸如对象识别Qobject identityQ、多态、数据和行ؓ的封装、实例职责、承等对象技术都是手到擒来。因此,当我_“明天对E序表单q行数据建模吧”,Bob 不会产生我要会因为关p表而放弃对象这L误解Q他知道我指的是在对象模型中体现出来的结构化Ҏ进行徏模。他知道我会说些什么,因此我用或误用q些术语不会造成什么误解。但作ؓ一个对象技术的初学者,如果Bob 发现你把数据和行为完全分d了, q且没有使用Q?或者说忽视了)对象识别或者多态等技术, q时候, 如果你说?数据建模”,Bob 会像一堵墙一样D你,直到你明白该怎样改变。这样工作几个月Q你会发玎ͼ你的模型Q以及徏模)中渐渐有了对象识别、多态、数据和行ؓ的绑定,q时候再用?数据建模”这个词׃是那么危险了Q?但Bob q可能会担心你走回到老\上。换句话_ 他对你还不够信QQ?因此Q你不得不很心C用这些术语?
q一个对象模型可以分为“结构”和“行为”特性,我们也不会用“对象徏模”和“流E徏模?q种术语Q以免引h淆。事实上Qؓ对象模型的“结构”特性徏模可以看成是数据建模的特DŞ式,只不q徏模的对象不再是表Q而是需要捕L信息的结构。我们将它称为?概念数据模型”,而不是逻辑数据模型或物理数据模型。第二步Q让我们考虑两个OO 使用者一赯论的情况。如果其中一个家伙说到“流E徏模”这L词,肯定会让他的拍档琢磨半天Q这家伙是说用标准数据流图作程建模吗?如果q样的话Q以后OO 实现的时候不是相当麻烦了吗?他是不是指模型的行ؓҎ?是不是说在一个对象内部对程q行建模Q(如果q样的话Q那会很有意思,因ؓ很少有hq么做的。) 通过q个例子我们可以看到Q这U谈话中使用?程建模?q种意图不明的词实在是太危险了,很容易就交变得非常困难?
最后来说use case 和场景的问题Q它们都是获取需求的主要手段Q和实现技术无兟뀂其好处是可以ؓ设计时的讨论提供内容和范围。它们不是“面向对象”的Q这是事实,它们cM于功能分解,q也是事实,而且q一点吓坏了很多人,但这些都无所谓。重要的是它们ؓ设计提供了内容,在用来描q内部设计时Q它们表Cpȝ的行为。Flow chart 、交互图、Petri |、数据流图、use case 都可以用来描q系l的行ؓҎ, 但各自用途不同,各有优劣。关键是要知道:对象不仅有数据,也有行ؓQ?认识到这一点, 可以大胆地去考虑怎样可以更好地捕捉、描q对象内部和对象之间的行为?
数据建模Qdata model Q得到的模型和对象模型的l构部分会不会很像?程建模Qprocess modelQ?和对象模型的行ؓ部分呢?Ҏ我的l验Q数据徏模h员可以分ZU-一U是为存储的数据建模Q而另一U是为系l或l织中的信息建模。这两者截然不同。前者讨论和思考所用的概念通常都很具体Q比如说数据表。他们的模型和OO 建模者的模型大相径庭Q而且他们q不愿意看到q些技术之间的怼性。在数据建模C中,只有讨论逻辑数据模型或物理数据模型才不会受到d后者得到的是概忉|据模型。根据我的经验,他们得到的模型和那些有经验的OO 建模者得到的模型非常怼。他们的工作更加cM于业务h员,而不是逻辑数据建模人员Q这U说法可能会有助于理解概忉|据模型和逻辑数据模型的区别?
g有一套学问可以帮助h们比OO 建模人员更快地得到结果。我多次发现q样的事实:OO 建模人员׃三四ơP代才得到的模型,实际上和Q概念)数据建模人员一个@环得到的模型是一L。这个发C我对数据建模人员充满了敬佩。事实上Q在q行对象设计的时候,我有时就直接L数据建模人员的品拷贝过来看看, 从中来看我最后得到的模型大概会是什么样的?
我曾l召开q数据徏模h员和对象建模人员之间的会议。采取的Ҏ是?一个听众的CRC 会议QCRC sessions for an audienceQ。四个经验丰富的OO 设计师坐在长桌一端,业务专家沿着长桌坐下Q他们负责回{问题ƈU正对业务的误解。接着是数据徏模h员,长桌的另一头是其它有关人员。ȝ来说Q屋里大概是十几个hQ但谈话主要是在四个OO 设计师之间进行?
讨论大概一个小时之后,OO 设计师可以得到对象设计的一部分。这时候,咨询数据建模人员Q这个模型和他们已经得到的模型本质上是不是一L。他们说Q“是的”,重复q个q程两次以上Q每ơ都询问数据建模人员同样的问题,除了使用的符h术是不同的,例如Q他们没有用l承Q但在同L地方有同L占位W,在本质上Q这个模型和他们的模型没有什么不同的。接着Q我们分成几个小l,每个组包括一个业务专家、一个数据徏模专家和一个OO 专家Q很快就剩下的设计达成一_扑և技术上一些小的不同之处,q且q行排序。一般情况都是这LQ要么数据徏模h员考虑得更加合理,要么OO 建模人员考虑得更加合理,组要做的是在他们的设计之间q行协调?
从上面的q些l验可以看到Q用CRC q行OO 建模得到的模型和概念数据建模得到的结果非常相伹{另外,Ҏl验Q基于逻辑Q存储的信息Q的关系建模和OO 建模是不同的。大多数情况下,区别是由于技术的不同D的,例如Q在OO 模型中可以自由地使用l承和多对多的关pR由于技术上的差异,两种建模人员之间不能很好C,q是最大的困难?
数据建模部分的问题就说这么多吧?
ҎE徏模而言Q?情况却不一样了?0 q代初, 涌现出各U方法、计划、会议和目Q试囑ְ数据图的模型{变ؓ对象模型。曾l有几个目声称获得了成功,但都是后l无韻I业界一致的l论是: q个转变太困难了。大多数使用数据图的徏模h员最l都抛弃了这Ҏ术, 投入了对象设计的怀抱(Martin-Odell 和Ptech 组是著名的例外Q。对于流E徏模,现阶D|的回{是Q其模型无法与OO 模型相比较。但qƈ不是最l的回答QOO 建模人员正在开始进行本质上的流E徏模,下一步的发展会怎样Q我也ƈ不清楚,程变成过E那Pq程~程复活Q)Q?或者变成数据流NPq是cM于封装的对象Q?
业务l构建模中ؓ什么要用use case 和场景(scenarioQ?
use case 和场景…?
……ؓ讨论提供范围和内容,
……预C内容,包括什么,不包括什么,讨论多广Q多深入Q什么时候停止,
……ؓ设计的压力测试提供参数?
我见q一些不使用“场景”的组Q他们徏模的时候实际上是在问题域内作随机的探险。负责hҎ不知道下一个该问什么问题?l常都是凭直觉。一般说来,他们也不知道现在捕获的信息最后是否真正需要,q知道也是凭直觉或者瞎蒙?
在一个寂静的深夜Q几个真正专家的OO 设计师向我说了心里话Q根本就没有一个有效的规则来指导漂亮的对象设计。其中一位说Q“我们有时真的是在毫无目的地讨论”, 另一位也相当赞同Q“有时候我们就象没头的苍蝇一样到处ؕ撞,直到H然把一些好的对象给撞出来。?
使用场景也不能防止盲目的讨论和到处撞墙,只是可以论提供内容和边界。我曄见过有的组不用场景Q长旉在很大的建模范围内四处ؕ撞,失去控制。因此,使用场景和use case 的第一个理由就是ؓ建模的努力提供一个范围?
使用场景的第二个理由是决定需求获取到什么程度算_?
假设现在让你Z家货q公司徏模。你建模的内Ҏ什么?卡R的购Ch值……实际h值……{售h值……R内颜艜y…快送单据的数目……旅途的数目和目的地Q如果你惌把所有的内容都包括进来,那你永q无法结束。除此之外,q有其它的问题:从何处开始,何时l束Q包括什么,不包括什么,需要多细节? 场景可以回答关于内容的问题,{案是:你需要以回{场景中所有问题的信息。在l构化模型或完全的对象模型中Q如果用一个方框来对应一个场景。然后在览场景的过E中涉及到的元素(译者注Q这里的元素Q?是比如在OO 的类或对象) 涂成U色Q?会发现最后所有的元素都按某种序q接h了(不会有遗漏的元素Q。如果对所有的场景都执行这个操作,最后所有的元素都会被涂成红艌Ӏ在建模q程中,讨论会经常离题,用这U方法可以保证针对当前的需要制定一个边界,q样不会跑题太远?
最后,场景q提供了压力试的参C保证质量?
怎样比较两个模型的优劣? “和业务相符”,q是必要的,但肯定不够,可能有很多模型都可以“和业务相符”,怎样来度量这些模型的质量呢?
软g变更的成本很高,另外Q变更越靠后Q变更的成本也就高。(变更成本曲线Q。Y件设计的一个目的就是将变更隔离开来,每次变更只需改变一个模块。这q不是什么时候都能做到的Q正如Kent Beck 所_“如果你可以只改变一个类Q?那Y件的质量得到显著的提高”。和软g相比Q?业务模型的成本函数ƈ不是很清楚,但将变更影响的范围降低到最低肯定是应该的?
Z试变更曲线Q需要参敎ͼ而场景可以提供这些参数。如果用h要“something-or-the-other”,怎么办?模型中到底需要多元素?像CRCQclass-responsibility-collaborator Q这L技术鼓q户在现场快速得到场景, 揭示可能的未来变更。最后,查这些可能的变更Q检查需要ؓ之改动多元素。对于两个都“和业务相符”的模型Q哪个模型受场景影响的点一些,哪个模型p好一些。其它很多地方也可以扑ֈ压力试的参数。例如相关品的l构Q变更时是不是可以只改一点就可以了?在评h型时Q这些参数都应该用上?
场景q可以用在很多地方,例如Q同用户一h查需求,为系l提供功能测试的用例。以上所_是在徏模活动中采用场景的三个理由?BR>
作者的|站Q?A >http://alistair.cockburn.us/
]]>
waf包括在petstoreQ学习EJB的经典实例)中,我是从sun download的,最新版本是1.3.2Q在其自带的doc里面有安装说明。在提供的源代码中包括了waf frameworkQ我是把它放入jbuilder工程中进行研I?BR>
Waf framework实际包括俩部分,web tier和EJB tierQWebController作ؓq俩层之间访问的proxy。Event(事g)作ؓq俩层交互时传递的对象Q根据实际要求可以定义不同的事g对象Q而作为EJB层也会提供对q些事g处理的业务方法?BR>waf包括一个mappings.xml的文件用于定义事件映和url映射?BR>
web tierQ?BR>在web tier包括一个Control(MainServlet)Q用于接收客LhQ一些承自HtmlAction的action处理c?Model部分)Q这个和struts里面是一LQ这些action处理cȝ于处理不同的客户端请求,用户h的url和action之间的映在mappings.xml中定义?BR>action包括3个处理方法,doStart()、perform()、doEnd()Q在perform()Ҏ中,action除了q行一些web层的处理外还要决定触发哪个Event事g(卌发商业层的什么操?QControl会根据action的返回事仉过proxy对象WebController对EJB tierq行调用?BR>
EJB tierQ?BR>和web tier一样EJB tier也是MVCl构的,包括Control(EJBControllerLocalEJBQ一个local接口的会话bean)和Model(l承自EJBAction的actionc?。每一个actionc负责处理一个或多个事gEventQ这个也是在mappings.xml中进行定义。在action当中会调用业务逻辑层的EJB(通常是一些facsade EJB)q行实际业务处理?BR>wafZ保证一个client端所q行的所有业务处理可以是相关联的Q即允许保存各业务处理状态,q提供了状态机StateMachineQ用于保存业务操作时的处理状态?BR>
mappings.xmQ?BR>事g映射Q用于EJB tier的处理:
<event-mapping>
<event-class>com.sun.j2ee.blueprints.waf.event.events.ChangeLocaleEvent</event-class>
<ejb-action-class>com.sun.j2ee.blueprints.waf.controller.ejb.action.actions.ChangeLocaleEJBAction</ejb-action-class>
</event-mapping>
url映射Q用于web tier的处理:
<url-mapping url="changelocale.do" screen="locale_change_success.screen" >
<web-action-class>com.sun.j2ee.blueprints.waf.controller.web.action.actions.ChangeLocaleHTMLAction</web-action-class>
</url-mapping>
waf的处理过E如下:
1.客户端访问系l?BR>2.在web.xml中定义的listener(servlet技术,默认为DefaultComponentManager)被激z,为当前客L做初始化工作Q包括创建EJB tier的代理对象WebController以及EJB tier的Control(EJBControllerLocalEJBQ还包括当前记录当前客户端状态的StateMachine?BR>3.客户端发出xxx.do的请求?BR>4.web tier的Control(MainServlet)截获q个hQ这个在web.xml中进行定义?BR>5.MainServlet调用request处理cRequestProcessorq行本次业务h处理?BR>6.RequestProcessor会到mappings.xml文g中读取这个请求对应的actionQ然后依ơ调用action的doStart()、perform()、doEnd()Ҏ?BR>7.actionҎ用户的request来决定生什么处理事件Event?BR>8.RequestProcessor这个事件Event通过代理cWebController传递到EJB tier?BR>9.EJB tier的控制器EJBControllerLocalEJB捕获到这个事件后首先到mappings.xml中检索哪个EJBAction负责处理q个事g?BR>10.EJBControllerLocalEJB调用仅属于当前客L的StateMachine负责处理本次操作?BR>11.StateMachine依次调用EJBAction的doStart()、perform()、doEnd()Ҏ?BR>12.EJBAction的perform()除了调用业务逻辑层的EJB对象的业务方法,比如一些用了facasde模式的会话beanQ还可以业务处理结果存攑ֈ和一些状态值存攑ֈ仅属于当前客L的StateMachine当中?BR>x整个处理q程完成?BR>
从waf的源码来看注释显得ƈ不是很完善,因此对于理解带来了一些困难。虽然我们不一定是要用它Q但了解一下它的架构思想q是很有必要的。waf中采用的事g机制和struts昄有所不同?BR>
该帖?sunyn ?Jan 25, 2005 9:06 AM ~辑q?BR>
]]>
单来_Design Patten 是一个常用的Ҏ?在我们的开发过E中Q经怼遇到一些相同或者相q的问题Q每ơ我们都会去L一个新的解x法,Z节省旉提高效率Q我们提供一些能够解册些常见问题的Q被证实可行的方案,构成一个统一的资源库?BR> 一个Design Patten描述了一个被证实可行的方案。这些方案非常普通,是有完整定义的最常用的模式?q些模式可以被重用,有良好的伸羃性,而这些Design Patten的优势将在设计J2EE应用时得C现?/P>
1. Model-View-Controller
a. 问题
如果开发一个企业应用Q只需要一U客L的话Q那么一切都非常Ҏ解决。但真实情况是,我们必须面对q行在各U设备上客户端,象PDAQWAP览器以及运行在桌面上的览器,我们不得不开发不同的应用E序来处理来自不同客L的请求。数据访问与现实淆在一P可能会出现重复的数据讉KQ导致整个开发周期没有必要的廉?/P>
b. 的解x?BR> Model-View-Controller (MVC) 开发模式被证明是有效的处理Ҏ之一。它可以分离数据讉K和数据表现。你可以开发一个有伸羃性的Q便于扩展的控制器,来维护整个流E。如?所CZؓ整个模式的结构。MVC模式可以被映到多层企业U的J2EE应用上?BR> 所有的企业数据以及商业逻辑可以作ؓ模式?BR> 视图可以通过模式讉K数据QƈҎ客户端的要求来显C数据。视囑ֿM证当模式改变的时候,数据昄也必d时改变?BR> 控制器用来结合模式和视图Q把客户端来的请求{换成模式能够理解q执行的hQƈ且根据请求以及执行结果来军_下一ơ显C那一个视图?BR>Ҏ以上的逻辑Q你可以象这样徏立一个应用:
应用的商业逻辑由MVC中的模式也就是EJB来表现。模式必d理由控制器传递过来的Ҏ据的讉Kh?BR> 多个面l成了MVC中的视图Q这些视囑ֿ随模式一h新?BR> 控制器是一pd接收用户动作的对象,他们把用Lh转换成模式可理解的请求,q决定显C那一个页面当模式处理完请求后?/P>
c. 要点
MVCl构适用于那些多用户的,可扩展的Q可l护的,h很高交互性的pȝ?BR> MVC可以很好的表辄L交互和系l模式?BR> 很方便的用多个视图来昄多套数据Q是pȝ很方便的支持其他新的客户端类型?BR> 代码重复辑ֈ最低?BR> ׃分离了模式中的流控制和数据表玎ͼ可以分清开发者的责QQ另外,也可以加快品推向市场的旉?/P>
2. Front Controller
a. 问题
MVCl出了一个整个应用的松散的耦合架构。现在来看一下这样一个经常发生的情况。在某一个应用中Q用L到的视图和他所做的操作密切相关。这是一些具有高度交互性的面Q而这些页面之间含有高度的依赖性。在没有M模式的时候,q个应用只是一个许多独立的面的集合,l护和扩展变得异常困难?BR> 当一个页面移动后Q其他含有这个页面链接的文gQ都必须修改?BR> 当有一pd面需要口令保护时Q许多配|文仉要修改,或者页面需要包含新的标记?BR> 当一个页面需要一个新的表C层Ӟ面中的标记要被重新安排?BR> 当这个系l变得复杂时Q这些问题将变得更糟。如果用MVC来解决的话,变成一个如何管理控制器和视图之间交互的问题?/P>
b. 的解x?BR> 前台控制模式可以解决q个问题。这个模式中Q所有的h都被传送到一个对象中。这个主要的对象处理所有的hQ决定以后显C那一个视图,以及实现必要的安全需求。对于把视图昄以及其他功能实现集中C个主要的对象中,修改变得很容易,对应用的修改Q可以在所有视图中反映出来?/P>
c. 要点
q个模式对于需要在多个含有动态数据的面之间q行复杂D的系l来_是很有效的?BR> q个模式对于要在所有页面中都包含模板,转换{的应用来说Q也是很有效的?BR> ׃视图的选择集中在前端控制器上,因此Q视囄D变得更加Ҏ理解和便于配|?BR> 视图重用和变更会更加Ҏ?BR> 视图之间的复杂交互,使得控制器变得复杂。从而,当应用发展的时候,控制器将变得难以l护。不q,大部分情况下可以用XML映射来解冟?BR> 实现应用要求的安全性检验变得很单?BR> q个模式不适合型的,只显C静态内容的应用?/P>
d. 样例
RequestMappings.xml 文g映射了传入的hQ处理器以及下一个页?/P>
useRequestHandler="true"
requiresSecurityCheck="true"
nextScreen="screen2.jsp">
com.blah1.blah2.blah3.request1Handler
以上q个文g是控制器的指定配|,控制器的代码如下Q?BR> FrontControllerImpl.java 利用上面的XML实现了控制器
// all required imports
// exceptions to be caught appropriately wherever applicable
public class FrontControllerImpl extends HttpServlet {
// all required declarations, definitions
private HashMap requestMappings;
public void init() {
// load the mappings from XML file into the hashmap
}
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException
{
doGet(request, response);
}
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
String currentPage= request.getPathInfo();
// get all mapping info for "currentPage" from the hashmap
// if "securityCheckRequired = true", do the security check
// if "useRequestHandler = true", pass on the incoming request to the specified handler
// forward the results to the given "nextScreen"
}
}
用这U方法实现的控制器将很容易维护,当应用有新的变动的时候,只要修改XML文gp解决了。前台控制模式将使在视图和控制器之前有复杂交互的J2EE应用变得单?/P>
3. Session Facade
a. 问题
前台控制l出了一个基于MVC的,能有效管理用户与J2EE应用之间q行的复杂交互。这个模式可以处理面的现实顺序和用户的ƈ发请求变得简单。ƈ且增加和改变页面现实变得更加容易?BR>另外一个常见的问题是,当EJB或者业务逻辑发生变化的时候,应用的客L也必随之改变。我们来看一下这个问题?BR> 一般来_Z表现一个̎户中的用P我们使用一个业务逻辑来表C̎户中的信息,象用户名和口令,再用一个EJB来管理用L个h信息Q象爱好Q语a{。当要创Z个新的̎h者修改一个已l存在的账号Ӟ必须讉K包含账号信息的EJBQ读取个Z息,修改q且保存Q这L一个流E?BR> 当然Q这只是一个非常简单的例子Q实际情况可能比q个复杂的多Q象查看用户定制了哪些服务,验客户信用卡的有效性,存放订单{。在q个案例中,Z实现一个完整的程Q客L必须讉K账户EJB来完成一pd适当的工作。下面的例子昄了一个Servlet客户端如何来控制一个用戯单?BR>A servlet that does the workflow required for placing an order
// all required imports;
// exceptions to be caught appropriately wherever applicable;
// This servlet assumes that for placing an order the account and
// credit status of the customer has to be checked before getting the
// approval and committing the order. For simplicity, the EJBs that
// represent the business logic of account, credit status etc are
// not listed
public class OrderHandlingServlet extends HttpServlet {
// all required declarations, definitions
public void init() {
// all inits required done here
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
// other logic as required
// Get reference to the required EJBs
InitialContext ctxt = new InitialContext();
Object obj = ctxt.lookup("java:comp/env/ejb/UserAccount");
UserAccountHome acctHome = (UserAccountHome)
PortableRemoteObject.narrow(obj, UserAccountHome.class);
UserAccount acct = acctHome.create();
obj = ctxt.lookup("java:comp/env/ejb/CreditCheck");
CreditCheckHome creditCheckHome = (CreditCheckHome)
PortableRemoteObject.narrow(obj, CreditCheckHome.class);
CreditCheck credit = creditCheckHome.create();
obj = ctxt.lookup("java:comp/env/ejb/Approvals");
ApprovalsHome apprHome = (ApprovalsHome)
PortableRemoteObject.narrow(obj, ApprovalsHome.class);
Approvals appr = apprHome.create();
obj = ctxt.lookup("java:comp/env/ejb/CommitOrder");
CommitOrderHome orderHome = (CommitOrderHome)
PortableRemoteObject.narrow(obj, CommitOrderHome.class);
CommitOrder order = orderHome.create();
// Acquire the customer ID and order details;
// Now do the required workflow to place the order
int result = acct.checkStatus(customerId);
if(result != OK) {
// stop further steps
}
result = credit.checkCreditWorth(customerId, currentOrder);
if(result != OK) {
// stop further steps
}
result = appr.getApprovals(customerId, currentOrder);
if(result != OK) {
// stop further steps
}
// Everything OK; place the order
result = order.placeOrder(customerId, currentOrder);
// do further processing as required
}
}
以上的代码显CZ一个单个的客户端。如果这个应用支持多U客L的话Q必Mؓ每一个客L制定一U处理方法来完成工作程。如果有一个EJB的实现流E需要改变的话,那么所有的参与q个程的客L都需要改变。如果不同的EJB之间的交互需要改变的话,所有的客户端都必须知道q一点,如果程中需要增加一个新的步骤的话,所有的客户端也必须随之修改?
q样一来,EJB和客L之间的改变变得非常困难。客L必须Ҏ个EJB分开q行讉KQ致使网l速度变慢。同P应用复杂,ȝ大?/P>
b. 的解x?BR> 解决q个问题的方法是Q把客户端和他们使用的EJB分割开。徏议适用Session Fa?ade模式。这个模式通过一个Session BeanQؓ一pd的EJB提供l一的接口来实现程。事实上Q当客户端只是用这个接口来触发程。这P所有关于EJB实现程所需要的改变Q都和客L无关?BR> 看下面这个例子。这D代码用来控制与客户相关的订单的处理Ҏ?BR>// All imports required
// Exception handling not shown in the sample code
public class OrderSessionFacade implements SessionBean {
// all EJB specific methods like ejbCreate defined here
// Here is the business method that does the workflow
// required when a customer places a new order
public int placeOrder(String customerId, Details orderDetails)
throws RemoteException {
// Get reference to the required EJBs
InitialContext ctxt = new InitialContext();
Object obj = ctxt.lookup("java:comp/env/ejb/UserAccount");
UserAccountHome acctHome = (UserAccountHome)
PortableRemoteObject.narrow(obj, UserAccountHome.class);
UserAccount acct = acctHome.create();
obj = ctxt.lookup("java:comp/env/ejb/CreditCheck");
CreditCheckHome creditCheckHome = (CreditCheckHome)
PortableRemoteObject.narrow(obj, CreditCheckHome.class);
CreditCheck credit = creditCheckHome.create();
obj = ctxt.lookup("java:comp/env/ejb/Approvals");
ApprovalsHome apprHome = (ApprovalsHome)
PortableRemoteObject.narrow(obj, ApprovalsHome.class);
Approvals appr = apprHome.create();
obj = ctxt.lookup("java:comp/env/ejb/CommitOrder");
CommitOrderHome orderHome = (CommitOrderHome)
PortableRemoteObject.narrow(obj, CommitOrderHome.class);
CommitOrder order = orderHome.create();
// Now do the required workflow to place the order
int result = acct.checkStatus(customerId);
if(result != OK) {
// stop further steps
}
result = credit.checkCreditWorth(customerId, currentOrder);
if(result != OK) {
// stop further steps
}
result = appr.getApprovals(customerId, currentOrder);
if(result != OK) {
// stop further steps
}
// Everything OK; place the order
int orderId = order.placeOrder(customerId, currentOrder);
// Do other processing required
return(orderId);
}
// Implement other workflows for other order related functionalities (like
// updating an existing order, canceling an existing order etc.) in a
// similar way
}
在模式允许的情况下,Servlet代码很Ҏ实现?BR>// all required imports
// exceptions to be caught appropriately wherever applicable
public class OrderHandlingServlet extends HttpServlet {
// all required declarations, definitions
public void init() {
// all inits required done here
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
// other logic as required
// Get reference to the session facade
InitialContext ctxt = new InitialContext();
Object obj = ctxt.lookup("java:comp/env/ejb/OrderSessionFacade");
OrderSessionFacadeHome facadeHome = (OrderSessionFacadeHome)
PortableRemoteObject.narrow(obj, OrderSessionFacadeHome.class);
OrderSessionFacade facade = facadeHome.create();
// trigger the order workflow
int orderId = facade.placeOrder(customerId, currentOrder);
// do further processing as required
}
}
p上面昄的,客户端的逻辑变得非常单。流E中的Q何改变只要修Ҏ式中的一处地方就可以了。客L可以仍旧使用原来的接口,而不必做M修改。同Pq个模式可以用来响应其他处理器的程处理。这让你能用同样的模式来处理不同客户端的不同程。在q个例子中,模式提供了很好的伸羃性和可维护性?/P>
c. 要点
既然q种模式不涉及到数据讉KQ就应该用Session Bean来实现?BR> 对于用简单接口来实现复杂EJB的子pȝ来说Q是一个理想的选择?BR> q个模式不适用于无程处理的应用?BR> q个模式可以减少客户端于EJB之间的通信和依赖?BR> 所有和EJB有关的交互,都有同一个Session Bean来控Ӟ可以减少客户端对EJB的误用?BR> q个模式可以使支持多cd客户端变得更Ҏ?BR> 可以减少|络数据传递?BR> 所有的服务器端的实现细节都对客L隐藏Q在改变发生后,客户端不用重新发布?BR> q个模式可以同样看成一个集中处理器来处理所有的安全或日志纪录?/P>
4. Data Access Object
a. 问题
目前为止Q你看到的模型都是用来构建可伸羃的,易于l护的J2EE应用。这些模式尽可能的把应用在多个层上来实现。但是,q有一点必d调:EJB的数据表现。它们包括象EJBq样的数据库语言。如果数据库有改变的话,相应的SQL也必L变,而EJB也必随之更新?BR> q些常见问题是Q访问数据源的代码与EJBl合在一Pq样致代码很难l护。看以下的代码?BR>An EJB that has SQL code embedded in it
// all imports required
// exceptions not handled in the sample code
public class UserAccountEJB implements EntityBean {
// All EJB methods like ejbCreate, ejbRemove go here
// Business methods start here
public UserDetails getUserDetails(String userId) {
// A simple query for this example
String query = "SELECT id, name, phone FROM userdetails WHERE name = " + userId;
InitialContext ic = new InitialContext();
datasource = (DataSource)ic.lookup("java:comp/env/jdbc/DataSource");
Connection dbConnection = datasource.getConnection();
Statement stmt = dbConnection.createStatement();
ResultSet result = stmt.executeQuery(queryStr);
// other processing like creation of UserDetails object
result.close();
stmt.close();
dbConnection.close();
return(details);
}
}
b. 的解x?BR> Z解决q个问题Q从而让你能很方便的修改你的数据讉K。徏议用DAO模式。这个模式把数据讉K逻辑从EJB中拿出来攑օ独立的接口中。结果是EJB保留自己的业务逻辑ҎQ在需要数据的时候,通过DAO来访问数据库。这L模式Q在要求修改数据讉K的时候,只要更新DAO的对象就可以了。看以下的代码?BR>A Data Access Object that encapsulates all data resource access code
// All required imports
// Exception handling code not listed below for simplicity
public class UserAccountDAO {
private transient Connection dbConnection = null;
public UserAccountDAO() {}
public UserDetails getUserDetails(String userId) {
// A simple query for this example
String query = "SELECT id, name, phone FROM userdetails WHERE name = " + userId;
InitialContext ic = new InitialContext();
datasource = (DataSource)ic.lookup("java:comp/env/jdbc/DataSource");
Connection dbConnection = datasource.getConnection();
Statement stmt = dbConnection.createStatement();
ResultSet result = stmt.executeQuery(queryStr);
// other processing like creation of UserDetails object
result.close();
stmt.close();
dbConnection.close();
return(details);
}
// Other data access / modification methods pertaining to the UserAccountEJB
}
现在你有了一个DAO对象Q利用这个对象你可以讉K数据。再看以下的代码?BR>An EJB that uses a DAO
// all imports required
// exceptions not handled in the sample code
public class UserAccountEJB implements EntityBean {
// All EJB methods like ejbCreate, ejbRemove go here
// Business methods start here
public UserDetails getUserDetails(String userId) {
// other processing as required
UserAccountDAO dao = new UserAccountDAO();
UserDetails details = dao.getUserDetails(userId);
// other processing as required
return(details);
}
}
M数据源的修改只要更新DAO可以解决了。另外,Z支持应用能够支持多个不同的数据源cdQ你可以开发多个DAO来实玎ͼq在EJB的发布环境中指定q些数据源类型。在一般情况下QEJB可以通过一个Factory对象来得到DAO。用q种Ҏ实现的应用,可以很容易的改变它的数据源类型?/P>
c. 要点
q个模式分离了业务逻辑和数据访问逻辑?BR> q种模式特别适用于BMP。过一D|_q种方式同样可以UL到CMP中?BR> DAOs可以在发布的时候选择数据源类型?BR> DAOs增强了应用的可~性,因ؓ数据源改变变得很Ҏ?BR> DAOsҎ据访问没有Q何限Ӟ甚至可以讉KXML数据?BR> 使用q个模式导致增加一些额外的对象Qƈ在一定程度上增加应用的复杂性?/P>
We explore this in more detail in Chapter 5, in the section "What Is a Pattern?" on page 118.
This well-known pattern definition comes from Christopher Alexander in A Pattern Language [Alex2]:
Each pattern is a three-part rule, which expresses a relation between a certain context, a problem, and a solution.
?SPAN class=docEmphasis>Christopher Alexander
Alexander expands his definition further, and noted patterns figure Richard Gabriel [Gabriel] discusses this definition in more detail [Hillside]. Gabriel offers his own version of Alexander's definition as applied to software:
Each pattern is a three-part rule, which expresses a relation between a certain context, a certain system of forces which occurs repeatedly in that context, and a certain software configuration which allows these forces to resolve themselves. [See A Timeless Way of Hacking.]
?SPAN class=docEmphasis>Richard Gabriel
This is a fairly rigorous definition, but there are also much looser ones. For example, Martin Fowler offers the following definition in Analysis Patterns [Fowler2]:
A pattern is an idea that has been useful in one practical context and will probably be useful in others.
?SPAN class=docEmphasis>Martin Fowler
As you can see, there are many definitions for a pattern, but all these definitions have a common theme relating to the recurrence of a problem/solution pair in a particular context.
Some of the common characteristics of patterns are as follows:
Patterns are observed through experience.
Patterns are typically written in a structured format (see "Pattern Template" on page 129).
Patterns prevent reinventing the wheel.
Patterns exist at different levels of abstraction.
Patterns undergo continuous improvement.
Patterns are reusable artifacts.
Patterns communicate designs and best practices.
Patterns can be used together to solve a larger problem.
Many great minds have spent a significant amount of time attempting to define and refine the notion of a software pattern. Suffice it to say, we do not presume to be great minds, nor do we wish to spend time expanding these discussions. Instead, we attempt to be true to aspects of these various definitions, focusing on the most simple and recurring theme in each.
---------------from Core J2ee Patterns