




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、深入浅出装饰模式一、引子装饰模式?肯定让你想起又黑又火的家庭装修来。其实两者在道理上还是有很多相像的地方。家庭装修无非就是要掩盖住原来实而不华的墙面,抹上一层华而不实的涂料,让生活多一点色彩。而墙还是那堵墙,他的本质一点都没有变,只是多了一层外衣而已。那设计模式中的装饰模式,是什么样子呢?通常OO程序员会用继承的方法来扩展类的功能,并进而解决所面临的问题。但继承的威力最多也只能到编译时刻为止,运行时它也无能为力了(这里还没有说继承可能带来的麻烦,我们将在后面见到)。但是能够在运行时对类功能进行扩展那才叫“牛”。且看装饰模式是如何做到这点的。二、定义和结构装饰模式(Decorator Patte
2、rn)也叫包装器模式(Wrapper Pattern)。GoF在设计模式一书中给出的定义为:动态地给一个对象添加一些额外的职责。换而言之,装饰类为子类扩展新功能提供了一种灵活的替换机制,Decorator模式比生成子类更为灵活。这里有一家著名的咖啡店Starbuzz,正在抓狂地更新其订货系统,以适应不断增长的供货要求。为了是咖啡更好喝,或更符合你的口味,你可以向咖啡店要多种调味品,如steamed milk、soy、mocha,或巧克力等等(当然这都是要付钱的,否则就不会有我们这里的问题的 J)。Starbuzz咖啡店为了调味品计费的问题头都大了,因为它们都要单独处理才行,请看他们的第一个设计
3、:怎么样?类爆炸了吧!这是因为每种咖啡中的cost( ) 方法都要考虑其订单中的不同的调味料计费问题。上面的设计显然太蠢了,看看一个更聪明的OO程序员是如何设计的。他将设计一些实例变量,并用继承超类的方法来追踪调味料的情况。然后再添加子类,它们各自对应于菜单上的一种咖啡(或其它饮料):相关代码如下,其中最关键的是cost( )方法的代码:看起来还不错,一共也只有5个类,cost( )方法也不算太复杂。那么这样的设计行吗?请想想下列问题:l 当每种调料的价格发生变化怎么办?好象问题不大,我们还不必改写代码!l 如果要加入新的调味料该怎么办?我们不得不增加新方法并修改超类中的cost( )方法!l
4、 咖啡店还可以卖别的饮料。如Ice Tea,调味料是不适用于它们的,但子类IceTea继承hasWhip( )这样的方法仍然是非常合理的。千万不要忘记分类学的原则,想想上述设计违反了分类学的哪些原则?l 如果客人想要两份mocha调料,该怎么办?l 如果客人想要三份whip调料,该怎么办?l 如果客人想要四份soy调料,该怎么办?l 等等其它问题此时我们可以考虑采用装饰模式来动态的解决上述问题了。先看看装饰模式的组成,不必急于解决上面的问题,到了下面自然就明白了!1. 抽象构件角色(Component):定义一个抽象接口,以规范准备接收附加责任的对象。2. 具体构件角色(Concrete Co
5、mponent):这是被装饰器,定义一个将要被装饰增加功能的类。3. 装饰角色(Decorator):持有一个构件对象的实例,并定义了抽象构件定义的接口。4. 具体装饰角色(Concrete Decorator):负责给构件添加增加的功能。以下是装饰模式的类图:卧室谈话Mary:哦,我有点困惑我以为我们不会在这个模式中使用继承,而是更加依赖组合来解决问题。Sue: 你什么意思?Mary:看看这个类图。类CondimentDecorator扩展了类Beverage。这是继承,对吗?Sue: 对。我想关键点在于装饰器必须与被装饰对象具有相同的类型。因此我们在这里使用了继承以获得类型匹配的效果,但我
6、们并不使用继承来获得行为。Mary:哦,我知道装饰器为何需要具有与被包装组件一致的“接口”了,因为它们需要取代后者。但相关的行为从哪里来呢?Sue: 当我们将一个装饰器与一个组件组合在一起时,我们就在增加新的行为。我们不是通过继承超类,而是通过将对象组合在一起来获得新的行为。Mary:好的,因此我们继承了抽象类Beverage以便具有正确的类型,而不是继承它的行为。行为是通过与其它装饰器进行组合而加入的。Sue: 非常正确。Mary:喔我知道了,因为我们使用了对象组合,我们在如何将调味品与饮料混合起来的问题上获得了全面而更多的灵活性。非常顺畅。Sue: 是的,如果我们依赖继承的话,那么我们的行
7、为在编译时就被静态地确定了。换句话说,我们只能拥有超类给予的,或是我们重载的行为。而通过组合,我们可以将装饰器任意地进行混合和匹配在运行时。Mary:而按我的理解,我们可以在任意时刻实现新的装饰器以增加新的行为。如果我们依赖继承,当我们需要新的行为时就必须进入并修改原有的代码了。Sue: 完全正确。Mary:我还有一个问题。如果我们所需要继承的不过是组件的类型,为什么我们不使用一个接口,而是使用Beverage这个抽象类呢?Sue: 好的,请记住,当我们得到代码时,Starbuzz公司已经有了一个抽象类Beverage。传统上装饰器模式确实会指定一个抽象组件,但在Java中,显然我们可以使用接
8、口。但由于我们总是避免修改已有代码,因此当抽象类能够很好地工作时就不需要“改正”它。三、解决方案让我们考虑一个咖啡订货实例,客户需要一份Dark Roast咖啡,并且还要了一份Mocha和Whip调味品。我们将这样进行工作:现在,我们知道了:l 装饰器与被装饰对象具有相同的父类型(supertype);l 你可以使用一个或多个装饰器包装一个对象;l 由于装饰器与被装饰对象具有相同的父类型,我们可以分发被装饰对象以替换原始(被包装的)对象;l 装饰器可以在委托给被装饰对象之前或之后增加其自身的行为,以完成剩余的工作(The decorator adds its own behavior eith
9、er before and/or after delegating to the object itdecorates to do the rest of the job)。这样就能够很容易地扩展被包装类的功能,而且没有修改被包装类和它的父类;l 对象可以在任何时候被装饰,因此在运行时可以用装饰器动态、随意地装饰我们的对象。我们可以看一下具体的程序了:StarbuzzCoffee.java Beverage.javaCondimentDecorator.java DarkRoast.javaSoy.java Decaf.javaEspresso.java HouseBlend.javaMilk
10、.java Whip.java以下并非无聊的问题问题:我对那些可能用于测试特定具体组件的代码有些担心就是说,HouseBlend或其它什么东西,喜欢打折扣的话。一旦我将HouseBlend用装饰器包裹起来的话,那打折扣的行为就不管用了。回答:确实如此。如果你的代码依赖于具体组件的类型,那么装饰器将破坏那些代码。只要你写的代码只依靠抽象组件类型,那么装饰器的使用对于你的代码而言仍然是透明的。然而,一旦你根据具体组件来写代码,你应该对你的应用程序设计以及装饰器的使用进行重新的思考。问题:某些客户的饮料不是很容易在最后出问题吗?它用其它装饰器,而没有用最外层装饰器包装上。就象我有一杯DarkRoas
11、t咖啡,加了Mocha、Soy和Whip等调料,在写代码时很容易在最后用了Soy类的引用,而忘记加上Whip类的引用,那就意味着没有按顺序包括Whip调料。回答:你当然会争论说由于必须用装饰模式管理更多的对象,因此编码错误的几率增加了,从而导致你设想中的各种问题。然而,装饰器通常是用其它模式,如工厂模式或建造模式产生的。一旦你学习了如何使用这些模式,你会发现具体组件与它的装饰器的生成是“包装好”的,并不会导致这些问题。问题:装饰器能够知道装饰链上的其它装饰器吗?也就是说,我希望自己的getDescription( )方法打印“Whip,Double Mocha”,而不是打印“Whip,Moch
12、a, Mocha”,可以吗?那就需要我的最外层装饰器了解在它包装之内的所有装饰器。回答:装饰器的本意是为它们包裹的对象增加行为。当你需要窥探装饰链上的多个层次时,你就是把装饰器推离它的本意了。尽管如此,这种事情是可能的。设想一个CondimentPrettyPrint装饰器,它对说明做最后的解析,并且能够将“Mocha, Whip,Mocha”打印成“Whip,Double Mocha”。请注意getDescription( )方法可以返回一个有关说明的列表,使得这项任务变得容易些。四、现实世界中的装饰模式:Java I/O我们可以仔细阅读关于java.io API的文档,看看各种装饰器是如何
13、与各种input流组合在一起的。请看以下两张图:以上两图说明了FilterInputStream是一个抽象装饰器类,而BufferedInputStream和LineNumberInputStream是具体的装饰器类。炉边夜话HeadFirst:欢迎装饰模式。我们听说你最近有点自己看不起自己?Decorator:是的,我知道全世界将我看作是一种富有魅力的模式,但你知道,就象其他所有人一样,我也有自己的问题。HeadFirst:你愿意把你的麻烦告诉我们吗?Decorator:当然了。嗯,你知道我有本事增加设计的灵活性,这是非常有用的,但我也有不好的一面。你看到,我有时会向设计增加许多小类,而这导
14、致设计对别人不够直观,不好理解。HeadFirst:你能给我们一个例子吗?Decorator:就拿Java I/O库来说吧。开始的时候,它对用户而言是出了名的不好理解。但如果人们将这些类看做是围绕着一个InputStream的一组包装器,那事情就变得简单多了。HeadFirst:那听起来不算太坏呀。你仍然是一个伟大的模式,而改进不足之处只是一个公共教育问题,对吗?Decorator:我担心问题会更多。我有类型问题:你看,人们有时将一部分依赖于特定类型的代码拿过来,然后没有经过好好思考就引入装饰器。现在,我的一个伟大之处就是你能经常透明地插入装饰器而客户决不需要知道它是在和装饰器打交道。但就象我
15、说的,有些代码依赖于特定的类型,而当你引入装饰器后,砰!坏事发生了。HeadFirst:哦,我认为每个人都懂得当插入装饰器时必须要当心,我不认为你应为了这件事情而感到自卑了。Decorator:我知道,我不必如此。我另外的问题就是,对于实例化组件所需要的代码,引入装饰器会增加复杂性。一旦你有了装饰器,你就不但需要实例化组件,还要将它用天知道多少个装饰器把它包装起来。HeadFirst:我会在下星期访问工厂和建造者模式我听说他们对这个问题很有帮助。Decorator:那好啊;我应该和那些家伙好好谈谈。HeadFirst:好的,我们大家都认为你是一个伟大的模式,能产生灵活的设计并遵循了OCP原则,
16、所以请抬起头来,想开些。Decorator:我会努力的,谢谢你。五、装饰模式的不足装饰模式的不足之处有(不过这些缺点好象也没有什么了不起 J):l 有时装饰模式需要增加太多的小类,这导致设计显得不够直接而难于理解(sometimes add a lot of small classes to a design and this occasionally results in a design thats less than straightforward for others to understand);l 由于装饰器的插入方式是透明,客户不可能知道他正在和装饰器打交道(you can us
17、ually insert decorators transparently and the client never has to know its dealing with a decorator)。六、其它信息对于面向接口编程,应该尽量使客户程序不知道具体的类型,而应该对一个接口操作。这样就要求装饰角色和具体装饰角色要满足Liskov替换原则。像下面这样:Component c = new ConcreteComponent( );Component c1 = new ConcreteDecorator( c );这种方式被称为透明式。而在实际应用中,比如java.io中往往因为要对原有接口做太多的扩展而需要公开新的方法(这也是为了重用)。所以往往不能对客户程序隐瞒具体的类型。这种方式称为“半透明式”(java.io包中的类并不是纯装饰模式的范例,它是装饰模式、适配器模式的混合使用)。关于该模式的应用环境,GoF书中给出了以下适用情况:1. 在不影响其他对象的情况下,以动态、透明的方式给单个对
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 中华文化图片展
- 《深圳大运城市志愿服务站点工作指引培训课件 - 站点概述》
- 服装品牌双十一营销策略
- 元件行业研究报告
- 高三一轮复习生物:走近细胞课件
- 《食管癌详细解读》课件
- 走进学习掌握考试
- 《现代金融科技》课件
- 2025年农产品销售合同简单版本
- 《室内绿植布置》课件
- LED制程与工艺介绍
- 《马克思主义中国化思想通史》导读-南京林业大学中国大学mooc课后章节答案期末考试题库2023年
- 北京中考语文词语表
- 水资源利用智慧树知到答案章节测试2023年西安理工大学
- 水质对干豆腐品质的影响机制及调控技术
- LY/T 2676-2016半干旱地区灌木林平茬与复壮技术规范
- 装配式混凝土结构的构件安装分项工程(验收批)质量验收记录表
- 作业许可检查表
- 农产品集中交易市场等级技术规范-编制说明
- 张京16分钟中英文对照翻译稿
- 武汉绿地中心项目技术管理策划书(48页)
评论
0/150
提交评论