装饰者模式

来源:互联网 发布:淘宝连帽 编辑:程序博客网 时间:2024/05/16 19:40

前言:我曾经以为男子汉应该用继承去处理一切,后来我领教到运行时扩展,远比编译时期的继承威力大的多。

装饰模式是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。

编辑
以下情况会使用到Decorator模式
1. 需要扩展一个类的功能,或给一个类添加附加职责。
2. 需要动态的给一个对象添加功能,这些功能可以再动态的撤销。
3. 需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变的不现实。
4. 当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。
下面进入实战,现在需求是一家果汁店有各种各样的果汁,eg:橙汁、柠檬汁... 还有各种附加调料,eg:加冰、加糖、加热(不同的调料有不同的费用),然后根据相应的果汁和调料计算总价格。
我们可能会更先想到 让我们的果汁 去继承一个父类Beverage,把各种调料当作成员变量,需要什么调料,把boolean置为true就好了,然后再cost()方法中去计算价格。 很显然这样做会产生“类爆炸”,并且,也为自己制造了维护噩梦,如果果汁的价格和调料的价格上涨了怎么办?
想到我之前所说的设计原则,对扩展开放,对修改关闭。
我们把饮料父类提取出来 
public abstract class Beverage {public String description = "Unknown Beverage";public String getDescription(){return description;}public abstract double cost();}
装饰者超类也提取出来,被装饰者和装饰者拥有共同的父类
public abstract class CondimentDecorator extends Beverage{public abstract String getDescription();//所有的调料装饰者都必须重新实现getDescription()方法。}
我们把饮料和调料分离
下面创建两种饮料 橙汁 和柠檬汁(被装饰者)
public class Orange extends Beverage {public Orange(){  //为了设置饮料的描述,我们写了一个构造,description继承自Beveragethis.description = "橙汁"; }public double cost() {return 2.00;}}public class Lemon extends Beverage{public Lemon(){this.description = "柠檬汁"; }public double cost() {return 3.00;}}

创建2种调料 加冰、加糖(装饰者)
/** * @author IT小蔡 *  冰是一个装饰者,所以让它扩展自CondimentDecorator */public class Ice extends CondimentDecorator {/** * 用一个实例变量记录饮料,也就是被装饰者。 */Beverage beverage;/** * @param beverage *            想办法让被装饰者(饮料)被记录到实例变量中。这里的做法是:把饮料当作构造器的参数,再由构造器将此饮料记录在实例变量中。 */public Ice(Beverage beverage) {this.beverage = beverage;}/* * (non-Javadoc) 叙述的不光是饮料(如橙汁),而是完整的把调料也描述出来 */public String getDescription() {return beverage.getDescription() + " ,加冰";}/* * (non-Javadoc) *  * @see cai.yu.abstrac.Beverage#cost() 计算出加冰的饮料的价钱。 */public double cost() {double cost = beverage.cost() + 0.20;return cost;}}public class Suger extends CondimentDecorator{Beverage beverage;public Suger(Beverage beverage){this.beverage = beverage;}public String getDescription() {return beverage.getDescription()+" ,加糖";}public double cost() {double cost = beverage.cost() + 0.30;return cost;}}

Test类
public class Test {/** * @param args */public static void main(String[] args) {Beverage beverage1 = new Lemon(); System.out.println(beverage1.getDescription()+" $"+beverage1.cost());//先订一杯柠檬汁,不加调料,打印出描述与价格Beverage beverage2 = new Orange(); //构造出一杯橙汁,什么都不加beverage2 = new Ice(beverage2); //用冰去装饰beverage2 = new Suger(beverage2); // 用糖去装饰System.out.println(beverage2.getDescription()+" $"+beverage2.cost());//订一杯加冰加糖的橙汁,打印出描述与价格}}

运行结果






如果现在又有了新需求,要把每一种饮料分为 大、中、小 杯。分别加 0.2, 0.15, 0.10元。又该怎么实现?
为了尽量不去修改源代码,我想到的是 把杯子大小也当作一种装饰者,把杯子size,作为Beverage的成员变量。
public abstract class Beverage {public String description = "Unknown Beverage";public static final String TALL = "小杯";public static final String GRANDE = "中杯";public static final String VENTI = "大杯";public String size; //默认是小杯的。public String getDescription(){return description;}public abstract double cost();public String getSize(){return size;}public void setSize(String size){this.size = size;}}

杯子尺寸装饰者 Capacity
/** * @author IT小蔡 * 把杯子也当成一种装饰者 */public class Capacity extends CondimentDecorator{Beverage beverage;/** * @param beverage 被装饰的饮料 * @param size 杯子尺寸 */public Capacity(Beverage beverage,String size){this.beverage = beverage;this.size = size;}@Overridepublic String getDescription() {return beverage.getDescription()+","+size;}@Overridepublic double cost() {double cost = beverage.cost();switch (size) {case Beverage.TALL:cost+=0.10;break;case Beverage.GRANDE:cost+=0.15;break;case Beverage.VENTI:cost+=0.20;break;default:break;}return cost;}

Test类
public class Test {/** * @param args */public static void main(String[] args) {Beverage beverage1 = new Lemon(); System.out.println(beverage1.getDescription()+" $"+beverage1.cost());//先订一杯柠檬汁,不加调料,打印出描述与价格Beverage beverage2 = new Orange(); //构造出一杯橙汁,什么都不加beverage2 = new Ice(beverage2); //用冰去装饰beverage2 = new Suger(beverage2); // 用糖去装饰System.out.println(beverage2.getDescription()+" $"+beverage2.cost());//订一杯加冰加糖的橙汁,打印出描述与价格beverage2 = new Capacity(beverage2,Beverage.VENTI); //来一杯大杯加冰加糖的橙汁。System.out.println(beverage2.getDescription()+" $"+beverage2.cost());//先订一杯柠檬汁,不加调料,打印出描述与价格}}
运行结果


java里面典型的装饰者应用,在 IO包里面,有兴趣的同学可以看看,
继承属于扩展形式之一,但不见得是达到弹性设计的最佳方式,在我们的设计中,应该允许行为被扩展,而无需修改现有的代码,装饰者模式意味着一群装饰类,这些装饰类用来包装具体组件。你可以用无数个装饰者去包装一个组件,但装饰者会导致设计中出现许多小对象,如果过度使用,会让程序变得复杂。




1 0
原创粉丝点击