装饰器模式:允许向现有的一个类增加(增强)其新功能,但是却不改变其结构,比继承会更灵活!
解决的问题:有时候为了扩展一个类,用继承会产生子类膨胀的问题。
关键代码:Component类充当抽象类(可以是接口);装饰类引用和继承Component类;集体扩展类重写父类方法;
可以增强或扩展原有的方法,如果在装饰类中添加方法,则后面只能强转成具体实现类,才能调用,因为装饰器定义字段类型为Component类型,所以调用方法必须在Component定义或声明过;
顺便说一句:接口中的字段默认是static final类型的
使用装饰器类典型的特征就是java中I/O类,放在后面说
下面举一个例子:
一家咖啡店有基础咖啡:Espreeso(浓缩咖啡)、HouseBlend(混合咖啡),为了适合不同人的口味,自由添加Macho(摩卡)、Soy(豆浆)、奶泡(whip)如果使用继承,就拿Espresso来说,就有至少2的3次方种(不算加双份的),即8个子类(包含都不添加的类),所以这种继承很容易出现子类膨胀的问题;
但是使用装饰器模式:
共同继承的超类(抽象类和接口)Component类
package com.pattern.decorator;
//在java中也可以直接使用接口 作为超类
public abstract class Beverage {
String description="unknow beverage";
public String getDescription(){
return description;
}
public abstract double cost();
}
需要被扩展的基础咖啡类
//所有的基础咖啡都已经且必须实现抽象类Beverage并实现方法。
package com.pattern.decorator;
public class Espresso extends Beverage {
public Espresso() {
description="Espression";
// TODO Auto-generated constructor stub
}
@Override
public double cost() {
// TODO Auto-generated method stub
return 1.99;
}
}
package com.pattern.decorator;
public class HouseBlend extends Beverage {
public HouseBlend() {
description="HouseBlend";
// TODO Auto-generated constructor stub
}
@Override
public double cost() {
// TODO Auto-generated method stub
return 0.89;
}
}
装饰者类 包含被扩展类的抽象类型引用和继承抽象类型component
package com.pattern.decorator;
//添加调料的抽象类,具体扩展类都要继承该类并实现方法
public abstract class CondimentDecorator extends Beverage {
@Override
public double cost() {
// TODO Auto-generated method stub
return 0;
}
//抽象类继承抽象类,可以添加新的方法,有些方法可以不写,但是默认继承,其子类会被要求实现
public abstract String getDescription();
}
具体扩展类,继承并实现装饰者抽象类
package com.pattern.decorator;
public class Macho extends CondimentDecorator {
Beverage beverage;
public Macho(Beverage beverage){
this.beverage=beverage;
}
@Override
public String getDescription() {
// TODO Auto-generated method stub
return beverage.getDescription()+",macho";//实现中必须调用方法,不能直接调用属性
}
@Override
public double cost() {
// TODO Auto-generated method stub
return beverage.cost()+.20;
}
}
package com.pattern.decorator;
public class Soy extends CondimentDecorator {
Beverage beverage;
public Soy(Beverage beverage){
this.beverage=beverage;
}
@Override
public double cost() {
// TODO Auto-generated method stub
return beverage.cost()+.10;
}
@Override
public String getDescription() {
// TODO Auto-generated method stub
return beverage.getDescription()+",soy";
}
}
package com.pattern.decorator;
public class Whip extends CondimentDecorator {
Beverage beverage;
public Whip(Beverage beverage){
this.beverage=beverage;
}
@Override
public String getDescription() {
// TODO Auto-generated method stub
return beverage.getDescription()+",whip";
}
@Override
public double cost() {
// TODO Auto-generated method stub
return beverage.cost()+.30;
}
public void bubbling(){
}
public void bubbing() {
// TODO Auto-generated method stub
System.out.println("whip oO0");
}
}
测试demon
package com.pattern.decorator;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
public class Demo {
public static void main(String[] args) throws Exception {
Beverage beverage=new Espresso();
beverage=new Macho(beverage);
beverage.getDescription();
beverage=new Soy(beverage);
beverage=new Whip(beverage);
beverage=new Whip(beverage);
System.out.println(beverage.getDescription()+" $"+beverage.cost());
((Whip)beverage).bubbing();
File file=new File("D:\\source.txt");
FileInputStream fis=new FileInputStream(file);
BufferedInputStream bis=new BufferedInputStream(fis);
BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream(new File("D:\\dest.txt")));
byte[] b=new byte[1024];
int len=0;
while((len=bis.read(b))!=-1){
bos.write(b, 0, len);
}
bis.close();
bos.close();
}
}
运行结果:
Espression,macho,soy,whip,whip $2.8899999999999997
whip oO0
说明:从上面可以看出,需要构建扩展类的实例对象的时候,需要传入已经有的基础咖啡类,由于装饰器中字段类型是超类Beverage,因此能调用的方法必须是超类包含的(比如调用子类独有的Bubbing()则需要强转,所以一般不会这么用,可以把这个方法在超类一已有的方法中调用它(把它私有化),有点像增强原有的方法)。
典型的例子就是I/O模型:
FilterInputStream(装饰器类:字段值为InputStrean in)子类扩展类中的BufferedInputStream中read等速度比较快,它是区别于不同的读取每次读取都是从磁盘读取,而它维护一个较大数组缓存,一次读取很多,然后read时是从缓存数组中读取,减少读取磁盘的操作。