??xml version="1.0" encoding="utf-8" standalone="yes"?> q有是l心体味了一下OO的设计原则,q些原则是凌驾于模式之上的,也就是更宏观的原则?/P>
其中Q最高指导的一个就是“开Q闭”原则。别的原则,里氏代换原则、依赖倒置原则、组?聚合复用原则和_c特法则都是Z辑ֈ“开Q闭”原则而出现的规则?/P>
q些原则告诉我很多东西,聚焦于一点就是要“面向抽象”来做一切事情?/P>
分析对象的时候,要多分析设计“抽象”的概念Q对象之间的联系要多Z抽象的概念而不是具体,q样具体才能能够变化Q这h是开闭。用我自q话就是要“游C 抽象”?/P>
q里有一个我必须C的就是,在封装变化时候,多用聚合/l合Q少用ѝ在装原子变化q且是同cd对象时才用承,别的都尽量用聚合/l合。而且量不要用多U承,多l承一般意味着有两U变化脉l,可能的话Q让两种变化脉络独立演化。很明显Q一独立演化Q又要聚?l合了?/P>
q有一个必记住的是:q用抽象以后Q客L的用发生了巨大的变化。不再是指那儿用那儿。而是要做更多的准备工作,因ؓq用抽象Q本w就把具体“组合”的职责推迟C用的阶段。那谁用,肯定是客L。所以,客户端的使用要革新。要习惯用工厂,习惯把一pd的抽象定具体了,q按照一定方式“组合”v来用。而且Q最l要善于用接口来调用Ҏ?BR>
]]>
用小飞推荐的一个工L了个图,如下Q?BR>
MARCO ZHANG 2006q??7?:18:57
]]>
׃n?/SPAN>idea是生zM最基本?/SPAN>ideaQ不必有意的使用Q到处已l存在了。在生活中,大部分事物都是被多h多次使用的,q都是共享的实际应用?/SPAN>
之于OO的共?/SPAN>
OO中的׃nQ无非就是说让“对象”也能被“多人多ơ”(q里的“h”也无非是q程、线E而已Q用,更详l的_是要让对象的生存空间更大一些,生存周期更长一些?/SPAN>
自己闷个儿脑子,提炼Z几个需要用共享的环境Q?/SPAN>contextQ,也可以说是原因吧Q?/SPAN>
1. Z保持“对象”的一_我们需要共享。例如,“国家主席”就一个,不能多了Q如果多了,隑օ决策混ؕ?/SPAN>
2. Z控制“对象”的存储I间Q我们需要共享。毕竟,目前来说Q系l的memoryq是~程时最珍贵的资源?/SPAN>
3. Z优化“对象”的创徏消耗,我们需要共享。如果,一个对象的创徏q程消耗太大,pȝ不能支持频繁的创建,׃n的用它也是一个好L?/SPAN>
4. {等?/SPAN>
而在实际的应用中Q往往我ƈ没有l想“我Z么用共享?”,已经不自觉的q了;如果真的认真分析hQ基于的环境也是多样Qƈ不会只是上面的其中一U?/SPAN>
常用的“共享”方法或模式Q我曄用过的,知道的不多,望谅解)Q?/SPAN>
1. ?/SPAN>Singleton模式”:一?/SPAN>class׃个对象实例,大家都用它,满context1?/SPAN>
2. ?/SPAN>pool技术”:只提供一定数目的对象Q大安用他们,实现context2?/SPAN>context3?/SPAN>
3. ?/SPAN>flyweight模式”:一?/SPAN>class的一个状态就一个对象实例,实现一个状态对象的׃nQ实?/SPAN>context2?/SPAN>context3?/SPAN>
使用时要注意的地方:
1. 定׃n?/SPAN>scope。例如,?/SPAN>Java Web Application中就是选择?/SPAN>pageQ?/SPAN>sessionq是applicationQ当然也可以?/SPAN>jvmU别?/SPAN>static?/SPAN>
2. 认thread safe。当׃n的对象可能被多个U程׃nӞq是不可以回避的问题?/SPAN>
3. 应对对象状态的变化。一旦共享的对象发生了变化,我们怎么处理Q改变之Q舍弃之Q也是我们需要确定的?/SPAN>
目中的应用Q?/SPAN>
目需求:
为学校的同学提供Web查询Q查询的内容有很多。其中,“查课表”、“查考表”是最为关键的需求,以后可能q要提供“查询空闲自习教室”的功能?/SPAN>
在这些查询中Q有一个共同点Q就是都涉及“教室”这一对象。“查课表”时要告诉同学在哪个教室上课Q“查考表”时要告诉同学在哪个教室考试Q等{?/SPAN>
数据库设计:
对于“查课表”用例,有关的数据库设计如下Q?/SPAN>
对象层的设计Q?BR>
学生每查询一门课E的课表Q系l就?/SPAN>sql查询“视?/SPAN>V_LESSONSCHEDULE”,q而生成一?/SPAN>LessonSchedule对象Q然后返回给用户昄。当Ӟ在生成这?/SPAN>LessonSchedule对象的过E中Q属于它?/SPAN>Classroom对象Q以及更׃步的Building?/SPAN>Area对象都会生成。下面就是这个过E的序图:
因此Q每生成一个“课表”对象(LessonScheduleQ或“考表”对象(ExamScheduleQ时Q都要:
1. 查数据库中的教室、教学楼、校区的信息Q?/SPAN>
2. 创徏相应的“教室对象”(包括了属于它的“教学楼”对象和“校区”对象)?/SPAN>
考虑׃n“教室”对?/SPAN>
“教室”对象一旦可以生成以后,完全可以l后l共享用,不必要每个查询都要去生成?/SPAN>
详细说是Z下面的考虑Q?/SPAN>
1. q类查询用例Q查课表Q查考表Q发生的频繁很高很高Q也是_一旦让用户查v来,pȝ中会产生大量的“教室”对象这cd象,应该说会占很大的内存I间?/SPAN>
2. ׃n“教室”对象后Q可以减对数据库的查询ơ数Qƈ降低了查询粒度(以前是基于二U视图查询,现在可以Z基本表查询)Q提高了一Ҏ据库查询性能?/SPAN>
当然Q同时我脑袋中也有反对的声音Q?/SPAN>
1. 虽说Q这cL询会产生很多相同的“教室”对象,但是JVM本生提供的垃圑֛收功能完全可以处理它。除非,“同时”有很多很多q类对象都在被用,Ҏ回收不了Q才会造成内存短缺?/SPAN>
2. 如果Q我以某U共享机制让q些“教室”对象,在系l中存在下来Qg长了生命周期Q了。而它们本w的数目很多(如,我们学校有××Q,可能q没有等你用上,pȝ已经挂了。另外,如果不是同时有很多查询需要,我留q么多“教室”对象在pȝ里,反而是占了内存Q而不是优化了内存的用?/SPAN>
1. 所有模式的通病――“增加了复杂度”?/SPAN>
l论Q?/SPAN>
l过我们分析Q系l对于“教室”对象的重复使用Q频J程度非帔R。一般,?/SPAN>10个h同时使用Q就?/SPAN>5个h左右涉及的用例要使用“教室”对象。把它共享v来,q是有一定必要的?/SPAN>
q一步考虑Q?/SPAN>
而实际上Q我们可以进一步共享下去:
除了让客L׃n“教室对象(ClassroomQ”外Q还可以让“教室对象”共享“教学楼对象Q?/SPAN>BuildingQ”,和让“教学楼对象”共享“校区对象(AreaQ”?/SPAN>
因此Q最l的׃n是在三上都实现?/SPAN>
Flyweight模式Q?/SPAN>
特点Q?/SPAN>
书上_“n元模式可以ɾpȝ中的大量粒度对象被׃n使用”。第一Q对象出现的量要大,惛_q比较好理解Q很用的也就没有必要׃n了;W二Q要粒度,我比较纳PN对于大粒度对象就不行吗?可能书上认ؓQ大_度对象的共享已l占了比较大的空_没有对象那么有效吧?/SPAN>
另外Q书上还_要用“n元模式”,被共享的对象的状态(cdQ要比较固定Q这样就可以为每一个状态仅仅创Z个对象。当Ӟ如果每次使用对象Ӟ对象的状态都是不一LQ那根本不存在׃nq些对象的必要了?/SPAN>
联系目思考:
Z上面寚w目的分析Q“教室”、“教学楼”、“校区”对象都是在pȝ中会被大量用的对象Q而且_度的确比较;q且它们有固定的cdQ而且不易改变。如校区对象Q暂时就?/SPAN>4个。教学楼可能40Q?/SPAN>50个左叟뀂很适合“n元模式”的使用环境?/SPAN>
定׃n方式Q?/SPAN>
1. 定׃n对象?/SPAN>scope。在?/SPAN>webE序中,q些׃n对象?/SPAN>scope理应?/SPAN>applicationQ而更单的一个作法就是把q些对象设ؓstaticQ我选择后者?/SPAN>
2. 认thread safe。这些对象是可能被多?/SPAN>servlet讉K的,也就是有可能存在多线E访问。但是,׃q些对象的可变性很差,一旦创建就不大可能变化。因此,我决定把q写׃n对象设计成不变模式的Q一旦创建就只会被读取,而不会改写,q样׃存在多线E控制的问题了?/SPAN>
3. 应对对象状态的变化Q如某个教室的类型变了。这里采取的是舍弃的ҎQؓ每个工厂d了一个清I方法―?/SPAN>clear()Q用于清I已l生成的׃n对象?/SPAN>
设计cdQ?/SPAN>
当然Q也可以把这些工厂都设计成?/SPAN>Singleton模式”的Q它们只会有一个实例?/SPAN>
客户端用:
׃׃n的对象都被包含在了“课表”和“考表”对象里Q不会被客户端直接访问,因而不会对客户端的使用有Q何媄响:
实例代码
oneClassroom?/SPAN>twoClassroomQ?/SPAN>oneBuilding?/SPAN>twoBuildingQ?/SPAN>oneArea?/SPAN>twoArea׃都是32可E的东西Q根据我们的设计意图Q应该实现共享?/SPAN>
而实际上Q它们每对的是同一个对象的引用。因此,实现了预期的设想?/SPAN>
ReviewQ?/SPAN>
在本目中,当第一ơ设计出来的时候,我们发现了某些对象恰好有׃n的需要?/SPAN>
而更多的实际情况是,q些需要共享的“信息或状态”在设计中ƈ不是那么恰好的表Cؓ“一个对象”的_度Q而是要不包含在一个对象内部,要不p几个对象。在q样的情况下Q共享的设计更多是发生在代码重构阶段而不是第一的设计阶Dc当ӞZ׃n对象而做出的代码重构Q最重要的一步就是把需要共享的“信息或状态”设计成为新的对象?/SPAN>
对于Q“n元模式”来_是要把需要共享的“信息或状态”设计成“n元对象”。别的在此就不说了,因ؓ我也不懂了,呵呵?/SPAN>
MARCO ZHANG 2006q??3?3:48:49
预料中的日志3暂时生不出来,话说难好Q别夭折pQ有Ҏ价哦Q呵c?/SPAN>
因ؓ有些东西q没有想清楚。那先搞个四吧Q这个东西还是清楚那么一点的?/FONT>
目需?/SPAN> |
使每?/SPAN>servlet能对用户讉K权限q行查。简单来_是要给每个servlet加个锁,有钥匙的用户才能讉K?/SPAN> |
而项目中用户所谓的讉K权限是基于他拥有的角艌Ӏ也是_servlet对用戯问权限的查,是对他所拥有角色的检查。暂Ӟ每个用户只能拥有一个角艌Ӏ?/SPAN>
目的角色很多,但是?/SPAN>web端暂时只有如下的三种Q?/SPAN>
目暂时角色 |
游客Q学生,教师 |
既然q样Q?/SPAN>servlet的加锁方式就有:
servlet加锁方式 |
游客Q学生,教师Q?BR>游客或学生,游客或教师,学生或教师, |
注:上面的“游客”就是“游客角色有权访问”的意思,依此cL?/FONT>
q里只有关系“或”(||Q,如果一?/SPAN>servlet的加锁方式是“学生或教师”,也就是说拥有学生或教师角色的用户都可以访问它。关pZ与”(&&Q在q里不太可能存在Q因为没有需求说Q某?/SPAN>servlet一定只能由“既是学生又是教师的用户”才能访问,而且前面也说了,暂时一个用户“有且只有”一个角艌Ӏ?/SPAN>
So we can get following functionQ?BR>
|
q用OO的最基本方式Q就是封装对象。既然有?/SPAN>servlet“看门”的责QQ那把q个责Q装成一个对象,用个俗名Q?/SPAN>validator?/SPAN>
接着Q运用共性和个性的分析ҎQ既然有那么多种不同的看门方式(加锁方式Q,那就搞一个接口,然后让各U“不同”都实现q个接口QŞ成子cR那有下面的图Q?/FONT>
可以看到Q由于有7个加锁方式,那就要有7个子cR每个子cL据自己逻辑override接口?/SPAN>validateҎ?/SPAN>
q样Q对于一?/SPAN>servletQ想让它上什么样的锁Q只要让它拿到对应的子类的引用即可,如下图中?/SPAN>ClientServletQ我们规定只能有“学生或教师”才能访问它。它的部分代码便是:
xQ第一个解x案就出来了?/FONT>
不 |
validator接口的子cL目随“角色数”成“指数”增长,数量太多Q而且子类中重复逻辑的代码很多,如?/SPAN>Student_Or_Teacher_Validator”就重复了?/SPAN>Student_Validator”和?/SPAN>Teacher_Validator”的逻辑Q万一?/SPAN>Student_Validator”的逻辑要改Q只要涉?/SPAN>Student的子c都要跟着改,l护上不方便?/SPAN> |
q一步改q的可能 |
?/SPAN>Student_Or_Teacher_ValidatorcZ的validateҎ很大E度上就是?/SPAN>Student_ValidatorcZ的validateҎ和?/SPAN>Teacher_ValidatorcZ的validateҎ“或操作”出来的l果?/SPAN> 因此Q能不能考虑由?/SPAN>Student_ValidatorcȝvalidateҎ”和?/SPAN>Teacher_Validator?/SPAN>validateҎ?B style="mso-bidi-font-weight: normal">动态的构?/SPAN>一个功能如?/SPAN>Student_Or_Teacher_ValidatorcȝvalidateҎ”?/SPAN> q样Q?/SPAN>Student_Or_Teacher_Validator”就可以省略了,只剩下一些原子的“角色类”。要实现Student_Or_Teacher_Validator的验证功能,?/SPAN>Student_Validator?/SPAN>Teacher_Validator装配一下就可以了?/SPAN> |
l论Q需要根据实际情况,动态的改变Q装配)?/SPAN>Validator接口对象”的validateҎ?/SPAN>
W一个火花就是“装饰模式”。它可以让客L动态的l装对象的方法。真奇Q?/FONT>
注:上图中出CAndRelation?/SPAN>And的一pd角色c,可以暂时省略不看Q因为前面说了,现在q没有“与”关p这个需求。只?/SPAN>Or的就可以?/SPAN>
我喜Ƣ叫q个模式为洋葱模式,一层包一层,最外层对象某方法的逻辑是由内部一层一层对象的同一Ҏl合出来的?/FONT>
使用了这个模式,便不用如一版那样实现那么多子类Q只要实现几个“角色类”即可,q里?/SPAN>3个(学生角色Q?/SPAN>Or_Student、教师角ԌOr_Teacher、游客角ԌOr_GuestQ。所有一版中别的子类都可以由q?/SPAN>3个组装出来?/SPAN>
如要生成一版中?/SPAN>Student_Or_Teacher_Validator对象Q可以用Or_Student?/SPAN>Or_Teacher两个对象?/SPAN>Or”出来:
如要生成一版中?/SPAN>Guest_Or_Student_Or_Teacher_Validator对象Q可以用Or_Student?/SPAN>Or_Teacher?/SPAN>Or _Guest三个对象?/SPAN>Or”出来:
q种一层包一层的new方式Q是不是很像z葱Q第一ơ看是很不习惯,看多了就觉得习惯了?/SPAN>
一版中Q客L要什么样的验证类Q就直接使用具体cR?/FONT>
二版中,客户端要什么样的验证类Q它的工作多了那么一丁点Q它需要先l装一下,正如上面的例子。这U组装的Ҏ很易于理解和使用Q不会给客户端带来Q何的不便。如果实在觉得客Ll装不出来(?/SPAN>B客户端)Q也可以搞个工厂l它supplyQ?/SPAN>
相对一版最明显的优点就是类的数目少了很多?/FONT>
一版不是说“指数”吗Q这里只是线性的了。假设某一天系l拓展到?/SPAN>10个角Ԍ一版就?/SPAN>2?/SPAN>10ơ方那么多个Q也是1024个验证类?/SPAN>
而二版还?/SPAN>10个角色类Q别的都可以在客L使用的时候,动态的l装
更重要的是代码结构好了,重复逻辑了。每个逻辑都以最atomic的大放到最应该的地斏V?/SPAN>
q而,l护的代价少多了。如某天“教师角艜y的验证逻辑发生了变化,只要改动Or_Teacher一个地方即可?/SPAN>
MARCO ZHANG 2006q??8?3:49:56
研究生院目中“明䏀用了“工厂方法模式”。其实在遇到具体问题的时候,即我们不知道有q个模式存在Q我们也肯定会造一个类似的东西出来。但是,肯定没有书上的那么好Q那么全面。我惌是看书的好处吧?/SPAN>
工厂Ҏ出现的必Ӟ我的理解Q一个很狭隘q幼E理的h的理解)
刚开始用这个东西的时候,只是感觉是单U的一U模式,用于创徏需要的对象?/SPAN>
但是随着使用和思考的深入Q越发发现它l我的启CZ只在于单U的对象创徏Q而是告诉我应该怎么理解“品”,怎么得到“品”,怎么消费“品”,以至于以后怎么设计“品”?/SPAN>
下面q个U烦是我对它出现必然性的理解Q?/FONT>
1. “针Ҏ口编E?/FONT>
q是OO世界中经典的规范Q不你dq是被动Q你天天都在用这个东ѝ?/SPAN>
接口是共性的表示Q在对象的世界中Q共性和个性的辩证关系是最重要的关pR在万千的对象中Q通过它们之间的共性和个性,可以形成最基本对象层架构?/SPAN>
假设我们的讨论域中有以下一些对象:“学生”、“大学生”、“小学生”、“中学生”;我们不用l想Q学q一?/SPAN>OO的h都可以ؓq些耳熟能详的对象们Q通过个性和共性的关系得出下面的结构图?BR>
把这些对象之间的关系定义成这h理成章的?/SPAN>
下一步肯定是让客L“用”这个接口啦。也是如下图:
2. 接口和具体类的矛?/FONT>
勿庸|疑Q我们只希望Client跟接?/SPAN>Student打交道,让它Ҏ׃知道Student有哪些子c,l对不希望直接跟它们打交道?/SPAN>
但这里出现的困难是,接口都是“假”的Q都是由具体c?/SPAN>upcast的?/SPAN>
如果Client要用接?/SPAN>StudentQ?/SPAN>Client中必M出现下面的代码:
Student marco = new Small_Student();
只要一出现q个代码Q就说明Client不只?/SPAN>Student打交道了Q它知道?/SPAN>Small_Studentc,q违反了我们预先的想法?/SPAN>
3. 䏀h”帮我去创徏“接口对象?/FONT>
从上图体现出来的l构看,Client只想?/SPAN>Student打交道的目的是实C了的了?/SPAN>
最单的Ҏ是扑֏外的“帮手”去帮我生成q个“接口对象”。这个帮手它知道“接口对象”的具体cdQ但是它为客L提供的却一定是“接口类型”。这q合我们的要求了。如图:
q样Q?/SPAN>Client可以既用到了?/SPAN>Student接口对象”,又不用因为“只有具体类才能创徏对象”的规则Q而必d其子cȝ构有完全的了解。它成功的解决了2中的矛盾?/SPAN>
而“负责创建具体类对象的Q务”全部都落在了这个“帮手”n上,q个“帮手”(Student_FactoryQ就是工厂模式中的工厂类Q更具体的说Q它是“简单工厂模式”中的“简单工厂类”?/SPAN>
我觉得,即一炚w不知道工厂模式,一旦我遇到?/SPAN>2里说的矛盾,我也会用q样的方法处理?/FONT>
4. q个“帮手”不W合“开Q闭原则?/FONT>
q个帮手的确不错了,而且一跃成为系l中最重要的对象了Q所有“创建具体类的逻辑”都放进MQ也是因ؓ重要Q万一挂了不就惨了?/FONT>
再者,它不W合“开Q闭”原则,我不能在不修改这个帮手的情况下添加Q何一个品。在q个例子中就是,如果那天我有病非要加一个“幼儿园学生”进来,那您必M改这个“帮手”的代码了,q个“帮手”现在就变成Version2Q如下图Q了Q这个二版的帮手Q可以在“代码”层实现对一版(q没有添加幼儿园学生之前)的通用Q但q种保证在“开Q闭”原则看来,q是不够的,不保险的Q它要的是在cȝl构上的保证。声明一下,q是我很感性的理解Q不正确的可能性很高?BR>
5. 让“帮手”也多?/FONT>
q里可以试让“帮手”也多态一下,q样“每U学生类创徏的Q务”都被分zֈ了多态出来的cMM。这Ӟ再有新的学生cd加进来,d一个对应的帮手可以了。这栯然类多了一些,但是W合“开Q闭”原则,书上UC为“工厂方法模式”。如图:
假如Client现在要用一个小学生Q代码如下:
在这里还是要两点Q?/FONT>
n 虽然实际?/SPAN>Client的确使用了一个小学生对象Q?/SPAN>Small_StudentQ,但这?/SPAN>Client也认为它是Student对象Q这里一定要?/SPAN>Student接口来隐藏它的具体类?/SPAN>
n 另外Q却不需要用Student_Factoryq个接口来隐藏它的具体类Q因为,Client实际是通过“选择它的具体cZ这招儿来“选择创徏的学生类型”。这里的Student_Factory更多的功能不是“隐藏”具体类Q而是“规范”具体类?/SPAN>
目实践
扯E到此Q该联系我们的项目啦?/SPAN>
׃是做研究生院的项目,其中巨大的需求就是要让同学能在网上提交各U申请单Q申请退学的Q申误{专业的,甌复学的,甌保留学籍的,除了甌x友的外,应有有?/SPAN> 对于q些单子Q用最最基本OO思维Q根据共性个性分析方式,抽象Z个申请单接口Q和若干的具体类?/SPAN>
当然Q除了概念上感性上d以外Q在目中它们也要“真”的有巨大的共性才行,如都要提交,修改Q删除,审核Q打印等功能?/SPAN>
靠,既然都这样了Q肯定就用一接口规定它了?/SPAN>
惛_q里Q加上有了上面的思考,不用说了Q就用工厂方法模式啦?BR>
图中Ent_Shift是甌单接口。等于前面分析的Student接口。还可以看到有很多具体类Q前面例子中是代表各U学生,q里是代表各种甌单?/SPAN>
当然Q这里还有很多工厂,也就是前面一直叫的“帮手”?BR>
Bean_Shift是工厂接口Q相当于前面?/SPAN>Student_Factory接口。还有很多的具体cd是生产各U申请单的工厂类?/SPAN>
下面是使用“申请单工厂Ҏ模式”的一D客L代码Q?BR>
升华
个h比较同意?/SPAN>Design Pattern Explained》中作者讲的,要用好很多的模式Q其中都有一个思\Q就是用接口或抽象类来隐藏子cȝ不同?/SPAN>
我每当看到这Ӟ老是会被一U思\困扰――?/SPAN>new只能new具体cdQ这咋能隐藏呢,q隐藏还有什么用呢?”?/SPAN>
作者仿佛也曄有过我的q个?/SPAN>B苦恼Q它的解x法就是:Ҏ在用对象的时候,特别是设计阶D,量不去惛_象是在那里被new的。他认ؓQ反正有了工厂模式后Q你L办法把他?/SPAN>new出来的?/SPAN>
所以,我用了工厂模式后更发的启发是Q以后设计的时候不要想一?/SPAN>Client怎么创徏一个对象,管攑ֿ大胆的先l箋惻I直接使用好了。反正最后我q有工厂模式呢?/SPAN>
因此俺的副标题才是?/SPAN>Ignore how they were created”,呵呵?BR> MARCO ZHANG 2006q??6?:52:10