装饰者模式
概念
1、装饰者模式 动态地将责任附加到对象上,若要扩展功能,装饰者提供了比继承更有弹性地替代方案。
2、装饰者和被装饰对象有相同的超类型
3、装饰者可以在所委托被装饰者的行为之前与/或之后,加上自己的行为,以达到特定的目的。
4、对象可以在任何时候被装饰,所以可以在运行时动态地、不限量地用你喜欢的装饰者来装饰对象。
场景
咖啡店有多种咖啡,如深焙咖啡等,每种咖啡还可以加入任意种调料,例如牛奶(milk)、豆浆(Soy)、摩卡(Mocha)、奶泡等等。
如果顾客想要摩卡和咖啡,那么我们要做的就是:
1、拿一个深焙咖啡对象
2、以摩卡对象装饰它
3、以奶泡对象装饰它
4、调用cost()方法,并依赖委托将调料的价钱加上去
类图
代码
抽象类
饮料类
//饮料类
public abstract class Beverage {
String description="Unknown Beverage";
public String getDescription(){
return description;
}
public abstract double cost();
}
调料类
//调料类
public abstract class Condiment extends Beverage{
public abstract String getDescription();
}
具体实现
饮料类的具体实现:深焙咖啡
public class DarkRoast extends Beverage {
public DarkRoast(){
this.description="DarkRoast";
}
@Override
public double cost() {
return 1.88;
}
}
调料类的具体实现:摩卡
public class Mocha extends Condiment {
Beverage beverage;
public Mocha(Beverage beverage){
this.beverage=beverage;
}
@Override
public String getDescription() {
return beverage.getDescription()+", Mocha";
}
@Override
public double cost() {
return 0.20+beverage.cost();
}
}
调料类的具体实现:奶泡
public class Whip extends Condiment {
Beverage beverage;
public Whip(Beverage beverage){
this.beverage=beverage;
}
@Override
public String getDescription() {
return beverage.getDescription()+",Whip";
}
@Override
public double cost() {
return 0.4+beverage.cost();
}
}
测试
public static void main(String[] args) {
/* Beverage beverage=new Espresso();
System.out.println(beverage.getDescription()+"$"+beverage.cost());
Beverage beverage1=new HouseBlend();
beverage1=new Mocha(beverage1);
beverage1=new Mocha(beverage1);
beverage1=new Whip(beverage1);
System.out.println(beverage1.getDescription()+"$"+beverage1.cost());*/
Beverage beverage2=new DarkRoast() ;
beverage2=new Mocha(beverage2);
beverage2=new Whip(beverage2);
System.out.println(beverage2.getDescription()+",$"+beverage2.cost());
}
结果
理解
上述代码的逻辑大致如下,通过递归的方式,在被装饰者外面,层层包装,在每一层添加当前层的特有功能。
这样就可以任意组合,而不需要为每种组合都去独立创建一个类。
Java中用到装饰者的地方–IO包
实现一个功能:把读取的所有字符都转成小写
public class LowerCaseInputStream extends FilterInputStream {
protected LowerCaseInputStream(InputStream in) {
super(in);
}
public int read() throws IOException{
int c=super.read();
return (c==-1 ? c:Character.toLowerCase((char) c));
}
public int read(byte[] b,int offset, int len)throws IOException
{
int result=super.read(b,offset,len);
for (int i=offset;i<offset+result;i++){
b[i]=(byte) Character.toLowerCase((char)b[i]);
}
return result;
}
}
public class InputTest {
public static void main(String[] args) throws IOException {
int c;
InputStream in=new LowerCaseInputStream(new BufferedInputStream(new FileInputStream("test.txt")));
while ((c=in.read())>=0){
System.out.print((char)c);
}
in.close();
}
}