




已阅读5页,还剩39页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
设计模式初学者指南薛晖Good cooking takes time. If you are made to wait, it is to serve you better, and to please you.美酒的酿造需要年头,美食的烹调需要时间;片刻等待,更多美味,更多享受。 人月神话写在前面本文的内容主要来源于前段时间的部门分享,经过整理、修订与完善后,现分享给各位,祝开卷有益。欢迎来到模式的世界且慢,我们为什么需要模式?大学里开发“Hello World”我们用到了模式吗?在GOF的设计模式之前设计的程序难道都不是好的程序吗?王尔德曾说,百折不挠的决心若与科学法则相抵触,犹如江心补漏劳而无益。若想翘起地球,必先懂得杠杆原理和使用规律。当然哲学上来说,一切规律都是相对静止而绝对变化的,因此那些较为稳定、变化不那么频繁的规律更是我们追求的目标,毕竟,大家都想要掌握一些一劳永逸的东西。有兴趣的您不妨去看看IT十大死对头这篇文章,里面讲述了Linux单挑Windows以及Google对抗所有人等等,但传达给我们开发人员的精神只有一点,就是目下技术日新月异懂的程度堪比摩尔定律。尽管我们不提倡重新发明轮子,但是谁也不知道下一个轮子是谁发明的,并且不同“厂家”的轮子使用起来总会有一点差异;因此抛开具体的轮子不说,较为理想的是我们能够从宏观(设计模式角度)上掌握轮子的适用场景、改进它的使用方法等,或是能够从微观(算法角度)上了解轮子的具体结构、工作原理甚至DIY。好了,你已经知道设计模式是一种相对稳定、适于宏观把握的规律,那么使用设计模式究竟有什么好处呢?有些文章将鼓吹设计模式无所不能,在我看来,设计模式主要有两个好处:1、 以经验复用替代代码复用一切皆可复用!技术复用范畴很广,由低到高分别包括设计复用、组件复用、类库复用、代码复用,而设计模式提供了的“经验复用”,则是最高层次的复用。GOF设计模式所做的就是总结了面向对象设计中最有价值的经验,并且用简洁且可复用的形式表达出来。2、 共享词汇的威力假设去肯德基吃东西,你可能会对负责点餐的服务员说:给我一对用黑胡椒和新奥尔良秘制酱烤制的鸡翅,一个北京风味的、包含鸡肉、胡萝卜、黄瓜的特大春卷,一份夹了炸鸡腿、色拉酱和蔬菜的面包,一杯350ml的、加了冰块的百事可乐,另外把土豆打碎放在杯子里给我 ¥#%!¥当然,服务员小姐很有礼貌的说:重复一遍,您要的是一对新奥尔良烤翅、一份老北京鸡肉卷、一个田园脆鸡堡、一杯中可乐还有一份土豆泥,对吗? 得到你的默许之后,她又会对后面负责送餐的小弟说:9527,这边来一对烤翅、一份老北、一个田园、一杯中可、一杯泥。同样的意思源自于不同的表述,这就是人类语言的魅力;但现实中往往存在的是,哪怕是略微的表述不同也会造成别人极大的误解,那是沟通的陷阱。为了解决这个问题,各行各业产生了形形色色的“行话”,它们不仅将纷繁复杂的语言精炼化,还最大程度上避免了误解的产生。如果你认为肯德基点餐这个例子离软件设计太远的话,请看下面无间道的例子:华仔:我建立了一个专门对付琛哥监视类。它能够联系所有的探员,而且任何时候只要韩琛有轻举妄动,它就会通知每个人。最棒的是,任何警员都可以随时加入或退出这套系统。这样的设计可谓相当的面向对象伟仔:华仔,只要你说用了“观察者模式”,我就懂了 总得来说,共享词汇具有如下作用:1. 用更少的词汇作更充分的沟通 2. 避免误解 3. 在设计阶段,尽可能停留在设计层次,而排除编码阶段的影响 4. 帮助初级开发人员迅速成长 既然你已经了解了共享词汇的威力,以后碰到“张口闭口模式”的人,千万不要武断地以为他是在炫耀或是显摆,有可能他在他的圈子里已经习惯于共享词汇了J策略模式 上兵伐谋,其次供交,其次伐兵,其下攻城。 孙子在讲解策略模式的定义之前,请看下面的需求:全聚德烤鸭集团需要一个可以展示他们可爱鸭子的平台,希望用户可以通过这个可视化的平台“亲眼”看到全聚德的鸭子游泳或是呱呱叫,以此来提升他们低迷的股价。他们最重要的需求就是这个平台要有较强的灵活性和扩展性,因为现有情况表明,没有任何预算可以支付任何升级或重构的费用。让我们先来看看第一个“面向对象”的设计方案(继承):所有的鸭子都既会呱呱叫(quack),也会游泳(swim),所以这一部分的代码由超类(Duck)负责实现。但每一种鸭子的外观都是不同的,所以Duck类中的Display方法是抽象的,将其具体实现延迟到子类(MallardDuck、RedheadDuck)中去。正当一切看起来都很好的时候,新的需求来了。由于金融海啸的影响,全聚德公司的竞争压力骤增;在为期一周的头脑风暴会议之后,公司高层决定向潜在的客户展示一些令人印象深刻的元素,来提高公司低迷不振的股价,譬如说展示一些会飞的鸭子幸好当初我们采用了面向对象的设计,只需要在超类(Duck)中添加fly()方法,然后所有的鸭子(MallardDuck、RedheadDuck)都会继承它,这是展示OO强大继承威力的时候了 但是,可怕的事情发生了,测试人员在你的系统中看到了一些在天空中飞行的橡皮鸭子(RubberDuck,陪伴鸭子玩耍的玩具),它们虽然是鸭子,挤压它可以发出怪叫(quack),也可以浮在水面上(swim),但是却不会飞翔(fly);并非所有的Duck子类都会飞。如果在基类中加上新的行为,将会迫使某些不适合该行为的子类,也具有该行为;对超类代码所作的局部修改,影响层面可不只是局部!一个想当然的解决方法是,可以直接覆盖橡皮鸭类中的fly方法为空,神不知、鬼不觉,就好像一切都没有发生过一样;但这样做会有以下问题:1. 如果存在大量的不会飞行的鸭子子类,您将被迫多次覆盖fly方法为空,这样就失去了继承的意义了;即便你的功能可以正常实现,你是否闻到了代码的坏味道?每当有重复代码的时候,就应该考虑重构2. 可是如果以后加入诱饵鸭(DecoyDuck),又会如何呢?诱饵鸭是木头假鸭,不会飞也不会叫。您从来都不是生物学分类专家,鬼才知道下一次需求方会不会再创造唐老鸭(不会飞、不会游泳、会叫)抽象超类继承可能不是答案,因为您可能不是业务领域专家,无法事先预料到需求会如何改变;按照上述方案,每当有新的鸭子具体实现类出现,开发人员就要被迫检查并可能需要覆盖fly()和quack().这简直是无穷无尽的维护噩梦。 重新整理一下思路,我们想要的是一种更清晰的实现方案,让“某些”(而不是全部)鸭子的子类型可飞或可叫。究竟谁来决定它的行为呢?当然是具体实现类自己啦。请看下面的实现方案:我们可以把fly()从超类中取出来,放进一个“Flyable”接口中,只有会飞的鸭子才实现此接口。同样的方式,也可以设计一个”Quackable”的接口,因为不是每一个鸭子都会叫。每当加入一种新的鸭子后,可以自行决定是否需要实现Flyable或Quackable,而不会去影响原有的代码,这是多么美好的事啊可是,该系统的维护团队可不这么想!他们认为,这真是一个超笨的主意,这么一来重复的代码会增多,如果你认为覆盖几个方法不算什么,那么对于48个Duck的子类都需要稍微修改一下飞行的行为,你认为如何?! 让我们来总结一下失败的教训:并非所有的子类都具有飞行(fly)和呱呱叫(quack)的行为,鸭子的行为在子类里会不断地改变,所以继承显然不是最适当的解决方案。虽然Flyable与Quackable接口可以解决“一部分”问题(即不会再有会飞的橡皮鸭),但Java接口不具有实现代码因此造成了代码的无法复用,从而失去了面向对象的意义。这意味着:无论何时你需要修改某个行为,你必须得往下追踪并在每一个定义此行为的类中修改它,一不小心,就会犯下大错。 莎士比亚说:不要指着月亮起誓,它是变化无常的,每个月都盈亏圆缺;你要是指着它起誓,也许你的爱情也像它一样的无常。的确,不变只是愿望,变化才是永恒。幸好,设计模式的宝库里有一个设计原则可以帮助我们去更好使用地适应它(而不是去对抗它):封装变化,找出应用中可能需要变化的部分,把它们独立出来,不要和那些固定不变的代码混合起来。 这句话的概念似乎很简单,但它几乎是每个设计模式背后的精神所在。基本上,所有的模式都提供了一套方法让“系统中的某部分改变不会影响其他部分” 。我们知道Duck类中的fly()和quack()会随着鸭子的不同而改变。为了要把这两个行为从Duck类中分开,我们将把它们从Duck类中取出来,建立一组新类来改变每个行为。依照上面的思路,如何封装飞行和呱呱叫的行为呢?如果我们将具体的fly和quack直接绑定给duck类的话,我们就丧失了动态改变fly和quack行为的能力,试想一下,如果某一只鸭子(的实例)翅膀受伤了,它的fly行为就会改变,这意味我们最好是使用弹性的设计原则,即:接口编程原则: 针对接口编程,而不是针对实现编程现在,看一下我们对鸭子行为的封装:使用上述的设计,可以让飞行和呱呱叫的动作被其他对象复用,因为现在它们是具有代码实现的类而非接口了,并且这些行为已经与鸭子类无关了;而且我们可以新增一些行为(比如喷气式飞行、骑着扫帚飞行等),不会影响到既有的行为类(FlyWithWings、FlyNoWay),也不会影响“使用”到已有行为的类(各种Duck的具体子类)。好,让我们看看最终的成果:恩,抽象超类(Duck)与之前的设计相比,多了以下的内容1、 以接口的形式封装了行为(FlyBehavior、QuackBehavior),并以成员变量的形式(flyBehavior、quackBehavior)保存在类中,同时提供相应的setter方法(setFlyBehavior、setQuackBehavior)以达到动态改变其具体实现的目的。2、 由超类来负责行为的调用。performQuack和performFly内部分别调用了quackBehavior.quack()和flyBeavior.fly()。这并不等于说子类对行为的调用没有控制权(否则子类的实现就完全一样了),子类可以通过setter方法(setFlyBehavior、setQuackBehavior)来绑定具体的行为类,从而控制具体的行为。我们往往认为“面向对象 = 继承”,在我们的例子当中,鸭子的行为不是继承来的,而是和适当的行为对象“组合”来的,并且有一个原则可以支持这种做法:组合原则: 多用组合,少用继承。恩,你可能奇怪,讲了这么多怎么还没有切入“设计模式的正题”,其实你已经学会了第一个设计模式:策略模式定义了一系列算法族,并加以封装,让它们之间可以互相 替换;此模式让算法的变化独立于使用算法的客户。 GOF如果觉得鸭子的例子不够深刻的话,让我们来开发一款类似于Diablo的ARPG冒险游戏吧:Blizzard邀请您参与最新款的动作冒险游戏圆桌骑士的设计工作。您将能利用开发商提供的代表游戏角色的类和代表武器的类,每名角色一次只能使用一种武器,但在游戏过程中可以切换、买卖、装备、舍弃武器;您可以提出比较好的架构方案吗? 一群“富有策略”的圆桌骑士看上去应该是这样的:看吧,每个Character“有一个”WeaponBehavior,在冒险过程中切换、装备、买卖或是丢弃武器时,只要调用setWeapon方法即可。每一个具体的Character(King、Queen等) 都包含一个weapon的属性以及一个fight方法,后者在其内部调用WeaponBehavior接口的useWeapon方法,任何武器都可以实现WeaponBehavior接口,包括拳头、弹弓、口水,只要它实现了useWeapon方法。不同的武器的useWeapon可能千差万别,比如劈刺、挥砍、射击、喷吐等等。现在,您应该对策略模式有所了解了吧,下面做一下小结:策略模式的优点:1. 策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族。恰当使用继承可以把公共的代码移到超类里面,从而避免重复的代码。2. 策略模式提供了可以替换继承关系的办法。继承可以处理多种算法或行为。如果不是用策略模式,那么使用算法或行为的环境类就可能会有一些子类,每一个子类提供一个不同的算法或行为。3. 使用策略模式可以避免使用多重条件转移语句。多重转移语句不易维护,它把采取哪一种算法或采取哪一种行为的逻辑与算法或行为的逻辑混合在一起,统统列在一个多重转移语句里面,比使用继承的办法还要原始和落后。策略模式的缺点:客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。策略模式造成很多的策略类源代码:public class Context private Strategy stg; public Context(Strategy theStg) this.stg = theStg; public void DoAction() this.stg.AlgrithmInterface(); public class ConcreteStrategyA implements Strategy public void AlgrithmInterface() System.out.println(ConcreteStrategyA.AlgrithmInterface); public class ConcreteStrategyB implements Strategy public void AlgrithmInterface() System.out.println(ConcreteStrategyB.AlgrithmInterface); public class Context private Strategy stg; public Context(Strategy theStg) this.stg = theStg; public void DoAction() this.stg.AlgrithmInterface(); public class Client public static void main(String args) Strategy stgA = new ConcreteStrategyA(); Context ct = new Context(stgA); ct.DoAction(); 本文来自CSDN博客,转载请标明出处:/surprisesdu/archive/2006/03/24/636951.aspx心得:策略模式可以结合工厂模式,将策略聚合在Context中,通过传入参数判定使用哪个策略。适配器模式 While in rome , do as rome dose假设您所维护的应用暴露出来的接口与现有服务商提供的接口相冲突您可以通过实现一个适配器来使两者“相匹配”现在,由于适配器的引入,您的应用又可以正常工作了,而不用去修改现有系统或厂商类的代码了还记得全聚德的鸭子们吗?它们现在又碰到了麻烦。由于现在鸭肉紧缺,公司准备用火鸡来代替,但却发觉之前的系统似乎失灵了 恩,这似乎是显而易见的,之前的系统是针对鸭子接口的,当然不能用于火鸡的实例,要搞清楚的事,火鸡从不呱呱叫(quack),只会咯咯叫(gobble);火鸡由于体型的缘故,飞行距离很短;另外,您见过会游泳的火鸡吗?! 鸭子和火鸡的抽象超类和具体实现类如下所示:public interface Duck /鸭子的接口 public void quack(); public void fly(); public void swim();public class MallardDuck implements Duck /绿头鸭的具体实现 public void quack() System.out.println(Quack); public void fly() System.out.println(fly); public void swim() System.out.println(swim); public interface Turkey /火鸡的接口public void gobble(); public void fly();public class WildTurkey implements Turkey /野生火鸡的具体实现 public void gobble() System.out.println(Gobble); public void fly() System.out.println(Flying a short distance); 由于之前的系统只能识别Duck接口,因此我们实现了一个适配器: public class TurkeyAdapter implements Duck /首先,你必须实现你想要转换成的、客户 所期望看到的类型接口(Duck) Turkey turkey; public TurkeyAdapter(Turkey turkey) /必须采用某种方式,获得需要适配的对象的引用, 比如构造器注入 this.turkey = turkey; public void quack() /有些方法需要简单转换,比如用火鸡的咯咯叫(gobble)来代替鸭子的呱呱叫(quack) turkey.gobble(); public void fly() /有些方法需要复杂抓换,比如,由于火鸡的飞行距离较短,要达到鸭子的飞行效果,火鸡需要加倍的努力哦(调用fly五次) for (int i=0; i5; i+) turkey.fly(); public void swim() /还有些方法无法适配,这是可以覆盖为空、或者抛出异常、或者干脆像火鸡一样说:“这个我干不了”J System.out.println(No Turkey can SWIM!); 虽然很简单,但这里其实就用到了一个设计模式:适配器模式将一个类的接口,转换成客户期望的另一个接口。适配器让原来不兼容的类可以合作无间。 GOF当然,设计模式本身就是语言无关的,您正使用支持多重继承(C+)的语言,可能还会看到所谓的“类适配器”,它采用同时继承目标和被适配类、而不是组合被适配类的方法来适配被适配者 下面举一个现实生活中的例子:我们常常碰到遗留代码,它们往往暴露出Enumeration接口,但我们有希望在新的代码中使用Iterator 这个时候,您可以考虑使用一个适配器,如下所示:我们要使旧代码中的枚举变得像是新代码中的迭代器,新代码依然使用迭代器,虽然实际上背后隐藏的事枚举器。下面看一下这个适配器的具体实现:public class Enumeration implements Iterator /因为我们要将枚举适配成迭代器,因此必须要实现迭代器(被适配者)接口,让它看起来就像是一个迭代器 Enumeration enum; public EnumerationIterator(Enumeration enum) /利用组合的方式,保存一个枚举的引用 this.enum = enum; public boolean hasNext() /迭代器的hasNext方法其实是委托给枚举的hasMoreElements方法 return enum.hasMoreElements(); public object next() /迭代器的next方法其实是委托给枚举的nextElement方法 return enmu.nextElement(); public void remove() /很不幸,枚举不支持remove方法,所以必须放弃,在这里,我们抛出一个UnsupportedOperationException throw new UnsupportedOperationException(); 适配器模式的小结如下:1. 客户通过目标接口调用适配器的方法对适配器发出请求 2. 适配器使用被适配者接口吧请求转换为被适配者的一个或多个调用接口 3. 客户接收到调用的结果,但并未察觉到这一切是适配器在起转换作用 4. 当客户没有指定所需要的接口时,我们仍然可以应用Adapter模式,并使用现有类的实例来创建一个新的客户子类。这种方法会创建一个对象适配器,将客户调用转发给现有类的实例。但是这种方法也很危险,特别是当我们没有(或者不可以)重写客户可能调用的所有方法时。 观察者模式 观察,观察,再观察 巴甫洛夫假设你接到观察者日报的这样一个需求:1. 总部设在纽约的观察者日报急需新增一个电子版报纸系统,要求订阅电子版报纸的用户可以实时的获取最新报纸。2. 系统需要有注册用户和注销用户功能。当有最新的报纸出版时,所有订阅了报纸的用户可以自动获得最新版报纸。 3. 系统不需要了解究竟是哪些用户订阅了报纸,它负责的只是发布、发布、再发布! 4. 用户可以随时退订报纸,之后也可以重新再订阅,考虑到每天都有数以千计的用户订阅/退订,你不会想让我们每次都修改发报的代码吧? 先来看一个错误的设计方案:public void publishNewsPaper() NewsPaperOffice newPaperOffice = new NewsPaperOffice(观察者日报社); Paper = newsPaperOffice.getPaper(观察者日报); Google.send(paper); Yahoo.send(paper); Sina.send(paper); 这样的设计会有什么问题? 如果Alibaba也想订阅报纸,怎么办? 如果Yahoo想订阅两份报纸(Yahoo与Yahoo中国各一份),怎么办? 如果Google想退订观察者日报,怎么办? 如果Google退订之后又想再订阅观察者日报,怎么办? 按照目前的设计,只有一个方法,就是修改publishNewsPaper的代码!恩,让我们先来深入研究一下“订阅报纸”的业务: 报社的业务就是出版报纸 向某家报社订阅报纸,只要它们有新报纸出版,就会给你送来 当你不想再看报纸的时候,取消订阅,就不会再有新报纸送来 只要报社还在运营,就会一直有人(或单位)向他们订阅报纸或取消订阅报纸 其实,出版商 + 订阅者 = 观察者模式其实,主题和观察者都以为对方是俊男美女,因为他们都是高度近视,看不清对方的细节。事实上,有一个设计原则可以支持上面这种设计:耦合-内聚原则: 为了交互对象之间的松耦合设计而努力 一个松耦合的设计具有如下特点 : 主题和观察者之间松耦合,意味着它们虽然可以互相交互,但是不太清楚彼此的细节 任何时候都可以增加新的观察者,因为主题唯一依赖的是一个Observer接口的列表 有新类型的观察者出现时,主题的代码不需要因为兼容性的问题而修改 改变主题或是观察者中的其中一方,并不会影响另一方,因为两者是松耦合的 可以独立地复用主题或观察者一个松耦合的观察者日报的设计大概如下:观察者日报社的核心代码的实现如下:List observers;Newspaper newspaper;public NewspaperOffice() observers = new List();public void changeNewspaper(Newspaper n) newspaper = n; notifyObservers();public void registerObserver(Observer o) observers.Add(o);public void removeObserver(Observer o) observers.Remove(o);public void notifyObservers() foreach (Observer var in observers) var.update(newspaper); 让我们来测试一下这个“松耦合”设计方案的扩展性。观察者日报最初是面向中国地区的,有许多中国订阅者(观察者),它们的具体实现如下:class Chinese extends Observer Newspaper newspaper; Observer Members; public void update(Newspaper n) this.newspaper = n; display(); public void display() System.out.println(I am a chinese and I have got the newspaper); this.newspaper.display();观察者日报采用新的系统后大获成功,威震天感觉应该让所有Cybertron人人手一份,所以又来了许多Cybertron订阅者。这时问题出现了,日报没有Cybertron语言版的,怎么办呢?为了让所有人都能读懂报纸,必须在Cybertron订阅者中添加一个翻译方法,将中文版的观察者日报翻译成Cybertron版日报。 class Cybertron extends Observer Newspaper newspaper; public Newspaper translate(Newspaper n) Newspaper tmp = n; /translate the newpaper. tmp.setContent(#$(*&*(!)!)#(&$ *#*$*(); return tmp; public void update(Newspaper n) this.newspaper = n; display(); public void display() System.out.println(I am a Cybertron and I have got the newspaper); translate(newspaper).display(); 观察者日报最近新推出了一个版面“身边的特异功能者”,Sylar发现了这个版面后兴奋的三天没合眼,立即订阅了报纸,只要每天订阅这个报纸就可以轻而易举的找到那些超能者,挖开他们的大脑,获得他们的能力。Sylar立即订阅了一份观察者日报。现在Sylar也能不断收到最新的报纸了,其他Hero们要小心了class Sylar extends Observer Newspaper newspaper; Observer Members; public void update(Newspaper n) this.newspaper = n; display(); public void display() System.out.println(I am Sylar and I have got the newspaper); this.newspaper.display(); 看见了吗?无论是谁想成为观察者,都不会影响既有的代码,不需要重新编译、部署(新添代码除外),这就是松耦合的好处。当然,观察者模式只是松耦合实现的一种方法,现定义如下:观察者模式定义了对象之间的一对多关系,这样一来,当一个对象改变状态时,它的所有依赖者都会接收到通知,并自动更新。 GOF恩,刚才还保留了一点没说,其实Java具备内置的观察者模式解决方案,请看:这里要注意的是: 你可以使用推(push)或拉(pull)的方式传送数据,但这意味者你必须公开 你的get方法, 至于安全性如何,你自己决定吧J 不要依赖于观察者被通知的次序 Observable是一个抽象类而不是接口,你明白我是什么意思了吧,如果你的应用程序已经继承了一个抽象超类,你就不能使用它了,这也是为什么一开始不介绍它的原因 setChanged方法是被保护的(proteced),这意味者你不能采用组合的方式,违反了“多用组合,少用继承” 在GUI中,比如Swing,或MVC中,你能看到更多的观察者模式 工厂模式 任何一个民族,如果被剥夺了工业,那是不能和其他民族在文明上并驾齐驱的。 恩格斯假设在某系统中要用到一个DataSource对象。而且用的很广泛。在盘古开辟天地的时期,要获取一个DataSource对象,是这么干的: DataSourceds=newDataSourceImpl(dburl,user,password);在很多地方写了这样的代码之后,你的团队里面的一个测试MM开始与你合作做这个项目。这位美女拿到代码之后发现,你写在代码中一共999个地方的new DataSourceImpl(“dburl”,“user”,“password”)里面的数据库url,username和password和他的机器上的情况不符合,于是你发现,应该这样写: publicclassDataSourceManager publicDataSourcegetDataSource() returnnewDataSourceImpl(dburl,user,password); 经过上面的改造,你的同事拿到代码之后高兴了,发现自己只要把这个DataSourceManager里面的代码改一改,在自己机器上就能跑起来了。于是她请你吃了哈根达斯当然,不排除这是因为她对你有好感。又过了两天,团队又加入了2个人。当大家都开始从svn拿最新的代码的时候,你发现每天都会拿到新的DataSourceManager,里面变化的是各个团队成员自己机器上的数据库url,用户名,密码。这意味着要重新编译,你知道程序员最讨厌的是什么吗?于是你发挥聪明才智,把数据库用户名密码放在了一个properties配置文件中。暂且叫做perties。然后经过改造,DataSourceManager变成了下面的样子: publicclassDataSourceManager privateStringdbUrl; privateStringuser; privateStringpassword; static /读取perties,初始化dburl,user,password publicDataSourcegetDataSource() returnnewDataSourceImpl(dbUrl,user,password); 恭喜你,经过这样的改造,DataSourceManager封装了更多的创建逻辑,而你在不同的机器上部署应用的时候也不需要总是去编译了。 过了几天,领导对你说,现在用jdbc直接连数据库可能会有性能问题,我们希望使用jndi,使用容器管理的数据源,但是同样要给开发人员保持使用jdbc连接的余地。于是你再一次的修改DataSourceManager,根据一个配置,决定是去jndi中取dataSource直接返回,还是根据用户密码url来创建dataSource返回。 回首往事,你将会心潮澎湃。试想一下当初如果没有用DataSourceManager这么个东西来封装DataSource的创建过程,而是坚持在系统的各个角落创建DataSource的话,现在要改成jndi方式得费多大劲。于是一高兴,你请当初请你吃哈根达斯的那个MM吃了顿饭,顺道表露了一下你对她有某方面的好感。再后来的某一天,你有幸读到了和banq吵架很火爆的阎宏博士写的Java与模式,发现你所写的DataSourceManager就是他书中阐述的工厂模式因为其职责是封装对象的创建过程。 至此,你终于理解了工厂模式是干什么的,以及它为什么这么重要。 后来的若干岁月,你开始研究开源世界里面大大小小的轮子,某一天发现了Spring这么个好东西,之后又惊奇的发现,这个东西的核心内容,竟然就是一个BeanFactory也就一个工厂。 于是你融会贯通,高兴之余,拒绝了那个测试MM对你的求爱,告诉他说你现在只对工厂感兴趣,对女人没兴趣J上面那个DataSource的例子所说的工厂,其实只是工厂模式的一种,称为简单工厂模式;其实,总共有三种工厂模式: 简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类或接口,通常根据一个条件(参数)来返回不同的类的实例。 工厂方法模式定义了一个创建对象的接口,但由子类 决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。 抽象工厂模式提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。下面我们以社会分工理论来具体说明它们的区别:假设我们要分别创建宝马和奔驰,在建国初期,只有一个汽车工厂,你想要宝马,它就造一辆宝马;你想要奥迪,它就给你一辆奔驰;改革开放后,允许私有经济了,因此出现了宝马工厂和奔驰工厂,分别独立负责制造它们自己的汽车,用户想要买什么汽车就可以直接去相应的工厂去买;加入世贸组织后,社会分工进一步扩大了,汽车的不同部件(轮胎、车身)由不同的工厂加工,因此出现了宝马、奔驰两个平行的产品族(宝马轮胎、宝马车身、奔驰轮胎、奔驰车身)及相应的工厂,可以根据用户的需要自动地从工厂采购元器件拼装成一辆汽车。关于模式,你还应该知道的1、三类设计模式(懒得翻译了) Creational design patterns are all about class instantiation. This pattern can be further divided into class-creation patterns and object-creational patterns. While class-creation patterns use inheritance effectively in the instantiation process, object-creation patterns use del
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 光学高分子新材料生产线项目可行性研究报告(范文)
- 读书笔记:小王子中的友情与成长15篇范文
- 建筑装饰材料与施工技术试题库
- 智能化监控系统在项目管理中的应用
- 网络安全与防护知识梳理
- 食品营养学及食品安全管理题库
- 我家的冰箱作文范文13篇
- 中医药适宜技术的国际化发展与文化传播策略
- 复合型光伏电站配套储能系统项目可行性研究报告
- 2025年心理测量与评估考试试题及答案
- 修理工安全试题及答案
- 地面地砖检修方案(3篇)
- 公司工会内控管理制度
- 水发能源考试题及答案
- 2025年一年级语文1-8单元期末考试复习基础知识点默写清单(有答案)
- 食堂燃气培训试题及答案
- 产业协同创新对制造业升级的促进机制研究
- 2025陕西中考:语文必考知识点
- 2025-2030北京市大健康产业发展决策及未来经营模式战略规划研究报告
- 2024安全生产法律法规培训课
- (5篇)2025年春《形势与政策》专题测验与形势与政策大作业详细
评论
0/150
提交评论