咖啡存在许多的种类,同时也有不同的调料。此时用户可以单点咖啡,也可以点咖啡+调料,请计算费用(这里咖啡和调料都属于
Drink
的一类)
每出现一种组合就实现一个类,但是每次增加一个咖啡种类或者一个新的调料,类的数量就会倍增,出现类爆炸!
具体修改如下:
- 在基类中加上每个调料的实例变量,并且将其设置为
boolean
类型表示是否加入- 基类的
cost
方法不再是抽象方法,而是计算加入所有调料后的价格(为什么是计算所有调料???),子类会调用基类的cost
方法hasxxx
和setxxx
方法由于取得和设置调料的bool
值这样大大减少了类的数量,但是该方案会出现以下问题:
- 调整价钱就要修改代码
- 增加或者删除调料种类时,就要加上新方法并且修改基类的
cost
方法- 某些新饮料(子类)并不需要某些调料,但是仍要继承这些不需要的方法,比如
hashSoy
等
public class Drink{String desc;// mikeCost、soyCost的实例变量// mike、soy的getter和setter方法public double cost(){float cost = 0.0;if(hasMilk()){cost += milkCost;}if(hasSoy()){cost += soyCost;}return cost;}
}
public class DarkRoast extends Drink{public DarkRoast(){desc = "好喝";}public double cost(){return 2.0 + super.cost();}
}
假设需要一杯摩卡(
Mocha
)+奶泡(whip
)的深焙咖啡(DarkRoast
),具体做法和计算价格的过程如下:
- 拿到一个
DarkRoast
对象- 使用
Mocha
对象装饰它- 使用
Whip
对象装饰它- 调用
cost
方法,并依赖委托将调料价钱加上
装饰者可以在被装饰者的行为前/后加上自己的行为(Whip
是装饰者,则Mocha
是被装饰者,其他依次类推)
装饰者模式可以动态将责任附加到对象上,即扩展功能时更具弹性
装饰者和被装饰者具有相同的父类(因为装饰者可能变为被装饰者,被装饰者也可能变为装饰者)
// 需要被继承的公共父类
public abstract class Drink{String desc = "Unknown";public String getDesc(){return desc;}public abstract double cost();
}// 抽象装饰者类(目的在于让装饰者类必须实现getDesc方法)
public abstract class CondimentDecorator extends Drink{public abstract String getDesc();
}// Espresso类,即被装饰者类
public class Soy extends Drink{public Soy(){desc = "Soy"}public double cost(){return 2.0;}
}// Mocha类,即装饰者类
public class Mochas extends CondimentDecorator{Drink drink; // 组合public Mochas(Drink drink){this.drink = drink;}public String getDesc(){return drink.getDesc() + ", Mocha";}public double cost(){// 1.0是Mocha自己的价格return 1.0 + drink.cost();}
}// 测试
public class CoffeeStore{public static void main(String[] args){// 点一杯加了豆浆的摩卡Drink soy = new Soy();Drink Mocha = new Mocha(espresso);}
}
在
Java I/O
类中,同样使用了装饰者模式,但是也是有缺点的,比如会出现大量的小类
编写一个装饰者,把输入流中的大写都转为小写
import java.io.*;
// FilterInputStream是抽象装饰者
public class LowerCaseInputStream extends FilterInputStream{public 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(itn 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;try{// 先用BufferdInputStream修饰FileInputStream// 再用LowerCaseInputStream修饰BufferdInputStreamInputStream in = new LowerCaseInputStream(new BufferdInputStream(new FileInputStream("test.txt")));while((c = in.read()) >= 0){System.out.print((char)c);}in.close();}catch(IOException e){e.printStackTrace();}}
}
Head First 设计模式-装饰者模式
下一篇:刘桥镇总工会开展春节送温暖活动