ps:本文主要来源
给爱用继承的人一个全新的设计眼界.(可以在不修改底层代码的情况下给你的或者别人的对象赋予新的职责)
假设有个咖啡店,店里卖各种饮料。那么初始都有如下设计
抽象类 Beverage(饮料), Beverage类有个方法 cost(),返回饮料的价格,还有个属性 description,用来描述饮料。
那么所有饮料子类都必须继承Beverage,实现cost()方法.
当你购买饮料时,他们根据你购买的饮料的,通过cost()方法就能知道价格。
这样做在只要几种饮料时不会有什么问题,当要求把现有饮料加摩卡,加奶茶,或者加别的调料时,我们也新建一个类,那么类数量就很多,比如摩卡绿茶,奶茶绿茶,摩卡奶茶绿茶等类,当奶茶价格上涨,我们就必须修改与奶茶相关的饮料的cost(),当新上一种调料时,我们就要加好多新类。
这时有人说可以用实例变量与继承来做,不用设计这么多类。
现在Beverage类的cost()方法不再是抽象方法,我们提供cost()实现,让它计算加入各种调料价格。子类仍然覆盖cost(),但是会调用父类的cost(),计算基本饮料加上调料的价格。
这样做仍有不足,1.当出现新的调料,我们就要加新的方法,并改变父类的cost()方法,2.有些子类不需要父类的调料,比如Tea子类不需要whip(奶泡),也不需要hasWhip().
ps:利用继承设计子类的行为,在编译时静态决定的,并且所有子类都会继承相同行为。然而,如果能用组合的做法扩展对象的行为,那么就可以在运行时动态的进行扩展。
类应该对扩展开放,对修改关闭。(但也不必每个地方都采用这个原则,这样会导致代码复杂难以理解)
装饰者模式特点:
装饰者和被装饰对象有相同超类型,可以用一个或者多个装饰者包装一个对象。
ps:装饰者可以在所委托被装饰者的行为之前或者之后加上自己的行为,以达到特定目的。
下面通过具体代码来解决上面的问题。
//这是饮料抽象类
public abstract class Beverage{
String description = "unknowm beverage";
public String getDescription(){
return description;
}
}
//这是调料抽象类,也就是装饰者
public abstract class CondimenDecorator extends Beverage{
public abstract String getDescription();//都必须重新实现改方法
}
//浓缩咖啡饮料
public class Espresso extends Beverage{
public Espresso(){
descriptioin = "espresso";
}
public double cost(){
return 1.99;
}
}
//其它饮料类也是类似,就不写了。
//这是调料Mocha,它是一个具体装饰者 ,(注意:Mocha类也是Beverage的子类)
public class Mocha extends ConditionDecorator(){
Beverage beverage;//用一个实例变量来记录饮料,也就是被装饰者
public Mocha(Beverage beverage){
this.beverage = beverage;//想办法让被装饰者被记录到实例变量中,这里是通过把饮料当做构造器的参数,由构造器将此饮料传到实例变量
}
public String getDescription(){
return beverage.getDecription()+",mocha";
}
public double cost(){
return 20+beverage.cost();//这里20元是mocha价格。首先把调用委托给被装饰的对象(不一定只是饮料,可能是调料),以计算价格,再加上mocha价格。
}
}
//测试类
public class StarbuzzCoffee{
public static void main(String args[]){
Beverage beverage = new Espressp();
System.out.println(beverage.getDescription()+" $"+ beverage.getCost());//打印饮料价格。没加任何调料
beverage = new Mocha(beverage);//用Mocha装饰它,就是加了mocha调料。
System.out.println(beverage.getDescription()+" $"+ beverage.getCost());//打印饮料价格。加mocha调料
beverage = new Mocha(beverage);//用Mocha装饰它,就是再加一份mocha调料。
System.out.println(beverage.getDescription()+" $"+ beverage.getCost());//打印饮料价格。两份mocha调料
beverage = new Whip(beverage);//用Whip装饰它,就是再加一份whip调料。
System.out.println(beverage.getDescription()+" $"+ beverage.getCost());//打印饮料价格。两份mocha调料+whip
Beverage beverage2 = new Whip(new Mocha(new Mocha((new Espressp())));
//者两份饮料时一样的
}
}
jdk java.io相关类就是使用了装饰者模式
装饰者模式缺点就是:产生大量的小类,可能导致使用此api程序员困扰。