OOTV杯超级模式大赛——设计模式总结
演讲任务
时间:7月23日下午17点 地点:小菜办公室 人物:小菜、公司开发部经理
"小菜,"开发部经理来到小菜的办公桌前,“最近我听说你在工作中,用到了很多设计模式,而且在你们同一项目组中引发了关于设计模式的学习和讨论,反响非常好。明天全公司要开关于如何提高软件质量的研讨会,我希望到时你能做一个关于设计模式的演讲。”
"我?演讲?给全公司?"小菜很惊讶于经理的这个请求,“还是不要了吧,我从来都没上过台给那么多人讲东西。我怕讲不好的。”
“没事,你就像平时和同事交流设计模式那样说说你在开发中的体会和经验就行了。”
"明天,时间太仓促了吧,来不及的。"小菜想法推脱。
"哈,某位大导演在他的近期的电影里不是已经向众人证实,身材是可以靠挤来获得的。同理可证,时间也是可以挤出来的,晚上抓紧一些,一定行的。就这么定了,你好好准备一下。"说完,经理就离开了小菜的办公桌。
"我……"小菜还来不及再拒绝,已经没了机会。
时间:7月23日晚上22点 地点:小菜自己的卧室 人物:小菜
"讲什么好呢?"小菜使劲地想呀想,却没有头绪,"该死的大鸟,还不回
来,不然也可以请教请教他。"
“真困”,小菜睡眼蒙眬,趴到了桌上,打起盹来,不一会,进入了梦乡。
报名参赛
时间:未知 地点:未知 人物:很多
"来来来,快来报名了,超级设计模式大赛,每个人都有机会,每个人都能成功,今天你参加比赛给自己一个机会,明天你就成功还社会一个辉煌。来来来……“只见台子上方一很长的条幅,上写着"OOTV杯超级模式大赛海选”,下面一个帅小伙拿着话筒卖力地吆喝着。
"大姐、二姐,我们也去报名参加吧。"工厂三姐妹中最小的简单工厂说道。
"这种选秀比赛,多得去了,很多是骗人的,没意思。"大姐抽象工厂说
"我觉得我们三姐妹有机会的,毕竟我们从小就是学这个出身。"二姐工厂方法也很有兴趣参赛。
"大姐,去吧,去吧。"简单工厂拉住抽象工厂的手左右摇摆着。
"行了行了,我们去试试,成就成,不成可别乱哭鼻子。"抽象工厂用手指点了一下简单工厂的鼻子,先打上了预防针。
"放心吧,我们都会成功的。"简单工厂很高兴,肯定地说。
三个人果然顺利通过了海选。但在决赛前的选拔中,工厂方法和抽象工厂都晋级,而简单工厂却不幸落选了。
"唔唔唔……唔唔唔……"简单工厂哭着回到了后台。
"小妹,别哭了,到底发生了什么?"工厂方法搂住她,轻声地问道。
"他们说我不符合开放-封闭原则的精神,"简单工厂哽咽地答道,“所以就把我淘汰了。”
"你在对每一次扩展时都要更改工厂类,这就是对修改开放了,当然不符合开闭原则。"抽象工厂说道,“行了,别哭了,讲好了不许哭鼻子的。”
"哇……哇……哇……"被大姐这么一说,简单工厂放声大哭。
"好了,没关系,这次不行,还会有下次。"工厂方法拍了拍她的后背安慰道。
"二姐,你要好好加油,为我们工厂家族争光。"简单工厂握着工厂方法的手说,然后对着抽象工厂说,"大姐,哼,你老是说风凉话,祝你早日淘汰。"边说着,简单工厂却破涕为笑了。
"你这小妮子,敢咒我。看我不……"抽象工厂脸上带笑,嘴里骂着,举起手欲拍过去。
"二姐救我,大姐饶命。"简单工厂躲到工厂方法后面叫道。
三姐妹追逐打闹着,落选的情绪一扫而空。
超模大赛开幕式
"各位领导、各位来宾、观众朋友们,第一届OOTV杯超级设计模式大赛正式开始。"漂亮的主持人GoF出现在台前,话音刚落,现场顿时响起激昂的音乐和热烈的掌声。
"首先我们来介绍一下到场的嘉宾。第一位是我们OOTV创始人,面向对象先生。"只见前排一位40多岁的中年人站了起来,向后排的观众挥手。
工厂方法在后台对着抽象工厂说:“没想到面向对象这么年轻,40岁就功成名就了。”
"是呀,他从小是靠做Simula服装开始创业的,后来做Smalltalk的生意开始发扬光大,但最终让他成功的是Java,我觉得他也就是运气好。"抽象工厂解释说。
“运气也是要给有准备的人,前二十年,做C品牌服装生意的人多得是了,结构化编程就像神一样被顶礼膜拜,只有面向对象能坚持OO理念,事实证明,OO被越来越多的人认同。这可不是运气。”
"结构化编程那确实是有些老了,时代不同了,老的偶像要渐渐退出,新的偶像再站出来。现在是面向对象的时代,当然如日中天,再过几年就不一定是他了。"抽象工厂相对悲观。
"面向对象不是一直声称自己’永远二十五岁’吗?"工厂方法双手抱拳放在胸前,坚定地说,“我看好他,他是我永远的偶像。”
"到场来宾还有,抽象先生、封装先生、继承女士、多态女士。他们也是我们这次比赛的策划、导演和监制,掌声欢迎。"主持人GoF说道。
工厂方法说:“啊,这些明星,平时看都看不见的,真想为他们尖叫。”
抽象工厂说:“我最大的愿望就是能得到抽象先生的签名,看来极有可能梦想成真了。”
两姐妹在那里入迷地望着前台,自说自话着。
"现在介绍本次大赛的评委,单一职责先生、开放封闭先生、依赖倒转先生、里氏代换女士、合成聚合复用女士、迪米特先生。"主持人GoF说道。
"那个叫开放封闭的家伙就是提出淘汰小妹的人。"抽象工厂对工厂方法说。
"嘘,小点声,他们可就是我们的评委,我们的命运由他们决定的。"工厂方法把食指放在嘴边小声地说。
"下面有请面向对象先生发表演讲。"主持人GoF说道。
面向对象大步流星地走上了台前,没有任何稿子,语音洪亮地开始了发言。
"各位来宾,电视机前的朋友们,大家好!(鼓掌)
感谢大家来为OOTV的超级设计模式大赛捧场。OO从诞生到现在,经历了风风雨雨,我面向对象能有今天真的也非常不容易。就着这机会,我来谈谈面向对象的由来和举办此次设计模式大赛的目的。
软件开发思想经过了几十年的发展。最早的机器语言编程,程序员一直在内存和外存容量的苛刻限制下’艰苦’劳作。尽管如此,当时程序员还是创造了许多令人惊奇的工程软件。后来,高性能的计算机越来越普及,它们拥有较多的内外存空间,编程也发展到一个较高的层次,不再对任一细节都斤斤计较,于是出现了各种高级语言,软件编程开始进入了全面开花的时代。
刚开始的高级语言编写,大多是面条式的代码,随着代码的复杂化,这会造成代码极度混乱。随着软件业的发展,面条式的代码越来越不适应发展的需要,此时出现了结构化编程,即面向过程式的开发,这种方式把代码分割成了多个模块,增强了代码的复用性,方便了调试和修改,但是结构也相对复杂一些。面向过程的开发,把需求理解成一条一条的业务流程,开发前总是喜欢问用户’你的业务流程是什么样的?',然后他们分析这些流程,把这些流程交织组合在一起,然后再划分成一个又一个的功能模块,最终通过一个又一个的函数,实现了需求。这对于一个小型的软件来说,或许是最直接最简捷的做法。
而问题也就出在了这里。随着软件的不断复杂化,这样的做法有很大的弊端。面向过程关注业务流程,但无论多么努力工作,分析做得如何好,也永远无法从用户那里获得所有的需求,而业务流程却是需求中最可能变化的地方,业务流程的制定需要受到很多条件的限制,甚至程序的效率、运行方式都会反过来影响业务流程。有时候用户也会为了更好地实现商业目的,主动地改变业务流程,并且一个流程的变化经常会带来一系列的变化。这就使得按照业务流程设计的程序经常面临变化。今天请假可能就只需要打声招呼就行了,明天请假就需要多个级别管理者审批才可以。流程的易变性,使得把流程看得很重,并不能适应变化。
面向过程通过划分功能模块,通过函数相互间的调用来实现,但需求变化时,就需要更改函数。而你改动的函数有多少地方在调用它,关联多少数据,这是很不容易弄清楚的地方。或许开发者本人弄得清楚,但下一位维护代码者是否也了解所有的函数间的彼此调用关系呢?函数的修改极有可能引起不必要的Bug的出现,维护和调试中所耗费的大多数时间不是花在修改Bug上,而是花在寻找Bug上,弄清如何避免在修改代码时导致不良副作用上了。种种迹象都表明,面向过程的开发也不能适应软件的发展。
与其抱怨需求总是变化,不如改变开发过程,从而更有效地应对变化。面向对象的编程方式诞生,就是为解决变化带来的问题。
面向对象关注的是对象,对象的优点在于,可以定义自己负责的事物,做要求它自己做的事情。对象应该自己负责自己,而且应该清楚地定义责任。
面向对象的开发者,把需求理解成一个一个的对象,他们喜欢问用户’这个东西叫作什么,他从哪里来,他能做什么事情?',然后他们制造这些对象,让这些对象互相调用,符合了业务需要。
需求变化是必然的,那么尽管无法预测会发生什么变化,但是通常可以预测哪里会发生变化。面向对象的优点之一,就是可以封装这些变化区域,从而更容易地将代码与变化产生的影响隔离开来。代码可以设计得使需求的变化不至于产生太大的影响。代码可以逐步演进,新代码可以影响较少地加入。
显然,对象比流程更加稳定,也更加封闭。业务流程从表面上看只有一个入口、一个出口,但是实际上,流程的每一步都可能改变某个数据的内容、改变某个设备的状态,对外界产生影响。而对象则是完全通过接口与外界联系,接口内部的事情与外界无关。
当然,有了面向对象的方式,问题的解决看上去不再这么直截了当,需要首先开发业务对象,然后才能实现业务流程。随着面向对象编程方式的发展,又出现了设计模式、ORM,以及不计其数的工具、框架。软件为什么会越来越复杂呢?其实这不是软件本身的原因,而是因为软件需要解决的需求越来越复杂了。
面向过程设计开发相对容易,但不容易应对变化。面向对象设计开发困难,但却能更好地应对千变万化的世界,所以现代的软件需要面向对象的设计和开发。
(鼓掌)
设计模式是面向对象技术的最新进展之一。由于面向对象设计的复杂性,所以我们都希望能做出应对变化,提高复用的设计方案,而设计模式就能帮助我们做到这样的结果。通过复用已经公认的设计,我们能够在解决问题时避免前人所犯的种种错误,可以从学习他人的经验中获益,用不着为那些总是会重复出现的问题再次设计解决方案。显然,设计模式有助于提高我们的思考层次。让我们能站在山顶而不是山脚,也就是更高的高度来俯视我们的设计。[DPE]
如今,好的设计模式越来越多,但了解他们的人却依然很少,我们OOTV举办设计模式大赛的目的一方面是为了评选出最优秀的设计模式,另一方面也是希望让更多的人了解她们,认识她们,让她们成为明星,让她们可以为您的工作服务。
祝愿本届大赛圆满成功。谢谢大家!"(鼓掌)
正在此时,突然一个人双手举着一块牌子冲上了讲台,纸牌上写着"Service-Oriented Architecture(面向服务的体系架构,SOA)",口中大声且反复地说道:“抵制Object-Oriented,推广Service-Oriented,OO已成往事,SOA代表未来。”
这突如其来的变化,让全场哗然,很多人都交头接耳,说着关于SOA与OO的关系。只有面向对象先生依然站在讲台上,微笑不语,显然久经风雨的他对于这种事早已见怪不怪。保安迅速带着此人离开了会场。会场渐渐又恢复了安静。
"下面宣布一下比赛规则。"GoF的声音再次响起,“本次大赛根据模式的特点,设置了三个类别,分别是创建型模式、结构型模式和行为型模式,但由于有11位选择了行为型模式,人数过多,所以行为型模式又分为两组。也就是说,我们将选手共分为四组,所有的选手都将首先参加分组比赛,每组第一名将参加我们最终设计模式冠军的争夺。选手的分组情况,请看大屏幕。”
“下面我们就有请,单一职责先生代表评委宣誓。”
此时,几位评委站了起来,单一职责先生拿起事先写好的稿子,缓慢地说道:“我代表本届大赛全体评委和工作人员宣誓:恪守职业道德,遵守竞赛规则。严格执法,公正裁判,努力为参赛选手提供良好的比赛氛围和高效优质的服务,维护公正的评委信誉。为保证大会的圆满成功,做出我们应有的贡献!宣誓人:单一职责。”
“宣誓人:开放封闭”
“宣誓人:依赖倒转”
……
“下面有请策略模式小姐代表参赛选手宣誓。”
“为了展示面向对象的优点和思想,为了编程的光荣和团队的荣誉,我代表我们全体参赛选手,将弘扬’可维护、可扩展、可复用、灵活性好’的OO精神,严格遵守赛事活动的各项安排,遵守比赛规则和赛场纪律,尊重对手,团结协作,顽强拼搏,赛出风格,赛出水平,尊重裁判,尊重对手,尊重观众。并预祝大赛圆满成功。”
创建型模式比赛
“现在比赛正式开始,有请第一组参赛选手入场,并进行综合形象展示。”
“第一组创建型选手,他们身穿的是Java正装进行展示。”
“1号选手,抽象工厂小姐,她的口号是提供一个创建一系列或相关依赖对象的接口,而无须指定它们具体的类。[DP]”
“2号选手,建造者小姐,她的口号是将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。[DP]”
2号选手 建造者(Builder)
“3号选手工厂方法小姐向我们走来,她声称定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂模式使一个类的实例化延迟到其子类。[DP]”
“4号选手是原型小姐,她的意图是用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。[DP]”
4号选手 原型(Prototype)
“5号选手出场,单例小姐,她提倡简捷就是美,保证一个类仅有一个实例,并提供一个访问它的全局访问点。[DP]”
此时只见场下一帮Fans开始热闹起来。
简单工厂带领着抽象工厂和工厂方法的粉丝们开始齐唱,"咱们工厂有力量,嗨!咱们工厂有力量!每天每日工作忙,嗨!每天每日工作忙,……
哎!嗨!哎!嗨!为了咱程序员彻底解放!"
"单例单例,你最美丽,一人创建,全家获益。"单例的Fans同样不甘示弱地喊着口号。
观众席中还有两位先生安静地坐在那里,小声地聊着。
"你猜谁会胜出?"ADO.NET对旁边的Hibernate说。
"我觉得,抽象工厂可以解决多个类型产品的创建问题,就我而言,同一对象与多个数据库ORM就是通过她来实现的。我觉得她会赢。"Hibernate坚定地说。
"你也不看看,抽象工厂那形象,多臃肿呀,身上类这么多。做起事来一定不够利索。"ADO.NET不喜欢抽象工厂。
"那你喜欢单例?"Hibernate问道。
“单例又太瘦了。过于骨感也不是美。我其实蛮喜欢原型那小姑娘的,我的DataSet只要调用原型模式的Clone就可以解决数据结构的复制问题,而Copy则不但复制了结构,连数据也都复制完成,很是方便。”
“那你不觉得建造者把建造过程隐藏,一个请求,完整产品就创建,在高内聚的前提下使得与外界的耦合大大降低,这不也是很棒的吗?”
“问题是又有多少产品是相同的建造过程呢?再说回来,你造什么对象,不还是需要new吗?”
“哈,从new的角度讲,工厂方法才是最棒的设计,它可是把工厂职责都分了类了,其他几位不过是她的变体罢了。”
“有点道理,看来创建型这一组,工厂方法有点优势哦。”
"下面有请评委提问。"主持人GoF待五位选手出场亮相之后接着说。
"请问抽象工厂小姐,为什么我们需要创建型模式?"开放-封闭先生问道。只见抽象工厂思考了一下,说道:“我觉得创建型模式隐藏了这些类的实例是如何被创建和放在一起,整个系统关于这些对象所知道的是由抽象类所定义的接口。这样,创建型模式在创建了什么、谁创建它、它是怎么被创建的,以及何时创建这些方面提供了很大的灵活性[DP]。”
"请问原型小姐,你有什么补充?"依赖倒转对着原型问道。
原型显然没想到突然会问到她,而且对于这个问题,多少有点手足无措,她说:“当一个系统应该独立于它的产品创建、构成和表示时,应该考虑用创建性模式。建立相应数目的原型并克隆它们通常比每次用合适的状态手工实例化该类更方便一些[DP]。”
"哈,这可能是我们需要原型的理由。"依赖倒转说道,然后转头问建造者,“请谈谈你对松耦合的理解。”
建造者对这个问题一定是有了准备,不慌不忙,说道:“这个问题首先要谈谈内聚性与耦合性,内聚性描述的是一个例程内部组成部分之间相互联系的紧密程度。而耦合性描述的是一个例程与其他例程之间联系的紧密程度。软件开发的目标应该是创建这样的例程:内部完整,也就是高内聚,而与其他例程之间的联系则是小巧、直接、可见、灵活的,这就是松耦合[DPE]。”
"那么你自己是如何去实践松耦合的呢?"依赖倒转接着问。
“我是将一个复杂对象的构建与它的表示分离,这就可以很容易地改变一个产品的内部表示,并且使得构造代码和表示代码分开。这样对于客户来说,它无须关心产品的创建过程,而只要告诉我需要什么,我就能用同样的构建过程创建不同的产品给客户[DP]。”
"回答得非常好,现在请问单例,你来说说看你参赛的理由,你与别人有何不同?"单一职责问道。
单例小姐有些羞涩,停了一会,才开口说:“我觉得对一些类来说,一个实例是很重要的。一个全局变量可以使得一个对象被访问,但它不能防止客户实例化多个对象。我的优势就是让类自身负责保存它的唯一实例。这个类可以保证没有其他实例可以被创建,并且我还提供了一个访问该实例的方法。这样就使得对唯一的实例可以严格地控制客户怎样以及何时访问它[DP]。”
"工厂方法,请问你如何理解创建型模式存在的意义?"合成聚合复用问道。
此时只听场下一声音叫道,“二姐加油”,原来简单工厂在观众席上喊叫呢。工厂方法对着观众席微笑了一下,然后非常有信心地答道,“创建型模式抽象了实例化的过程。它们帮助一个系统独立于如何创建、组合和表示它的那些对象。创建型模式都会将关于该系统使用哪些具体的类的信息封装起来。允许客户用结构和功能差别很大的’产品’对象配置一个系统。配置可以是静态的,即在编译时指定,也可以是动态的,就是运行时再指定。[DP]”
“那么请问你与其他几位创建型模式相比有什么优势?”
“我觉得她们几位都可能设计出比我更加灵活的代码,但她们的实现也相对就更加复杂。通常设计应该是从我,也就是工厂方法开始,当设计者发现需要更大的灵活性时,设计便会向其他创建型模式演化。当设计者在设计标准之间进行权衡的时候,了解多个创建型模式可以给设计者更多的选择余地。[DP]”
几位评委都在不住地点头,显然,他们非常肯定工厂方法的回答。
"下面有请几位评委写上您们认为表现最好的模式小姐。"GoF说道。
“单一职责先生,您的答案是?”
只见单一职责翻转纸牌,上面写着"单例"。
“非常好,单例小姐已有一票。”
“开放封闭先生,您的选择是?”
"工厂方法。我觉得工厂方法能使得我们增加新的产品时,不需要去更改原有的产品体系和工厂类,只需扩展新的类就可以了。这对于一个模式是否优秀是非常重要的判断标准,我选择她。"开放封闭说道。
“OK,工厂方法小姐也有一票了。”
……
“工厂方法小姐再加一票。”
……
"工厂方法小姐一共有五票。成功晋级,恭喜你。"GoF宣布完,只见工厂方法抱住了旁边的抽象工厂泪流满面,喜极而泣。下面的简单工厂和一帮工厂方法的小Fans们欢呼雀跃。而其他模式的Fans们低头不语,个别竟然已潸然泪下。
“好的,各位来宾,观众朋友们,第一场的比赛现在结束,工厂方法成功晋级,但其他四位选手并不等于没有机会,希望您能通过手机给她们投票,移动用户,请发送OO加选手编号到www.ootv.com,联通用户请发送OO加选手编号到www.ootv.net,电信用户请发送OO加选手编号到www.ootv.org,您的支持将是对落选选手的最大鼓励,最终获得票数最多者同样可以晋级决赛。下面休息一会,插播一段广告。”
ADO.NET开始发牢骚:“什么最大鼓励,根本就是电视台在骗钱。”
"你不发拉倒,我可要给抽象工厂投上一票了。"Hibernate说道。
“唉,算了,反正也就一元钱,我给原型投上一票。”
“哥们,原型没戏了,投抽象工厂吧,这样她进级了,你的钱也不白花。”
"你怎么知道抽象工厂会比原型的票数多,大家都不投她,她能成功吗?我不但投原型,而且要投她十五张票(最高限额)。"ADO.NET坚持道。
“我碰到神经病了。你去打水漂去吧,我不陪你。”
结构型模式比赛
此时的后台,第二组选手正在做着准备,电视台的记者抓紧时间,对适配器小姐做了一个小小的专访。
“适配器小姐,您入选的结构型模式组被称为死亡之组,这一点您怎么看?”
“我觉得,所谓死亡之组,意思是有多个可能得冠军的选手不幸被分在了一组,造成有实力的选手会在小组赛中提前被淘汰,但那是针对体育比赛,我们这种选秀活动,选手只要充分表现了自己,就是成功,最终结果往往是多赢的局面。所以我不担心。”
“您觉得您有可能成为冠军吗?”
“不想当冠军的模式不是好模式。我来了,当然就是要努力争取第一。”
“您是否有与众不同的杀手锏来获得胜利?”
"我有杀手锏?"适配器小姐笑着摇摇头,“努力争取胜利就可以了。不好意思,我得准备去了,再见。”
当适配器离开后,记者小声地对摄像师说,"你把最后一句擦掉。然后我们再录。"接着这记者拿着话筒对着摄像机,正式地说道:“观众朋友,适配器小姐说,她有成功致胜的杀手锏,但她却并没有提及内容,最终是什么让我们拭目以待。OOTV记者赵谣前方为您报道。”
“欢迎回到第一届OOTV杯超级设计模式大赛现场,下面是第二组,也就是结构型模式组的比赛,她们将穿C#休闲装出场。”
“6号选手,适配器小姐,她的口号是将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些=类可以一起工作。[DP]”
6号选手 适配器(Adapter)
“7号选手叫桥接。桥接小姐提倡的是将抽象部分与它的实现部分分离,使它们都可以独立地变化。[DP]”
“8号选手向我们走来,组合小姐,一个非常美丽的姑娘,她的口号是将对象组合成树形结构以表示’部分-整体’的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。[DP]”
8号选手 组合(Composite)
“9号选手,装饰小姐,她的意图非常简单,就是动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更加灵活[DP]。的确,她把自己装饰得非常漂亮。”
“10号选手出现了,外观小姐,她的形象如她的名字一样棒,她说为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。[DP]”
“11号选手是享元小姐,她的参赛宣言为运用共享技术有效地支持大量细粒度的对象。[DP]”
“本组最后一位,12号选手,代理小姐向我们走来,她声称为其他对象提供一种代理以控制对这个对象的访问。[DP]”
观众席中的ADO.NET和Hibernate又开始了讨论。
Hibernate:“C#休闲装我不喜欢,还是Java正装漂亮。”
ADO.NET:“哈,你这么正儿八经的人,当然是不喜欢休闲装,你兄弟NHibernate一定喜欢得不得了,我也是喜欢休闲装的。”
Hibernate:“这一组够强,没有太弱的,你感觉谁最有机会?”
ADO.NET:“不好说,都很漂亮的,各自有各自的特点,你认为呢?”
Hibernate:“我喜欢桥接,太漂亮了,那种解耦的方式,用聚合来代替继承,实在是非常巧妙。”
ADO.NET:“是的,桥接很漂亮,不过装饰也非常美丽。由于善于打扮,所以她可以很好地展示其魅力。”
Hibernate:“说得也是,装饰好歹也是靠化妆自己展示好看,而代理那个小妮子,听说她甚至有可能就不是自己来参加比赛,而是找了一替身。”
ADO.NET:“啊,不会吧,这种谣传你也会信呀,找人替身,那出了名算谁的?”
Hibernate:“当然还是她自己,大牌明星都这样的,找了替身做了很多,最后可能连替身名字都不让人家知道。”
ADO.NET:“代理要是大牌就不用来参加比赛了,出名前一切还是只有靠自己的。说心里话,我最喜欢的是适配器小姐。”
Hibernate:“哦,为什么喜欢她?她好像并不算漂亮。”
ADO.NET:“因为她对我的帮助最大,我在访问不同的数据库,如SQLServer、Oracle或者DB2等时,需要将数据结构和数据都转换成XML格式给DataSet,DataAdapter就是适配器,没有她的帮助,我的DataSet就发挥不了作用,真的很感激她。”
Hibernate:“哈,原来是恩人呀,打小认识的?青梅竹马?哈,好像就你认识她一样,我也和她是老相好哦。”
ADO.NET:“你就吹吧你,之前也听你说你和抽象工厂是相好,现在又和适配器相好,你的相好真够多的。”
Hibernate:“不信拉倒。我们不如来打赌吧,我赌10元钱,桥接会赢。”
ADO.NET:“瞧你那小气样,我赌100元,适配器会赢。”
Hibernate:“100就100,Who怕Who呀。”
"下面有请评委提问。"主持人GoF说。
"请问适配器小姐,刚才记者提到你有成功的杀手锏,那是什么呢?"开放封闭先生问道。
"杀手锏?"适配器心里一咯噔,心想,"那记者太不道义了,我明明没有回答她的问题,怎么就断章取义地说我有杀手锏呢?造谣呀。"犹豫了一下,她说道,“我所谓的杀手锏是说,面向对象的精神就是更好地应对需求的变化,而现实中往往会有下面这些情况,想使用一个已经存在的类,而它的接口不符合要求,或者希望创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类协同工作。正如开放封闭先生您所倡导的对修改关闭,对扩展开放的原则,我可以做到让这些接口不同的类通过适配后,协同工作。[DP]”
开放封闭不住地点头。
"桥接小姐,面对变化,你是如何做的?"合成聚合复用问道。
桥接答道:“继承是好的东西,但往往会过度地使用,继承会导致类的结构过于复杂,关系太多,难以维护,而更糟糕的是扩展性非常差。而仔细研究如果能发现继承体系中,有两个甚至多个方向的变化,那么就解耦这些不同方向的变化,通过对象组合的方式,把两个角色之间的继承关系改为组合的关系,从而使这两者可以应对各自独立的变化,事实上也就是合成聚合复用女士所提倡的原则,总之,面对变化,我主张’找出变化并封装之’。[DPE]”
"这个问题也同样提问给装饰小姐,面对变化,你如何做?"合成聚合复用接着问装饰。
装饰显然对此问题很有信心,答道:“面对变化,如果采用生成子类的方法进行扩充,为支持每一种扩展的组合,会产生大量的子类,使得子类数目呈爆炸性增长。这也是刚才桥接小姐所提到的继承所带来的灾难,而事实上,这些子类多半只是为某个对象增加一些职责,此时通过装饰的方式,可以更加灵活、以动态、透明的方式给单个对象添加职责,并在不需要时,撤销相应的职责。[DP]”
"组合小姐,我们通过你的材料,了解到你最擅长于表示对象的部分与整体的层次结构。那么请问,你是如何做到这一点的?"里氏代换问道。
组合答道:"我是希望用户忽略组合对象与单个对象的不同,用户将可以统一地使用组合结构中的所有对象。"组合回答道,“用户使用组合类接口与组合结构中的对象进行交互,如果接收者是一个叶节点,则直接处理请求,如果接收者是组合对象,通常将请求发送给它的子部件,并在转发请求之前或之后可能执行一些辅助操作。组合模式的效果是客户可以一致地使用组合结构和单个对象。任何用到基本对象的地方都可以使用组合对象。[DP]”
一直没有提过问题的迪米特先生,突然接过话筒,对着外观小姐问了个问题,“请问外观小姐,信息的隐藏促进了软件的复用[J&DP],你怎么理解这句话?”
外观小姐有些紧张,停顿了一会,然后缓缓答道,“类之间的耦合越弱,越有利于复用,一个处在弱耦合的类被修改,不会对有关系的类造成波及。如果两个类不必彼此直接通信,那么就不要让这两个类发生直接的相互作用。如果实在需要调用,可以通过第三者来转发调用。[J&DP]”
"那你又是如何去贯彻这一原则呢?"迪米特继续问道。
"我觉得应该让一个软件中的子系统间的通信和相互依赖关系达到最小,而具体办法就是引入一个外观对象,它为子系统间提供了一个单一而简单的屏障[DP]。通常企业软件的三层或N层架构,层与层之间的分离其实就是外观模式的体现。"外观小姐说话很慢,但显然准备过,并没说错什么。迪米特满意地点了点头。
"享元小姐,请问你如何看待很多对象使得内存开销过大的问题?"单一职责问道。
"对象使得内存占用过多,而且如果都是大量重复的对象,那就是资源的极大浪费[DP],会使得机器性能减慢,这个显然是不行的。"享元说,“面向对象技术有时会因简单化的设计而代价极大。比如文档处理软件,当中的字符都可以是对象,而如果让文档中的每一个字符都是一个字符对象的话,这就会产生难以接受的运行开销,显然这是不合理也是没必要的。由于文档字符就是那么些字母、数字或符号,完全可以让所有相同的字符都共享同一个对象,比如所有用到’a’的字符的地方都使用一个共享的’a’对象,这就可以节约大量的内存。”
"OK,最后一位,代理小姐,请对比一下你和外观小姐,有哪些不同?与适配器小姐又区别在何处?"迪米特问道。
代理没有想到会问这样一个问题,而旁边就站着外观和适配器,如果说得不好,显然就是很得罪人的事,她思考了片刻,说道:"代理与外观的主要区别在于,代理对象代表一个单一对象而外观对象代表一个子系统;代理的客户对象无法直接访问目标对象,由代理提供对单独的目标对象的访问控制,而外观的客户对象可以直接访问子系统中的各个对象,但通常由外观对象提供对子系统各元件功能的简化的共同层次的调用接口。[R2P]"代理停了一下,然后接着说,“至于我与适配器,其实都是属于一种衔接性质的功能。代理是一种原来对象的代表,其他需要与这个对象打交道的操作都是和这个代表交涉。而适配器则不需要虚构出一个代表者,只需要为应付特定使用目的,将原来的类进行一些组合。[DP]”
"下面有请六位评委写上您们认为表现最好的模式小姐。"GoF说道。
桥接
适配器
外观
适配器
桥接
外观
"哦,各位来宾,观众朋友们,第二场结构型模式的比赛真是相当精彩,各位选手也都实力相当,难分伯仲,现在出现了’桥接’‘适配器’'外观’的比分均为两分的相同情况。根据比赛规则,她们三位需要站上PK台,进行PK。
三位有请。"
“下面请三位各自说一说你比其他两位优秀的地方。适配器小姐先来。”
适配器说:"我主要是为了解决两个已有接口之间不匹配的问题,我不需要考虑这些接口是怎样实现的,也不考虑它们各自可能会如何演化。我的这种方式不需要对两个独立设计的类中任一个进行重新设计,就能够使它们协同工作。[DP]"
“非常好,下面有请桥接小姐。”
“我觉得我和适配器小姐具有一些共同的特征,就是给另一对象提供一定程度的间接性,这样可以有利于系统的灵活性。但正所谓未雨绸缪,我们不能等到问题发生了,再去考虑解决问题,而是更应该在设计之初就想好应该如何做来避免问题的发生,我通常是在设计之初,就对抽象接口与它的实现部分进行桥接,让抽象与实现两者可以独立演化。显然,我的优势更明显。[DP]”
“OK,说得很棒,外观小姐,您有什么观点?”
“首先我刚听完两位小姐的发言,我个人觉得她们各自有各自的优点,并不能说设计之初就一定比设计之后的弥补要好,事实上,在现实中,早已设计好的两个类,过后需要它们统一接口,整合为一的事例也比比皆是。因此桥接和适配器是被用于软件生命周期的不同阶段,针对的是不同的问题,谈不上孰优孰劣。然后,对于我来说,和适配器还有些近似,都是对现存系统的封装,有人说我其实就是另外一组对象的适配器,这种说法是不准确的,因为外观定义的是一个新的接口,而适配器则是复用一个原有的接口,适配器是使两个已有的接口协同工作,而外观则是为现存系统提供一个更为方便的访问接口。如果硬要说我是适配,那么适配器是用来适配对象的,而我则是用来适配整个子系统的。也就是说,我所针对的对象的粒度更大。[DP]”
"各个观众朋友们,在评委宣布结果之前,希望您能通过浏览器给她们投票,Chrome用户,Firefox用户,……,您的支持将是对当前选手的最大鼓励,最终获得票数最多者同样可以晋级决赛。现在插播一段广告。"主持人GoF说道。
此时场下观众席中的两位,ADO.NET和Hibernate已经争论得不可开交。
Hibernate:“哈,你我看好的人都在PK台上,不过我相信桥接一定会赢。”
ADO.NET:“那可不一定,没出结果之前,别乱下结论,桥接老说适配器不如她,你没见评委在摇头吗?”
Hibernate:“我坚信桥接一定会赢,你要是不服,我加赌50,也就是150。”
ADO.NET:“哟哟哟,就加50,神气什么呀,要赌就赌大一些,1000,我赌适配器进级。”
Hibernate:“1000太多了点,500吧。”
ADO.NET:“我赌1000,我赢了,你给我1000,我输了,我给你500。”
Hibernate:“小子,你也太狠了。赌,1000就1000。——评委呀,你们一定要看清楚呀,桥接才是真正的美女呀。”
ADO.NET:“哼,走着瞧吧。”
回到现场,"下面有请单一职责先生宣布评委的决定。"GoF大声说道。
"根据我们六位评委的讨论,做出了艰难的决策,最终统一了思想,一致决定,"单一职责停了停,“外观小姐晋级。”
外观小姐眼含泪光,但却保持着镇定,显然胜利的喜悦并没有让她失去常态。
适配器和桥接都非常失望,想哭,却又不得不强忍住泪水,强颜欢笑,对外观表示祝贺。
场下的ADO.NET和Hibernate看不出什么失望,反而都有些高兴。
行为型模式一组比赛
“欢迎回到第一届OOTV杯超级设计模式大赛现场,下面是第三组,也就是行为型模式一组的比赛,她们将穿VB.NET运动装出场。”
“首先出场的是13号选手,观察者小姐,它的口号是定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。[DP]”
13号选手 观察者(Observer)
“14号选手,模板方法小姐,她提倡定义一个操作的算法骨架,而将一些步骤延迟到子类中,模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。[DP]”
“15号选手是命令小姐,它觉得应该将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;可以对请求排队或记录请求日志,以及支持可撤销的操作。[DP]”
“16号是状态小姐,她说允许一个对象在其内部状态改变时改变它的行为,让对象看起来似乎修改了它的类。[DP]”
“本组最后一位,17号选手,职责链小姐,她一直认为使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。[DP]”
观众席中的ADO.NET和Hibernate又开始了讨论。
Hibernate:“VB.NET是你们.NET家族的品牌吧?”
ADO.NET:“是的,最早是BASIC,它是很古老的一个品牌,经过二十多年的发展,它已经成功地从以简单入门为标准转到了完全面向对象,真的很不容易,既简单易懂,又功能强大,所以它做出的运动装非常实用。”
Hibernate:“行为型模式的小姐们长得怎么都不太一样,风格差异也太大了。我不喜欢。”
ADO.NET:“我却觉得还不错,它们大多各有各的特长,比如这一组,应该还是有点看头,观察者、模板方法、命令都算是比较强的选手。”
Hibernate:“多半是观察者胜出了,因为她实在是到处接拍广告,做宣传,什么地方都能见到她的踪影,恨不得通知所有人,她是一设计模式。”
ADO.NET:“我猜也是她,人家本来就是以通知为主要魅力点的模式呀。咱们拭目以待吧。”
"下面有请评委提问。"主持人GoF说。
"请问观察者小姐,说说你对解除对象间的紧耦合关系的理解?"依赖倒转问道。
“我觉得对象间,尤其是具体对象间,相互知道的越少越好,这样发生改变时才不至于互相影响。对于我来说,目标和观察者不是紧密耦合的,它们可以属于一个系统中的不同抽象层次,目标所知道的仅仅是它有一系列的观察者,每个观察者实现Observer的简单接口,观察者属于哪一个具体类,目标是不知道的。”
"非常好,请问模板方法小姐,请你谈谈,你对代码重复的理解以及你如何实现代码重用?"里氏代换问。
模板方法说,“代码重复是编程中最常见、最糟糕的’坏味道’,如果我们在一个以上的地方看到相同的程序结构,那么可以肯定,设法将它们合而为一,程序会变得更好[RIDEC]。但是完全相同的代码当然存在明显的重复,而微妙的重复会出现在表面不同但是本质相同的结构或处理步骤中[R2P],这使得我们一定要小心处理。继承的一个非常大的好处就是你能免费地从基类获取一些东西,当你继承一个类时,派生类马上就可以获得基类中所有的功能,你还可以在它的基础上任意增加新的功能。模板方法模式由一个抽象类组成,这个抽象类定义了需要覆盖的可能有不同实现的模板方法,每个从这个抽象类派生的具体类将为此模板实现新方法[DPE]。这样就使得,所有可重复的代码都提炼到抽象类中了,这就实现了代码的重用。”
"下面请问命令小姐,为什么要将请求发送者与具体实现者分离?这有什么好处?"单一职责问道。
“您的意思其实就是将调用操作的对象与知道如何实现该操作的对象解耦,而这就意味着我可以在这两者之间处理很多事,比如完全可以发送者发送完请求就完事了,具体怎么做是我的事,我可以在不同的时刻指定、排列和执行请求。再比如我可以在实施操作前将状态存储起来,以便支持取消/重做的操作。我还可以记录整个操作的日志,以便以后可以在系统出问题时查找原因或恢复重做。当然,这也就意味着我可以支持事务,要么所有的命令全部执行成功,要么恢复到什么也没执行的状态。总之,如果有类似的需求时,利用命令模式分离请求者与实现者,是最明智的选择。”
“OK,职责链小姐,提问命令小姐的问题同样提问给你,为什么要将请求发送者与具体实现者分离?这有什么好处?你如何回答?”
"我们时常会碰到这种情况,就是有多个对象可以处理一个请求,哪个对象处理该请求事先并不知道,要在运行时刻自动确定,此时,最好的办法就是让请求发送者与具体处理者分离,让客户在不明确指定接收者的情况下,提交一个请求,然后由所有能处理这请求的对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。"职责链答道,“比如我住在县城,生了怪病,我不知道什么级别的医院可以诊治,显然最简单的办法就是马上找附近的医院,让此医院来决定是否可以治疗,如果不能则医院会提供转院的建议,由县级转市级、由市级转省级、由省级转国家级,反正直到可以治疗为至。这就不需要请求发送者了解所有处理者才能处理问题了。”
"非常好,例子很形象,不过得怪病不是好事,健康才最重要。"开放封闭微笑道,“下面请问最后一位,状态小姐,条件分支的大量应用有何问题?如何正确看待它?”
状态答道:“如果条件分支语句没有涉及重要的商务逻辑或者不会随着时间的变化而变化,也不会有任何的可扩展性,换句话说,它几乎不会变化,此时条件分支是应该使用的。但是注意我这里用到了很多前提,这些前提往往都是不成立的,事实上不会变化的需求很少,不需要扩展的软件也很少,那么如果把这样的分支语句进行分解并封装成多个子类,利用多态来提高其可维护、可扩展的需要,是非常重要的。状态模式提供了一个更好的办法来组织与特定状态相关的代码,决定状态转移的逻辑不在单块的if或switch中,而是分布在各个状态子类之间,由于所有与状态相关的代码都存在于某个状态子类中,所以通过定义新的子类可以很容易地增加新的状态和转换。[DP]”
"下面有请六位评委写上你们认为表现最好的模式小姐。"GoF说道。
"喔,观察者3票、模板方法2票、命令1票,最终观察者小姐晋级。"GoF在等评委翻牌后宣布道。“恭喜你,观察者小姐,有什么要说的吗?”
观察者小姐平静地说:“感谢所有关心我、喜欢我和憎恨我的人。比赛中的环境不太干净,但我是干净地站起来的。”
"啊,你,你说憎恨?不干净?什么意思?"GoF非常意外。
"无可奉告。"观察者显然知道刚才那些话的影响,所以选择回避。
"哦,"GoF有些尴尬,"下面先进段广告,广告后我们进行第四组的比赛。"慌忙之中,GoF连让短信投票的宣传都忘记说了。
此时场下观众都议论纷纷。当然ADO.NET和Hibernate两位也不例外。
ADO.NET:“你说她被谁憎恨了?”
Hibernate:“那谁知道。不过一定是来参赛前,受到了一些阻挠,甚至于产生了很大的矛盾,因此才有了憎恨一说。其实憎恨也就罢了,'不干净’一词力道可就重了。”
ADO.NET:“这有什么,只不过观察者她胆子大,说了出来。在娱乐圈、体育圈有潜规则,难道我们程序世界里就没有潜规则?”
Hibernate:“是呀,只要涉及利益,就不可能没有交易。我再给你爆个料,MVC你听说过吗?”
ADO.NET : " 知 道 呀 , 大 名 鼎 鼎 的 MVC 模 式 , 就 是Model/View/Controller,非常漂亮的姑娘。在电视上经常能看到它,好像谈模式、谈架构没有不谈到她的。"
Hibernate:“你可知道为何她没来参加这次超级模式大赛?”
ADO.NET:“咦,对哦,为什么她没来参加呢,要是她来,和这23个比,至少前三是一定可以进的。你不要告诉我,因为她被潜规则了?”
Hibernate:“我偷偷告诉你,你可别出去乱传。MVC是包括三类对象,Model是应用对象,View是它在屏幕上的表示,Controller定义用户界面对用户输入的响应方式。如果不使用MVC,则用户界面设计往往将这些对象混在一起,而MVC则将它们分离以提高灵活性和复用性[DP]。因此,有人甚至说,她是集观察者、组合、策略三个美女优点于一身的靓女。海洗和选拔赛时她都表现非常好的,但因为一次短信的事情,而她又在自己博客里写了《非得这样吗?》的文章,大大地得罪了主办方的一个大鳄。于是由于这件事,她就彻底把自己的前途给葬送了。后来博客的文章也被勒令删除。”
ADO.NET:“得罪谁了?短信什么内容?”
Hibernate:“我哪知道呀,反正她后来就退出比赛了。”
ADO.NET:“你这也叫爆料呀,什么都没说出来,根本就一个听风是雨没任何根据的小道消息。据我猜测,主要原因是这次是设计模式比赛,而MVC是多种模式的综合应用,应该算是一种架构模式,所以被排除在外。”
Hibernate:“不信就算了,不过你说的也有道理。”
行为型模式二组比赛
“欢迎回到第一届OOTV杯超级设计模式大赛现场,下面是行为型模式二组,也就是最后一组的比赛,她们将穿C++旗袍出场。”
“首先出场的是18号选手,解释器小姐,它声称给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。[DP]”
18号选手 解释器(interpreter)
“19号选手是中介者小姐,她说她是用一个中介对象来封装一系列的对象交互。中介者使各对像不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。[DP]”
“20号小姐向我们走来,访问者小姐,表示一个作用于某对象结构中的各元素的操作,使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。[DP]”
20号选手 访问者(Visitor)
“21号小姐是策略,一个可爱的姑娘,她的意图是定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。[DP]”
“22号选手,备忘录小姐,她说在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。[DP]”
22号选手 备忘录(Memento)
“最后一名选手,23号,迭代器小姐,她说,提供一种方法顺序访问一个聚合对象中的各个元素,而又不需暴露该对象的内部表示。[DP]”
Hibernate:“这组里我只认识策略小姐,看过她做过不少广告,迭代器好像也听说过。其他的MM太没名气了,我不看好她们。”
ADO.NET:“中介者也还算行吧,至少我是知道她的。不过这一组实力是不太强,估计策略拿第一没什么悬念了。”
"好的,各位小姐已展示完毕,下面有请评委提问。"主持人GoF说。
"请问解释器小姐,说说你参赛的动机和优势?"依赖倒转问道。
解释器小姐很镇定地答道:“在编程世界里,实现目标都是通过编写语言并执行来实现的,从最低级的机器语言到人能很容易读懂机器也可以执行的高级语言,但是高级语言编写起一些问题可能还是比较复杂。如果一种特定类型的问题发生的频率足够高,那么就可以考虑将该问题的各个实例表述为一个简单语言中的句子。也就是说,通过构建一个解释器,该解释器解释这些句子来解决该问题[DP]。比如正则表达式就是描述字符串模式的一种标准语言,与其为每一个字符串模式都构造一个特定的算法,不如使用一种通用的搜索算法来解释执行一个正则表达式,该正则表达式定义了待匹配字符器的集合[DP]。”
"中介者小姐,人家都说你是交际花,请问你广交朋友的目的是什么?"迪米特问道。
“交际花不敢当,但我的确喜欢交朋友。面向对象设计鼓励将行为分布到各个对象中,这种分布可能会导致对象间有许多连接。也就是说,有可能每一个对象都需要知道其他许多对象。对象间的大量相互连接使得一个对象似乎不太可能在没有其他对象的支持下工作,这对于应对变化是不利的,任何较大的改动都很困难[DP]。所以说朋友多既是好事情,其实也是坏事情。我提倡将集体行为封装一个单独的中介者对象来避免这个问题,中介者负责控制和协调一组对象间的交互。中介者充当一个中介以使组中的对象不再相互显式引用。这些对象仅知道中介者,从而减少了相互连接的数目[DP]。我作为中介者,广交朋友,就起到了在朋友间牵线搭桥的作用。可以为各位朋友们服务。这其实不也正是迪米特先生您一直倡导的最少知识原则,也就是如何减少耦合的问题,类之间的耦合越弱,越有利于复用[J&DP]。”
现在轮到访问者了,她面无表情,不知是否很紧张。合成聚合复用问道:“访问者小姐,听说你对朋友要求很苛刻,要请到你帮忙是很难的事情,你喜欢交朋友吗?”
听到这个问题,访问者笑开了颜:“哪有这种事情,朋友要我帮忙,我都会尽力而为的。的确,我不太喜欢交很多朋友,一般找到好朋友了,就不喜欢再交往新的朋友了。我的理念是朋友在精不在多。但是我和朋友间的交往通常会是多方面的,一同聊天、逛街、旅游、唱歌、游泳,哪怕是我们不会的活动,我们也可以尝试一起去学习、去扩展我们的生活情趣。也就是说,访问者增加具体的Element是困难的,但增加依赖于复杂对象结构的构件的操作就变得容易。仅需增加一个新的访问者即可在一个对象结构上定义一个新的操作。”
"非常有意思的交友观。下面请策略小姐准备接受提问。"GoF说道。
"请问策略小姐,说说你对’优先使用对象组合,而非类继承’的理解?"合成聚合复用问道。
策略小姐答得很流利,“继承提供了一种支持多种算法或行为的方法,我们可以直接生成一个类A的子类B、C、D,从而给它以不同的行为。但这样会将行为硬行编制到父类A当中,而将算法的实现与类A的实现混合起来,从而使得类A难以理解、难以维护和难以扩展,而且还不能动态地改变算法。仔细分析会发现,它们之间的唯一差别是它们所使用的算法或行为,将算法封装在独立的策略Strategy类中使得你可以独立于其类A改变它,使它易于切换、易于理解、易于扩展[DP]。这里显然使用对象组合要优于类继承。”
"策略小姐说得非常好,下面想请问一下备忘录小姐,在保存对象的内部状态时,为何需要考虑不破坏封装细节的前提?"单一职责问道。
“通常原对象A都有很多状态属性,保存对象的内部状态,其实也就是将这些状态属性的值可以记录到A对象外部的另一个对象B,但是,如果记录的过程是对外透明的,那就意味着保存过程耦合了对象状态细节。使用备忘录就不会出现这个问题,它可以避免暴露一些只应由对象A管理却又必须存储在对象A之外的信息。备忘录模式把可能很复杂的对象A的内部信息对其他对象屏蔽起来,从而保持了封装边界[DP]。”
"最后一位,迭代器小姐,说说迭代器模式对遍历对象的意义?"里氏代换问道。
“一个集合对象,它当中具体是些什么对象元素我并不知道,但不管如何,应该提供一种方法来让别人可以访问它的元素,而且可能要以不同的方式遍历这个集合。迭代器模式的关键思想是将对列表的访问和遍历从列表对象中分离出来并放入一个迭代器对象中,迭代器类定义了一个访问该列表元素的接口。迭代器对象负责跟踪当前的元素,并且知道哪些元素已经遍历过了[DP]。”
"下面有请六位评委评选出行为型二组比赛的第一名。"GoF说道。
"迭代器小姐2票、策略小姐4票。"GoF宣布。“晋级的是策略小姐。”
"Yeah!"策略小姐右手伸出食指和中指,打了"V"型手势,向台前晃了晃,然后收到头顶上方握紧拳头做下拉状。
"哈,策略小姐高兴起来真像个孩子,说说感受吧。"GoF对策略的反应也很开心,微笑着说。
"我要感谢OOTV,感谢六位评委,感谢咱爸咱妈,感谢所有支持我的朋友,我爱你们!"策略仿佛已经问鼎冠军一样说了一大堆感谢的话。
"各位观众,请加快给你喜爱的选手投票,广告过后,我们将关闭短信通道。宣布投票结果。"GoF宣布说。
听主持人一宣布,下面的Fans们又纷纷掏出手机开始最后一轮的疯狂短信。
Hibernate:“看来我们英雄所见完全相同。”
ADO.NET:“是呀,策略早就成名了,所以很习惯于这种大场合说些场面话,明星也是练出来的。”
Hibernate:“现在工厂方法、外观、观察者、策略晋级了,除了这几位,你猜短信的结果是谁?”
ADO.NET:“从感觉上来讲,组合、命令、适配器、迭代器都有机会。”
Hibernate:“就不会出黑马?比如抽象工厂、代理、模板方法、桥接等?不过这次确实难料了。”
决赛
"欢迎回来,我们的短信平台刚刚关闭,结果已经出来。"GoF说,“除了四位入选的选手外,短信票数最多的选手是……她是……适配器小姐。”
适配器手捧住脸,刚才PK失利时都没有流泪的她,现在却梨花带雨,楚楚动人。相信她自己也没想到竟然会是自己,所有的人都将目光集中到了这个被称为有"杀手锏"的女孩身上。
"谢谢,谢谢观众朋友,我真心地……感谢……您们。"适配器有些激动,有些泣不成声,和刚才的策略小姐的表现形成了鲜明的对比。
“好的,现在我们比赛已经进入了最高潮,23位选手,经过激烈的比拼,现在选出了五位选手将站在PK台上,来决定今天冠亚季军的归属。她们分别是……工厂方法小姐、外观小姐、观察者小姐、策略小姐和观众朋友选出的可爱的适配器小姐。”
"决赛是一道应用题,希望五位选手根据你们的经验,来说明如果你参与其中,将会发挥什么样的作用,具体如何做。评委将根据各位的临场表现打分,最终评出冠亚季军。"GoF说道,“各位请听题。”
"题目是说有一家以软件开发和服务为主的创业型的公司创业初期初见成效,公司有了发展,员工数量大量增加,于是就考虑自主开发一套薪资管理系统来对公司的员工薪资进行管理,做得好也将作为产品对外销售。公司的员工按照薪资来分类大致可分为普通员工,他们按照月薪制发放,每月有工资和奖金;市场和销售人员,按照每月底薪加提成的方式发放;中高级管理人员,按照年薪加分红的方式发放;兼职人员和临时工,按照小时计费发放。系统要求界面不花哨,但要方便易用,如要有菜单、工具栏和状态栏等要素。产品初步打算用SQL Server作为数据库,但不排除未来成为产品后将可以应用于Oracle、MySQL等数据库来做数据持久化。薪资可以提供多种查询和统计的功能,报表能生成各种复杂的统计图。由于系统是自主研发的产品,所以统计图生成如若时间充裕则自行开发,否则就购买第三方的组件。"GoF念完题目后,接着说,“好了,现在请选手们准备一下,说出你们对这个系统设计的建议。”
“首先有请外观小姐发言。”
"我觉得这是一套典型的企业办公软件,所以在设计上我建议用三层架构比较好,也就是表示层、业务逻辑层和数据访问层。由于公司对业务有明确的需求,但对界面却有模糊性,'不花哨’和’方便易用’实在是仁者见仁,智者见智。所以我的建议是在业务逻辑与表示层之间,增加一个业务外观层,这样就让两层明显地隔离,表示层的任何变化,比如是用客户端软件还是用浏览器方式表示都不会影响到业务与数据的设计。"外观小姐侃侃而谈。
“非常棒的回答,下面有请观察者小姐发言。”
“需求中提到’要有菜单、工具栏和状态栏等要素’,其实不管是C/S架构还是B/S架构,每个按钮或链接的单击都需要触发一系列的事件。而所有的事件机制其实都是观察者模式的一种应用,即当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新,比如状态栏就是一个根据事件的不同时刻需要更新的控件。因为我觉得整个表示层,采用事件驱动的技术是可以很好地完成需求的。我说完了。”
“OK,下面有请适配器小姐回答。”
“本来我以为这个系统没我什么事,但是最后的需求,让我感觉我还是能发挥大作用。需求说’统计图生成如若时间充裕则自行开发,否则就购买第三方的组件’,这里的意思其实就是说,统计图表的生成是自己开发还是购买组件是有变数的。既然这里存在可能的变化,那很显然要考虑将其封装,根据依赖倒转原则,我们让业务模块依赖一个抽象的生成图接口,而非具体的第三方组件或自行实现代码将有利于我们的随机应变。至于第三方组件的接口不统一的问题,完全可以利用适配器模式来处理,达到适应变化的需求。谢谢!”
“哈,看来题目难不倒各位模式的小姐。下面有请策略小姐发言。”
“轮到我了?好的,我是这么想的。公司有多种薪资发放规则,但不管用哪种发放规则,只不过是计算的不同,最终都将是数据的存储与展示,因此我们不希望发放规则的变化会影响系统的数据新增修改、数据统计查询等具体的业务逻辑。应用策略模式,可以很好地把薪资发放规则一个个封装起来,并且使它们可相互替换,这样哪怕再增加多种发放规则,或者修改原有的规则,都不会影响其他业务的实现。回答完毕。”
“Very Very Good!策略小姐的回答,非常精彩。下面有请最后一位,工厂方法小姐发言。”
“只要是在做面向对象的开发,创建对象的工作不可避免。创建对象时,负责创建的实体通常需要了解要创建的是哪个具体的对象,以及何时创建这个而非那个对象的规则。而我们如果希望遵循开放-封闭原则、依赖倒转原则和里氏代换原则,那使用对象时,就不应该知道所用的是哪一个特选的对象。此时就需要’对象管理者’工厂来负责此事[DPE]。比如需求中说到系统做成产品后可以用多种数据库来做持久化。如果我们创建对象时指明了是用SQL Server,那么就会面临着要用Oracle的时候的尴尬问题,因为这里不能适应变化。解决办法是我们可以通过抽象工厂模式、反射技术等手段来让业务逻辑与数据访问之间减少耦合。当然,如果系统比较复杂,也可以使用一种ORM工具来将业务对象与关系数据库进行映射。同样的道理,刚才提到的应用策略模式解决薪资发放规则,用适配器模式解决第三方组件适配等问题,在对象创建时,都可以通过工厂的手段来避免指明具体对象,减少耦合性。另外,在创建对象时,使用抽象工厂、原型、建造者的设计比使用工厂方法要更灵活,但它们也更加复杂,通常,设计是以使用工厂方法开始,当设计者发现需要更大的灵活性时,设计便会向其他创建型模式演化[DP]。总之,在面向对象开发过程中,为了避免耦合,都多多少少会应用工厂方法来帮助管理创建对象的工作。工厂方法的实现并不能减少工作量,但是它能够在必须处理新情况时,避免使已经很复杂的代码更加复杂[DPE]。工厂方法会继续努力,为大家做好优质的服务,谢谢大家。”
"哦,我对你的敬仰真如滔滔江水、连……"GoF突然感觉自己有些失态,连忙收口。“下面有请6位评委慎重做出选择,评出我们本届大赛的季军、亚军,以及我们的——冠军。现在是广告时间,不要走开哦,我们马上回来揭晓答案。”
场下,只见Hibernate双手紧握,举在胸前,ADO.NET则诧异地望着他。
Hibernate:“啊,工厂方法,我真是爱死你了。”
ADO.NET:“花痴,别犯傻了,人家可是大明星了,你爱到死人家也不会知道。”
Hibernate:“我猜工厂方法准赢、确定赢、一定赢、肯定赢!”
ADO.NET:“那可难说得很。其他几位也表现很好的,主要原因是工厂方法最后一个回答,所以占了便宜,不然怎么能举出前面选手所说的例子,这其实都不太公平。”
Hibernate:“咦,你说得对哦,为什么是工厂方法最后一个发言,按道理她应该第一个发言的。”
ADO.NET:“这谁知道呢,说不定又有什么潜规则在里面。”
Hibernate:“啊,如果真是这样,那可实在是太让我这’工仔’伤心了。”
ADO.NET:“‘公仔’?怎么听得像是广东香港地区那边对毛绒玩具的叫法。这是什么意思?”
Hibernate:“你不知道呀,'工仔’就是工厂方法的Fans的统称。”
ADO.NET:“啊,连粉丝统称都有了。太快了吧。”
此时,只听场外口号声四起。
"工厂工厂,工仔爱你,就像老鼠爱大米。"所有工仔们在简单工厂的指挥下举着条幅大叫道。
"模林至尊,策略同盟,号令天下,莫敢不从。"策略的粉丝开始齐呼。
外观的Fans坐不住了,跟着对叫道:“外观不出,谁与争锋。”
相比之下,观察者和适配器的Fans就显得没什么声势。
“各位现场的来宾,观众朋友们,第一届OOTV杯超级设计模式大赛的结果马上就要揭晓,让我们有请我们的总策划兼总导演抽象先生,上台来宣布结果。”
只见50多岁的抽象先生,缓缓走上台前,接过主持人GoF交给他的信封,对着话筒说:“真的很高兴能看到今天的大赛举办得圆满成功。在这我代表组办方,向所有为这次大赛付出艰苦努力的工作人员表示衷心的感谢。望着这23位模式小姐,她们在台上尽情地**‘群模乱舞’,我们在台下感受到了她们的’模法无边’**。非常值得庆贺的是,23位小姐都发挥出了应有的水平,赛出了风格,赛出了水平。我……”
GoF打断了抽象先生的说话,“请您打开信封宣布结果。”
"哦,对对对,我是来宣布结果的。"抽象先生反应过来,拆开了信封,念道,“第一届OOTV杯超级设计模式大赛决赛入围的有:工厂方法、外观、观察者、策略、适配器。她们五位设计模式小姐表现都很出色。其实说到底,面向对象设计模式体现的就是抽象的思想,类是什么,类是对对象的抽象,抽象类呢,其实就是对类的抽象,那接口呢,说白了就是对行为的抽象。不管是什么,其实……”
"抽象先生,请您宣布结果。"GoF不得不再次提醒他。
"哦,你等我说完,其实都是在不同层次、不同角度进行抽象的结果,它们的共性就是抽象。所以……"抽象先生打了个咯噔,“我宣布,第一届OOTV杯超级设计模式大赛的冠军是……”
梦醒时分
时间:7月23日晚上23:50 地点:小菜自己的卧室 人物:小菜、大鸟
"喂,小菜,醒醒。"大鸟站在小菜旁边,推了推他,“这样趴着睡会感冒的,上床去睡。”"啊,大鸟呀,你坏我好梦。"小菜用手揉着眼睛说道,“早不来晚不来,刚梦到要宣布结果,你就来了。气死我了。”
“你梦到什么了?都快12点了,快去睡觉吧。”
“这下是再也不可能梦到抽象宣布结果了。你说怎么办?”
“什么抽象宣布结果,你做什么鬼梦呢?”
“明天我们公司让我去做关于应用设计模式体会的演讲,我本想等你回来问问你的,一不小心就睡着了,做了一个好梦,但却被打扰了。”
“哦,这是好事呀,明天演讲什么内容,你想好了吗?”
“本来是没想好,不过现在吗,嘿嘿,我已经有数了。”
“你打算怎么讲?”
“保密,明天我回来再对你说。我去睡觉去了。”
“好吧,祝你好运。晚安。”
时间:7月24日下午15:30 地点:小菜公司大会议室 人物:全公司成员
“今天我给大家讲一个关于’OOTV杯超级模式大赛’的故事。故事是这样开始的……”
小菜的故事讲得非常精彩,成了公司的技术明星。在随后的日子里,逐步完成了由菜鸟程序员向优秀软件工程师的蜕变,并继续向着成为软件架构师的理想前进。
没有结束的结尾
生活还在继续,编程也不会结束。每天晚上,小菜和大鸟继续着对程序、对爱情、对理想、对人生的讨论和思考。而我们学习设计模式的故事,却要暂时告一段落了。祝愿您在学习设计模式的过程当中,学有所获,阅有所思。小菜和大鸟的故事虽然暂时告一段落了,但你的面向对象编程之路或许才刚刚开始。相信通过你的努力,你的人生会更加精彩。
如果对你有帮助,就一键三连呗(关注+点赞+收藏),我会持续更新更多干货~~