您好,欢迎访问三七文档
1第八章装饰者模式欢迎饮用星巴兹(Starbuzz)咖啡最初设计的订购系统(下页):2BeverageHouseBlendDarkRoastDecafEspressodescriptiongetDescription()cost()//其它方法cost()cost()cost()cost()Beverage(饮料)是一个抽象类。cost()方法是抽象的。每个子类实现cost()方法,以返回饮料的价钱。description实例变量,由每个子类设置,用来描述饮料。焦炒咖啡家庭混合咖啡无咖啡因咖啡蒸馏咖啡3购买咖啡时,每一种咖啡中能添加一种或几种调料:steamedmilk(蒸煮的牛奶),soy(酱油),mocha(摩卡,也称为巧克力),和whippedmilk(加了甜点心的牛奶)。每一种调料都要收一点钱,星巴兹(Starbuzz)咖啡订购系统变成下面的样子(下页):4BeveragedescriptiongetDescription()cost()//其它方法cost()cost()cost()cost()cost()cost()cost()cost()cost()cost()cost()cost()cost()cost()cost()cost()cost()cost()cost()cost()cost()cost()cost()cost()cost()cost()HouseBlenHouseBlendWithSteamedMilkandMochaDarkRoastWithSteamMilkandMochaDecafWithSteamedMilkandMochaEspressoWithSteamedMilkandMochaDarkRoastWithSteamedMilkandCaramelDecafWithSteamedMilkandCaramelEspressoWithSteamedMilkandCaramelEspressoWithWhipandMochaDecafWithSoyDecafWithSoyandMochaEspressoWithSteamedMilkandWhipEspressoWithWhipandSoyDecafWithWhipandSoyDarkRoastWithSoyandSoyHouseBlendWithSoyandSoyHouseBlendWithWhipHouseBlendWiHouseBlendWandHouDarkRoastWithSteamedMilkandDecafWithSteameandDarkRoastWDEspressoWithcost()DecafWithSteamedMilkandHouseBleDarkRoastWitDecafWithcost()HouseBDarkRoastWithSteamDarkRoaDecafWithSteamedMilkDecDarkRoastWcost()DarkRoastWithSteamedMilkandSoyDecafcost()cost()cost()cost()cost()每个cost()方法计算咖啡及各种调料的价钱。类爆炸!从基类派生添加了各种调料的咖啡子类。5太蠢啦,我们需要这么多类吗?我们为什么不能在超类中使用实例变量和继承,记录这些调料呢?我们试一试。在Beverage基类中增加实例变量,表示是不是需要添加某种调料。布尔值cost()不再是抽象的方法,它计算要加入各种饮料的调料价钱。子类仍将覆盖cost(),但是会调用超类的cost(),计算基本饮料加上调料的价钱。获取或设置布尔值BeveragedescriptionmilksoymochawhipgetDescription()cost()hasMilk()setMilk()hasSoy()setSoy()hasMocha()setMocha()hasWhip()setWhip()//其它方法6BeveragedescriptionmilksoymochawhipgetDescription()cost()hasMilk()setMilk()hasSoy()setSoy()hasMocha()setMocha()hasWhip()setWhip()//其它方法HouseBlendDarkRoastDecafEspressocost()cost()cost()cost()子类cost()覆盖基类的cost(),利用基类计算调料价格,再加上饮料价格,得到总价格。基类cost()计算所有调料的价格。修改后的订购系统7这个设计太好了!一共只有5个类。等等!将来一旦情况发生变化,需要修改系统时,这个设计可能存在一些潜在的问题。修改后的订购系统怎么样?8这个设计的缺陷•如果调料的价格改变,我们需要修改现存的代码。•如果增加了新的调料,我们需要在基类增加新的方法以及修改cost()方法。•我们也可能有新的饮料类型。一些饮料类型,例如,冰茶(icetea),现有的调料可能是不合适的。但是,茶子类仍然后继承基类的调料方法。•如果客户需要双份mocha,怎么办呢?9设计原则类应该对扩展开放,对修改封闭。开闭原则我们的目的是允许类易于扩展,组合新的行为,但不修改现有的代码。10会会装饰者模式我们已经看到:附加调料的饮料定价模式,用继承表示是不合适的;在基类增加调料的实例变量和方法,对一些子类也是不合适的。这里,我们试试:以饮料为主体,用调料“装饰”饮料。例如,如果客户需要DarkRoast(焦炒咖啡),添加Mocha(摩卡)和Whip(甜点心)。11会会装饰者模式(续)我们可以:①取DarkRoast(焦炒咖啡)对象;②用Mocha(摩卡)对象装饰它;③用Whip(甜点心)对象装饰它;④调用cost()方法计算价格,总价格的计算需要委托。但是,我们怎样装饰一个对象?又怎样委托?12用装饰者构造饮料订购系统①从DarkRoast(焦炒咖啡)对象开始DarkRoast继承于Beverage,有计算饮料价格的cost()方法。13②客户需要Mocha(摩卡),所以,我们创建一个Mocha对象,并用它包装DarkRoast(焦炒咖啡)。Mocha对象是一个装饰者。它的类型与被装饰的对象(Beverage)相同。Mocha也有一个cost()方法,通过多态,我们能处理包装在Mocha里的Beverage。装饰者对象是“包装者”。14③客户也需要Whip(甜点心),所以,我们也创建一个Whip装饰者,并用它包装Mocha(摩卡)。15④现在为客户计算价格。我们调用最外层装饰者(Whip甜点心)的cost()。Whip委托它所装饰的对象计算价格。它获得了价格后,再累加Whip的价格。首先调用最外层装饰者Whip的cost()。Whip调用Mocha(摩卡)上的cost()。Mocha调用DarkRoast(焦炒咖啡)上的cost()。DarkRoast(焦炒咖啡)返回它的价格99美分。Mocha再累加它的价格20美分,返回1.19美元。Whip再累加它的价格10美分,返回最终的总价格1.29美元。16目前做的…•装饰者和被装饰对象有相同的超类型;•可以用一个或多个装饰者包装一个对象;•任何时候需要原始对象(即被包装的对象),都可以用装饰过的对象代替它。因为装饰者和被装饰者有相同的超类型;•装饰者可以在所委托的被装饰者的行为之前与/或之后,加上自己的行为,以达到特定的目的。•对象可以在任何时候被装饰,所以可以在运行时动态地、不限量地使用装饰者来装饰对象。17装饰者模式定义装饰者模式动态地给对象添加职责。就扩展功能而言,装饰者比派生子类提供了更好的柔性。18ComponentConcreteComponentDecoratorConcreteDecoratorAConcreteDecoratorBmethodA()methodB()//其它方法methodA()methodB()//其它方法ComponentwrappedObjmethodA()methodB()newBehavior()//其它方法ComponentwrappedObjObjectnewStatemethodA()methodB()//其它方法methodA()methodB()//其它方法每一个装饰器HAS-A(包装一个)组件,即装饰器有一个该组件的引用。装饰者共同实现的接口或抽象类。装饰者能扩展Component的状态。ConcreteDecorator有一个包装的组件的引用。ConcreteComponent是将要动态添加新行为的对象,它扩展自Component。装饰者能添加新的方法。Component每个组件可以单独使用,或者被装饰者包装起来使用。装饰者模式类图19BeverageCondimentDecoratorHouseBlendDarkRoastEspressoDecafMilkMochaSoyWhipdescriptioncost()cost()cost()cost()getDescription()cost()//其它方法getDescription()BeveragebeverageBeveragebeverageBeveragebeverageBeveragebeveragecost()getDescription()cost()getDescription()cost()getDescription()cost()getDescription()四个具体组件,每一个代表一种咖啡类型。Beverage相当于抽象的Component类。调料(装饰者)。它们除必须实现cost()外,还必须实现getDescription()。Component装饰我们的饮料20写星巴兹(Strbuzz)代码我们从Beverage类开始,它与原设计是一样的:publicabstractclassBeverage{Stringdescription=UnknownBeverage;publicStringgetDescription(){returndescription;}publicabstractdoublecost();}21实现调料(装饰者)抽象类publicabstractclassCondimentDecoratorextendsBeverage{publicabstractStringgetDescription();}22对饮料编码publicclassEspressoextendsBeverage{//蒸馏咖啡publicEspresso(){description=Espresso;}publicdoublecost(){return1.99;}}23对饮料编码(续)publicclassHouseBlendextendsBeverage{//家庭混合咖啡publicHouseBlend(){description=HouseBlendCoffee;}publicdoublecost(){return.89;}}24实现调料(装饰者)具体类publicclassMochaextendsCondimentDecorator{//摩卡Beveragebeverage;publicMocha(Beveragebeverage){this.beverage=beverage;}publicStringgetDescription(){returnbeverage.getDescription()+,Mocha;}publicdoublecost(){return.20+beverage.cost();}}描述不只是饮料,还要包括添加的调料。25订购星巴兹(Starbuzz)咖啡的测试代码publicclassStarbuzzCoffee{publicstaticvoidmain(Stringargs[]){Beveragebeverage=newEspresso();//蒸馏咖啡System.out.println
本文标题:第8章装饰者模式
链接地址:https://www.777doc.com/doc-6897158 .html