学习笔记之装饰者模式

来源:互联网 发布:mindnode有windows版么 编辑:程序博客网 时间:2024/06/11 14:18

学习笔记之装饰者模式

在讨论装饰者模式之前我们先来说一下继承和组合

尽管继承威力巨大,但是它并不是总能实现最有弹性最好维护的设计,但是通过组合和委托可以在运行时具有继承行为的效果。利用继承设计子类的行为,是在编译时静态决定的,而且所有的子类都会继承到相同的行为。然而,如果能够利用组合的做法扩展对象的行为,就可以在运行时动态的进行扩展。通过动态的组合对象,可以写新的代码添加新的功能,而无需修改现有的代码。既然没有改变现有的代码,那么引起bug和意外副作用的机会将大幅减少。

所以代码应该如同晚霞中的莲花一样地关闭(免于改变),如同晨曦中的莲花一样开放(能够扩展)这也引出了一个开放-关闭设计原则:对扩展开放对修改关闭。

接下来认识装饰者模式

首先讨论一个例子,星巴克咖啡

 



如果按照这个类图来写的话,真是类爆炸,这不仅违反了面向接口编程而不面向实现编程的原则,还违反了多用组合少用继承的原则。真是事倍功半,超级麻烦并且代码也是超级的烂,保证你写完绝对不会再去想看第二眼,简直是一个维护噩梦,但是想一想还有没有更好的方法,如果你了解了观察者模式,简直是所有的问题都解决了

我们可以以饮料为主题,然后在运行时以调料来“装饰”饮料。

下面展示几张图片你就理解了

 



从图上我们可以得到

1. 装饰者和被装饰者具有相同的类型

2. 你可以用一个或多个装饰者包装一个对象

3. 既然装饰者和被装饰者拥有相同的超类型,所以在任何需要原始对象的的场合,都可以用装饰过的对象代替

4. 装饰者可以在所委托被装饰者的行为之前与/或之后,加上自己的行为,已达到特定的目的

5. 对象可以在任何时候被装饰,所以可以运行时动态地。不限量地用你喜欢的装饰者来装饰对象

定义装饰者模式

装饰者模式动态的将责任附加到对象上,若要扩展功能,装饰者提供了比继承更有弹性的替代方案。

装饰者模式的类图

 

让星巴克咖啡也符合装饰者模式的类图

 

此处插入代码

 

package decorate;public abstract class Beverage {public enum Size{TALL,GRANDE,VENTI};Size size=Size.TALL;String description="unkonow";public String getDiscription(){return description;}public void setSize(Size size){this.size=size;}public Size getSize(){return this.size;}public abstract double cost();}package decorate;/*为了节约时间此处我只写了一种饮料类型*/public class HouseBlend extends Beverage {//通过构造函数来传递参数public HouseBlend() {description="HouseBlend";}@Overridepublic double cost() {return 0.89;}}package decorate;public abstract class CondimentDecorator extends Beverage {public Beverage beverage;public abstract String getDiscription();public Size getSize(){return beverage.getSize();}}package decorate;public class Milk extends CondimentDecorator {Beverage beverage;//为了把装饰者记录到实例变量中,通过把饮料当做构造器的参数,再由构造器将此饮料记录到实例变量中。public Milk(Beverage beverage) {this.beverage=beverage;}public String getDiscription(){return beverage.getDiscription()+",Milk";}@Overridepublic double cost() {return beverage.cost()+0.20;}}package decorate;public class Mocha extends CondimentDecorator{Beverage beverage;public Mocha(Beverage beverage) {this.beverage=beverage;}@Overridepublic String getDiscription() {return beverage.getDiscription()+",Mocha";}@Overridepublic double cost() {return beverage.cost()+0.20;}}package decorate;public class Soy extends CondimentDecorator {Beverage beverage;public Soy(Beverage beverage) {this.beverage=beverage;}@Overridepublic String getDiscription() {return beverage.getDiscription()+",Soy";}@Overridepublic double cost() {double cost = beverage.cost();if (beverage.getSize() == Size.TALL) {cost += 0.10;} else if (beverage.getSize() == Size.GRANDE) {cost += 0.15;} else if (beverage.getSize() == Size.VENTI) {cost += 0.20;}return cost;}}package decorate;public class Whip extends CondimentDecorator {Beverage beverage;public Whip(Beverage beverage) {this.beverage=beverage;}@Overridepublic String getDiscription() {return beverage.getDiscription()+",Whip";}@Overridepublic double cost() {return beverage.cost()+0.10;}}package decorate;import decorate.Beverage.Size;public class Test {public static void main(String[] args) {Beverage beverage=new HouseBlend();System.out.println(beverage.getDiscription()+"$"+beverage.cost());Beverage beverage2 = new HouseBlend();beverage2 = new Mocha(beverage2);beverage2 = new Mocha(beverage2);beverage2 = new Whip(beverage2);System.out.println(beverage2.getDiscription() + " $" + beverage2.cost());Beverage beverage3=new HouseBlend();beverage3.setSize(Size.VENTI);beverage3=new Soy(beverage3);beverage3=new Mocha(beverage3);beverage3=new Whip(beverage3);System.out.println(beverage3.getDiscription()+beverage3.cost());}}

 

装饰者模式总结:

装饰者模式有能力为设计注入弹性,但也有“黑暗面”,有时候会再说设计中注入大量的小类,这偶尔导致别人不容易了解装饰者模式,有时候人们在客户代码中依赖某种特定类型,然后忽然导入装饰者,却又没有周详的考虑一切,不过通常可以透明的插入装饰者,客户程序不需要知道它在和装饰者打交道。还有就是,在采用装饰者实例化组件时,将增加代码的复杂度。一旦使用装饰者模式,不只需要实例化组件,还要把此组件包装进装饰者中。不过装饰者模式依然是一个很好的模式,适合用来建立有弹性的设计,维持开放-关闭原则。

原创粉丝点击