??xml version="1.0" encoding="utf-8" standalone="yes"?>
目的是惛_公司能很方便的访问家里那些收集很久电子书Q方便查阅?br />
用了1Q?个星期,虽然写的很烂Q但是没有用MW三方的产品Qserver or dbQ?br />
现在里面的书c已l接q?00本了?br />
注:serverq了家里的adslQ所以速度慢,关闭不定时。毕竟玩玩嘛?br />
有兴的朋友先装个jdk1.5。再q行下面压羃包里的exe文g执行卛_?br />
Ҏ下蝲
User ID: blogjava
Password: blogjava
因此理解q堆“集合(CollectionQ类”很有必要。声明一下,以前一直都是叫它们集合c,但是好像Think In Java的作者鄙视了q个说法Q严格的说应该叫Containerc,而后看了它整整一章书以后Q觉得还是h家说的有道理?/p>
它说q个containercdQ包含了两大c,Collection和MapQ而Collection又可以分为List和Set。当然这些抽象概念都被定义成了接口?/p>
话说Q这L分类的确是严格按照类之间的承关pL说得Q但是俺总觉得很别扭Q真动手的时候,q是很难选择。当ӞAnytime and Anywhere使用ArrayListl对都能解决问题Q但q样做毕竟太农民了一炏V?/p>
所以,我自己有了一些想法。先回归到最基本最基本的数据结构的层面Q管你是Collectionq是ContainerQ反正描q的都是一堆东西吧。数据结构第一章讲了一个结构:在物理上q箋分配I间的顺序结构,叫顺序表Q希望记性是好的Q,而离散分配空间的Q应该叫做链表,最常用的就是单链表。这两个东西Q其实就是很多复杂数据结构的基础Q还记得吗,当时是讲完q些东西Q才开始讲栈、队列、二叉树、有向无向图的。所以,q个序l构是很基础的。而在JAVA中,序表对应的是List接口Q而一般顺序表是ArrayListQ有效进行随机index查找Q;而单链表是LinkedListQ有效进行插入和删除Q,两个的优劣当q都讲烂了,q里׃说了?/p>
有了q两个结构以后,JAVA׃提供Stack和Queue单独的类了,因ؓQ用户可以用上面两个c轻易的d现?/p>
那Set和Map有怎么跟Listq上关系呢?
我认为可以把它们看成是无序和单一的ListQMap只是两个有映关pȝList|了Q?/p>
Set和Map无序和单一的特性,军_了它们天大的需求就是根据关键字Q元素对象)索。soQؓ了效率,必须hash?/p>
有了HashSet和HashMap?/p>
同时Q如果非要保持住元素的顺序,有了LinkedHashSet、LinkedHashMap?/p>
l论Q?/p>
假如你的需求是
1Q往Container中放的对象是无序且单一的;
2Q经常要索?br />用HashSet或HashMap吧?/p>
psQ这两个条g其实是一回事Q因为如果是不单一的话Q你L索它q嘛?/p>
如果q而需要保持元素的序Q不要让他顺便iterationQ那选择LinkedHashSet和LinkedHashMap?/p>
假如你的需求不满以上1&2Q那你放心,List肯定能帮你解冻I你只要稍微想一下是ArrayList好还是LinkedList好?/p>
题外话:
关于HashQ务必记得要让自q元素对象override hashCode()?equles() ҎQ要不你直接可以z了睡?/p>
关于所有这些ContainerQ务必记得有个辅助类叫InteratorQ遍历尽量要用它?br />
关于一些老的Stack、Vector、HashTableQ听说以后不要用了哦。收到啦Q!
在计机中,文字是字符的集合,也就是字W串QC是因ؓ对字W串设计的不好,才那么容易溢出。而别的一些高U语aQ对于这个进行了很多的改q?/p>
~程的h׃技术方向和应用方向的不同,日常~程的内容差距很大。但是对于字W串的处理,那可是永q都避不开的工作?/p>
昨天跑步的时候,想了一下,对于字符串的操作有那么多QsearchQmatchQsplitQreplaceQ,感觉很烦杂,能不能抓住这些操作的一个基本集Q?/p>
不知道对不对Q反正想出来了一个,q个基本操作是searchQ这里的search的意思是Q在输入串中扑ֈ目标串的开始位|(start indexQ,和结束位|(end indexQ?/p>
有了q个基本集,别的操作都很好衍生出来:
局部matchQ?/strong>其实是要求search操作臛_q回一个start index?/p>
全matchQ?/strong>其实要求search操作的至返回一个start indexQƈ且start index要ؓӞend index要ؓ输入串的全长?/p>
splitQ?/strong>其实是search操作之后Q把前一个end index和当前的start index之间的字W串截出来而已?/p>
replaceQ?/strong>其实是search操作之后Q把start index和end index之间的字W串换成另外的而已?/p>
所以,归根到底Q都是一个search操作的拓展Ş了。这么一惻I感觉清晰多了?/p>
q么一来,API对search的能力支持的好坏和效率高低是衡量字符串操作功能的标准Q当Ӟ如果有直接支持matchQsplitQreplace操作的话更好了?/p>
java对字W串search的支持,最基本的就是下面的String的indexOfҎQ?/p>
int indexOf(String str) q里我想说的是,很多时候我们所谓要search的目标串Q根本就不是固定单一的,而是变化多样的。如果只有一两种情况Q最多用两次上面的方法呗。但是有些情冉|q乎不可能罗列的Q例如,我们讲的代表email的字W串Q我们不可能遍历它吧?/p>
所以,需要一U能够通用表达字符串格式的语言。这是Regular ExpressionQreQ?/p>
假如上面ҎindexOf的str参数能支持re做ؓ参数的话Q那对于q种多样的search也可以用上面的方法了?/p>
可惜QindexOf不支持re作ؓ参数?br /> Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q?gt;> 全match操作Q?br />boolean matches(String regex) 全replace操作Q?br />String replaceAll(String regex, String replacement)
首个replace操作Q?br />
String replaceFirst(String regex, String replacement) 全split操作Q?br />String[] split(String regex) <<Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q?/p>
可惜啊,可惜Q可惜java的Stringc里面没有可以支持re的searchҎQ那如果要用re来searchQ只好用java中专门的recd?/p>
java中的recd主要׃个类Q一个叫PatternQ顾名思义Q代表re的类。一个叫Matcherc,反映当前match状况的类Q如存放了当前search到的位置Q匹配的字符串等{信息)?/p>
一般在构造中Q“re的表辑ּ”作为参C递入Patternc,“输入串Q待qoԌ”作为参C递入MatchercR?/p>
然后使用Matchercȝ字符串searchҎ可以了。Matcher真正提供search功能的API叫find。下面列出?br />Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q?gt;>
boolean lookingAt()
boolean matches()
boolean find()
String group() <<Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q?/p>
前三个都是searchҎQ返回成功与否。第四个是返回当前search上的字符丌Ӏ?br /> 当然QPattern和Matcher也包含直接用req行的matchQsplitQreplace操作?/p>
Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q?gt;> 全match操作Q?br />static boolean matches(String regex, CharSequence input) 全split操作Q?br />String[] split(CharSequence input) 有限制数的split操作Q?br />String[] split(CharSequence input, int limit)
全replace操作Q?br />String replaceAll(String replacement) 首个replace操作Q?br />String replaceFirst(String replacement) 动态replaceQreplacement可以Ҏ被替代的字符串变化而变化)
StringBuffer appendTail(StringBuffer sb)
<<Q-Q-Q-Q-Q-Q-Q-Q-Q-Q-Q?br />
Returns the index within this string of the first occurrence of the specified substring.
soQ以下就介绍java api中可以用re作ؓ参数的字W串操作ҎQ参C的regex是reQ?/p>
StringcȝQ?/p>
Tells whether or not this string matches the given regular expression.
Replaces each substring of this string that matches the given regular expression with the given replacement.
Replaces the first substring of this string that matches the given regular expression with the given replacement.
Splits this string around matches of the given regular expression.
有限制数的split操作Q?br />String[] split(String regex, int limit)
Splits this string around matches of the given regular expression.
Matchercsearch操作相关的方法:
Attempts to match the input sequence, starting at the beginning, against the pattern.
Attempts to match the entire input sequence against the pattern.
Attempts to find the next subsequence of the input sequence that matches the pattern.
Returns the input subsequence matched by the previous match.
okQ至此。用re的search操作也有眉目了?/p>
Pattercd的字W串操作Ҏ
Compiles the given regular expression and attempts to match the given input against it.
Splits the given input sequence around matches of this pattern.
Splits the given input sequence around matches of this pattern.
Matchercd的字W串操作Ҏ
Replaces every subsequence of the input sequence that matches the pattern with the given replacement string.
Replaces the first subsequence of the input sequence that matches the pattern with the given replacement string.
Matcher appendReplacement(StringBuffer sb, String replacement)
Implements a non-terminal append-and-replace step.
Implements a terminal append-and-replace step.
ȝQ?br />当必M用re的时候,search操作p用到PatternQMatcherQ当然动态的replace操作也要用到q两个类。而别的matchQreplaceQsplit操作Q可以用patternQMatcherQ当然也可以直接使用StringQ推荐还是用回咱们的String吧?br />
注:以上都是看jdk1.4以上的文档得出的l论Q以前版本不能用不负责Q?/p>
重点x对象的创建和销毁:什么时候、如何创建对象,什么时候、什么条件下应该避免创徏对象Q如何保证对象在合适的方式下被销毁,如何在销毁对象之前操作一些必ȝ清理行ؓ?/span>
如果一?/span> client 要实例化一个对象来使用Q傻 b 都知道应该先调用cȝ构造器?/span> new 一个对象,之后再调用相应的Ҏ。除了这个方式, Java Effective q徏议了另一U方法:用静态工厂方法来提供一个类的实?/u>。以下的例子不反映两者的优劣Q只是反映两者在代码实现上的不同Q优劣之后再谈:
假设׃要一个颜色ؓ黑色、长度ؓ
Hammer myHammer = new Hammer(Color.BLACK, 50);
而用静态工厂方法来实例化一个对象,如下
Hammer myHammer = Hammer.factory(Color.BLACK,50);
也可以用专门的一个工厂类来实例化
Hammer myHammer = Toolkit.factory(“Hammer? Color.BLACK,50);
单纯从上面的代码上看Q真的只有傻 b 才会选择静态工厂的ҎQ完全就是多此一举,直接 new 又快又爽Q搞q么ȝ做莫斯(武汉话“什么”的意思)Q?/span>
别急,别急,你急个?/span> b Q武汉粗话:基本是“你急个毛”的意思)Q?/span>
下面p说用静态工厂代替构造器的好处( advantage Q和不好处( disadvantage Q?/span>
W一个好处,讲你都不信,行家们认为,构造器有一个不好的地方是Q这个方法的{Q?/span>
signture
Q太固定了?/span>
构造器的名字是固定的,生个 Hammer Q构造器的名字就?/span> Hammer Q……)Q唯一能变化的地方是参数Q假设我的这个锤子有两个很变态的构造需要:
1 Q第一个参数是颜色Q?/span> Color 型)Q第二个参数是锤子头的重量( int 型)?/span>
Hammer Q?/span> Color c, int kg Q?/span> {
//remainder omited
}
2 Q第一个参数是颜色Q?/span> Color 型)Q第二个参数是锤子的长度Q?/span> int 型)?/span>
Hammer Q?/span> Color c, int cm Q?/span> {
//remainder omited
}
感觉满需要了Q但是细心一看,完了Q构造器的参数列表类型重复了Q肯定编译通不q,q是面向对象构造器天生的缺陷——唯一的变化就是参敎ͼ参数都分辨不了,q的分辨不了?/span>
而另外就参数能分L的了Q构造器一多,它的参数一多,您根本就不知道每个参数是用来q什么的Q只能去查阅文档Q在您已l眼qq时候再L文Q一个一个的对,折磨人的zR?/span>
q个时候,您就可以考虑用静态工厂方法来实例化对象了。因为静态工厂方法有一个最单的特点是Q他有可以变化的Ҏ名(构造器的名字变不了Q。用名字的不同来代表不同的构造需要,q么单的普通的特点在这里就是它相对于构造器?/span> advantage ?/span>
如上面的锤子的例子可以这P
1 Q?/span> Hammer.produceByWeight (Color c, int kg){
//remainder omited
}
2 Q?/span> Hammer.produceByHeight (Color c, int cm){
//remainder omited
}
q是不是一目了然多了。嗯Q我是这栯为的?/span>
W二个好处,“静态工厂方法不需要每ơ都真的d例化一个对象”——其实这也是另一些优化方法的前提?/span>
构造器的每?/span> invoke 必定会生一个新的对象,而静态工厂方法经q一定的控制Q完全可以不用每?/span> invoke 都生成一个新的对象?/span>
Z么不每次都生成一个对象的原因׃必说了,因ؓ原因太明显。这个原因就是ؓ什么要“共享”对象的原因?/span>
下面讲讲通常使用的两U共享具体策略,也就是具体方法了Q?/span>
1 Q单例模式的需要,一旦需要某个对象有单例的需要,必定对于q类对象的构造只能用静态工厂方法了?/span>
2 Q?/span> flyweight 模式和不变( immutable Q?/span> 模式的需要,q两个模式很多时候都说一起用的Q一旦一些对象我们认为是不变的,那自然就x来重用,也就说共享,?/span> flyweight 是用来重用q些粒度对象的?/span>
?/span> Boolean.valueOf (boolean) ҎQ?/span>
Boolean a = Boolean.valueOf (100);
Boolean b = Boolean.valueOf (100);
aQ?span style="FONT: 7pt 'Times New Roman'; font-size-adjust: none; font-stretch: normal">
b两个引用都是指向同一个对象?/span>
q些对象都是不变的,?/span> valueOf 的控制就是用?/span> flyweight Ҏ?/span>
q种一个状态(如上面一个数字)对应的对象只有一个还有一个好处,是可以直接通过比较“引用”来判断他们是否
equel
Q这里的
equel
是逻辑相等的意思)Q以前需?/span>
a.equels(b)
Q而一旦用?/span>
flyweight
模式和不变(
immutable
Q?/span>
模式”后Q避免了产生多余的相同对象,?/span>
a==b
可以达?/span>
a.equels(b)
的目的了。这样当然优化了
performance
?/span>
W三个好处,其实是工厂Ҏ的核心好处——我把它UCؓ“抽象类型构造器”。它可以为我们提供一个抽象类型的实例Q同时必要的隐藏了抽象类型的具体l构。这?/span>
new
怎么都达不到的?/span>
q种模式的好处其实就是面向对象的最核心的好处,抽象和具体可以分,一旦抽象定义好了,具体的东西可以慢慢的变化Q慢慢的拓展——开闭原则?/span>
?/span>
Collections Framework API
Q都是描q集合类型的接口Q也是对于客户端来看,只有
Collection
q个c要认识Q而实际上Q实现这个接口的
Collection
是多U多L。如果要让用户都知道q些具体实现?/span>
Collection
Q就增加了复杂度?/span>
q时Q通过一个静态工厂方法,可以隐藏各U?/span>
Collection
的具体实玎ͼ而让
Client
只用返回的
Collection
对象可以了?/span>
q里q可以加上一些权限控Ӟ如这些实现只要对于工厂来讲是可以讉K的,不用?/span>
public
的,而他们只要通过
public
的工厂就可以提供l用戗非常有利于代码的安全?/span>
静态工厂方法的W一个缺点就是:使用静态工厂方法创建的cȝ构造器l常都是非公共或?/span>
protected
的?/span>
q样Q以后这些类没有办法被l承了。不q也有h_不用l承q
composition
呗。也是!呵呵?/span>
静态工厂方法的W二个缺ҎQ在
jdk
文里,q些静态工厂方法很难跟别的静态方法相区别?/span>
而文中Q构造器是很Ҏ看到的?/span>
Z一定程度解册个问题,我们可以用一些比较特别的名字来给q类静态工厂方法来命名。最常用的有Q?/span>
valueOf
—?/span>
用来攑֛跟参数“相同值”的对象?/span>
getInstance
—?/span>
q回一个对象的实例。单例模式中Q就是返回单例对象?/span>
ȝQ静态工厂方法和构造器都有各自的特炏V最好在考虑用构造器之前能先考虑一下静态工厂方法,往往Q后者更有用一炏V如果权衡了以后也看不出那个好用一些,那就用构造器Q毕竟简单本分多了?/span>
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