装饰模式

整理文档很辛苦,赏杯茶钱您下走!

免费阅读已结束,点击下载阅读编辑剩下 ...

阅读已结束,您可以下载文档离线阅读编辑

资源描述

TheDecoratorPattern(装饰模式)装饰模式要解决的问题提供一种修改类的行为,而避免创建众多的派生类的途径。演示实例实例背景有这么一家咖啡连锁店,可以这样来形容其发展速度:如果今天在你吃午餐的小店边上有一家,明天在它的对面就会看到另一家。关于咖啡的知识咖啡种类Houseblend:家常混合咖啡Decaf:无咖啡因咖啡Darkroast:黑咖(将咖啡豆深度烘培至接近暗黑)Espresso:,爱斯普利索咖啡(把粉末状的咖啡豆用蒸汽加压煮出来的浓咖啡),意大利浓咖啡Latte:拿铁(意大利浓缩咖啡与牛奶的经典混合)Cappuccino:卡布奇洛(在偏浓的咖啡上,倒入以蒸汽发泡的牛奶)咖啡调料Mocha:摩卡(巧克力)milk:Soy:豆奶Whip:起泡牛奶(经过搅打使奶油起泡)开发咖啡点单系统因为生意兴隆,他们决定将其点单系统计算机化,以应付繁忙的业务需要。开始时他们设计的类图如下:初始类图HouseBlendCost()BeveragedescriptiongetDescription()Cost()//OthermethodsDarkRoastCost()DecafCost()EspressoCost()抽象类,被该店的所有饮料类继承。实例变量description在子类中保存对相应饮料的描述。cost()方法是一个抽象方法。需要在子类中实现。每个子类实现cost()方法,以计算它们的价格。问题…除了咖啡,客人还可以选择一些调味品。每种调味品都要收取一些费用,所以这些也应该包含在点单系统之内。他们想通过每个类的cost()方法来实现。结果是。。。类爆炸!!HouseBlendCost()BeveragedescriptiongetDescription()Cost()//OthermethodsDarkRoastCost()DecafCost()EspressoCost()HouseBlendCost()HouseBlendCost()HouseBlendCost()HouseBlendCost()HouseBlendCost()HouseBlendWithSteamedMilkandMochaCost()DecafCost()EspressoCost()DecafCost()EspressoCost()DecafCost()EspressoCost()DecafCost()EspressoCost()每个cost()方法计算出咖啡和调味品的价格。这显然是一个恶梦试设想:如果咖啡的价格发生变化如果新出了一种咖啡调料对系统开发人员来说意味着什么?怎么办?一种想法是,用实例变量和继承机制来解决各种调味品的问题新方案中基类的类图BeveragedescriptionmilksoymochawhipgetDescription()cost()hasMilk()setMilk()hasSoy()setSoy()…//Othermethods对应每种调味品的布尔型变量现在,cost()方法在基类中将被实现,用以计算与特定饮料的调料价格,同时,该方法在子类中将被重写,并被调用以计算基本饮料价和调味品价的总价。设置各种调味品实例变量的布尔值新设计方案的类图HouseBlendCost()DarkRoastCost()DecafCost()EspressoCost()每个子类实现cost()方法,以计算它们的价格。BeveragedescriptionmilksoymochawhipgetDescription()cost()hasMilk()setMilk()hasSoy()setSoy()…//Othermethods计算调料价格,该方法在子类中将被重写,扩展为计算包括基本饮料价格和调味品价格的饮料总价。基类的cost()代码应该是怎样的?PublicclassBeverage{...publicdoublecost(){}...}只计算调料价格子类的cost()代码应该是怎样的?PublicclassDarkRoastextendsBeverage{publicDarkRoast(){description=“优质DarkRoast咖啡”;}publicdoublecost(){}...}计算完整的咖啡价格,包括调料的价格这一设计方案只有五个类该方案有什么不足?如果调味品价格变化怎么办?如果增加新的调料怎么办?如果一个顾客点了双份的某种调料怎么办?面向对象设计的开-闭原则类应该对扩展开放,对修改封闭。目的是在不需修改已有代码的情况下方便的扩展类的功能。按照这一原则设计的系统具有以下优点:具有一定的适应性和灵活性。具有一定的稳定性和延续性。讨论“对扩展开放,对修改封闭。”这听起来自相矛盾啊!有哪些方法可以不修改已有代码但又能扩展其功能?何时使用开闭原则?关于讨论的一些参考尽管看上去有些自相矛盾,但在设计中实现这一原则是可能的。观察者模式就遵循了这一原则。类继承和类组合都能在一定程度上帮助实现开闭原则。本讲的装饰模式也遵循这一原则。咖啡店点单系统可以用装饰模式很好的来解决。在设计时应该认真选择需要扩展的部分。对设计的每一个部分都应用这一原则是不必要的,不仅费力,而且可能导致复杂难懂的代码。装饰模式实战我们已经看到用继承机制解决我们的咖啡店问题不太理想:或者会导致“类爆炸”,或者是导致基类包含一些子类中并不需要的成分。我们可以换一种做法:从一种基本饮料开始,在运行时用各种调味品对该饮料进行“装饰”。例如,如果一个顾客点了Darkroast咖啡加mocha和whip,那么就可以:生成一个Darkroast对象用一个mocha对象装饰它用一个whip对象装饰它调用装饰后的对象的cost()方法,依靠委派来计算含调味品的咖啡价钱何谓装饰?如何装饰?所谓装饰就是将一个对象包装起来。在程序上也就是让一个对象a包含另一个对象b。a对应的类A是“装饰类”,b对应的类B是被装饰类。如果要让装饰可以重复,且不必考虑次序,那么装饰的要点是装饰者和被装饰者具有相同的类型(有共同的父类)。首先我们生成一个DarkRoast对象DarkRoastCost()DarkRoast继承了Beverage,拥有一个计算饮料价格的方法cost()。MochaCost()然后DarkRoastCost()Mocha对象是装饰者,他与被它装饰的对象DarkRoast具有相同的类型(是Beverage的子类),也有一个cost()方法。顾客想要mocha,所以我们创建一个Mocha对象,并用它包装DarkRoast.再然后MochaCost()DarkRoastCost()whip对象是装饰者,他与被它装饰的对象DarkRoast具有相同的类型,也有一个cost()方法。顾客还想要whip,所以我们创建一个Whip对象,并用它包装Mocha.WhipCost()现在,要计算饮料的价格MochaCost()DarkRoastCost()调用最外层的装饰者whip的cost(),whip再将计算任务委派给被它包装的对象,得到一个价格后,再加上whip自己的价格...WhipCost()5.005.00+0.505.50+1.006.50何谓委派?所谓委派就是一个对象将工作(或工作的一步分)交给另一个对象来完成。在装饰模式中,委派是指装饰对象将任务交给被装饰对象来完成。委派可以传递,最终必须要有一个干实事的对象。问题如果一个顾客点了一份DarkRoast加两份mocha和一份soy,其装饰对象图和价格计算过程有什么样的呢?要点装饰者与被装饰者具有相同的类型可以用多个装饰者装饰一个对象由于装饰者与被装饰者具有相同的类型,我们可以用装饰后的对象代替原来的对象。装饰者在委派它装饰的对象作某种处理时,可以添加上自己的行为(功能扩展)(在委派之前或/和之后)。对象可以在任何时候被装饰,因此我们能在运行时动态的装饰对象。装饰模式的定义装饰模式可以动态的给一个对象附加一些功能。使用装饰模式扩展功能不会产生类爆炸。它采用的是合成方式,比继承方式更加灵活。装饰模式的类图结构ComponentmethodA()methodB()//OthermethodsConcreteDecoratorAComponentwrappedObjmethodA()methodB()//othermethodsConcreteComponentmethodA()methodB()//OthermethodsDecoratormethodA()methodB()//OthermethodsConcreteDecoratorBComponentwrappedObjObjectnewStatemethodA()methodB()//othermethodscomponent咖啡店的类图HouseBlendCost()BeveragedescriptiongetDescription()Cost()//OthermethodsDarkRoastCost()DecafCost()EspressoCost()CondimentDecoratorgetDescription()MochaBeveragebeveragegetDescription()Cost()WhipBeveragebeveragegetDescription()Cost()具体的咖啡品种调味品装饰者,不仅实现cost(),还要实现getDescription()考虑一下本讲开头的引例是如何实现的。咖啡店源代码Beverage.javapublicabstractclassBeverage{Stringdescription=UnknownBeverage;publicStringgetDescription(){returndescription;}publicabstractdoublecost();}CondimentDecorator.javapublicabstractclassCondimentDecoratorextendsBeverage{publicabstractStringgetDescription();}DarkRoast.javapublicclassDarkRoastextendsBeverage{publicDarkRoast(){description=DarkRoastCoffee;}publicdoublecost(){return0.99;}}请编写另外三种咖啡类的代码EspressoHouseBlendDecafMocha.java(具体装饰者:调味品Mocha)publicclassMochaextendsCondimentDecorator{Beveragebeverage;publicMocha(Beveragebeverage){this.beverage=beverage;}publicStringgetDescription(){returnbeverage.getDescription()+,Mocha;}publicdoublecost(){return0.20+beverage.cost();}}我们希望不仅描述咖啡本身,还要包括调味品,所以先委派被包装的对象获取它的描述,再加上调味品的描述。实例化的调味品Mocha包装一种饮料:方法是申明一个Beverage实例变量,通过构造方法对该变量赋值,引用被包装的饮料。计算价格时,先委派被包装的对象计算其价格,再加上调味品的价格。请编写其他调味品类的代码WhipMilkSoy咖啡店营业:StarbuzzCoffee.javapublicclassStarbuzzCoffee{publicstaticvoidmain(Stringargs[]){Beveragebeverage=newEspresso();System

1 / 42
下载文档,编辑使用

©2015-2020 m.777doc.com 三七文档.

备案号:鲁ICP备2024069028号-1 客服联系 QQ:2149211541

×
保存成功