装饰器模式
1. 实例
- 业务场景:星巴克卖咖啡,一开始,只有四种咖啡:Decaf,Espresso,DrakRoast,HouseBlend。因为所有咖啡都有共性,所以可以继承自一个父类,Beverage。
// 0. 业务
// 星巴克卖咖啡,一开始,只有四种咖啡:Decaf,Espresso,DrakRoast,HouseBlend。
// 因为所有咖啡都有共性,所以可以继承自一个父类,Beverage(饮料)。
// 1. 咖啡父类
abstract class Beverage{
private String description;
// 父类有有参构造器了,所以没有默认的无参构造器,所以子类需要重写有参构造器,如果不重写
// 则子类默认调用自己的无参构造器,从而调用父类的无参构造器(super()),但是父类没有无参(被有参代替),所以子类报错。
public Beverage(String description) {
this.description = description;
}
public abstract double cost();
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
// 2. 咖啡子类
// 2.1 无咖啡因咖啡
class Decaf extends Beverage{
public Decaf() {
super("无咖啡因咖啡");
}
@Override
public double cost() {
return 1;
}
}
// 2.2 浓缩咖啡
class Espresso extends Beverage{
public Espresso() {
super("浓缩咖啡");
}
@Override
public double cost() {
return 2;
}
}
// 2.3 深度烘焙咖啡
class DrakRoast extends Beverage{
public DrakRoast() {
super("深度烘焙咖啡");
}
@Override
public double cost() {
return 3;
}
}
// 2.4 混合咖啡
class HouseBlend extends Beverage{
public HouseBlend() {
super("混合咖啡");
}
@Override
public double cost() {
return 4;
}
}
// ========================客户端=====================
public class Test02 {
public static void main(String[] args) {
Beverage b1 = new Decaf();
Beverage b2 = new Espresso();
Beverage b3 = new DrakRoast();
Beverage b4 = new HouseBlend();
System.out.println(b1.getDescription() + ": " + b1.cost());
System.out.println(b2.getDescription() + ": " + b2.cost());
System.out.println(b3.getDescription() + ": " + b3.cost());
System.out.println(b4.getDescription() + ": " + b4.cost());
}
}
上面代码,目前没有问题。
但是变化来了:
星巴克老板为了提升竞争力,想到一个新业务,给咖啡中加调料:牛奶,豆浆,摩卡,泡沫。
那么如何应对这种变化呢?
针对上面的问题,有下面解决方案:
为加牛奶的Decaf创建一个类 DecafWithMilk,为加牛奶的Expresso创建一个ExpressoWithMilk…
但是这会引起类的爆炸增长,因为每个类不止加牛奶,豆浆,可以任意组合!
上面方案不可以!
针对上面问题,我们可以在父类Beverage中,添加4个boolean属性,分别代表4种调料,然后修改父类cost方法,要加上调料的价格;描述也要修改,要加上调料描述
private boolean milk,soy,mocha,bubble;
public boolean isMilk() {
return milk;
}
public void setMilk(boolean milk) {
this.milk = milk;
}
// 客户端
Beverage b1 = new Decaf();
b1.setMilk(true);
b1.setSoy(true);
// 0. 业务
// 星巴克卖咖啡,一开始,只有四种咖啡:Decaf,Espresso,DrakRoast,HouseBlend。
// 因为所有咖啡都有共性,所以可以继承自一个父类,Beverage(饮料)。
// 1. 咖啡父类
abstract class Beverage{
private String description;
private boolean milk,soy,mocha,bubble;
// 父类有有参构造器了,所以没有默认的无参构造器,所以子类需要重写有参构造器,如果不重写
// 则子类默认调用自己的无参构造器,从而调用父类的无参构造器(super()),但是父类没有无参(被有参代替),所以子类报错。
public Beverage(String description) {
this.description = description;
}
public double cost(){
double total = 0;
if(milk){
total += 0.2;
}
if(soy){
total += 0.3;
}
if(mocha){
total += 0.4;
}
if(bubble){
total += 0.1;
}
return total;
}
public String getDescription() {
String str = description;
if(milk) str = str + " + milk";
if(soy) str = str + " + soy";
if(mocha) str = str + " + mocha";
if(bubble) str = str + " + bubble";
return str;
}
public void setDescription(String description) {
this.description = description;
}
public boolean isMilk() {
return milk;
}
public void setMilk(boolean milk) {
this.milk = milk;
}
public boolean isSoy() {
return soy;
}
public void setSoy(boolean soy) {
this.soy = soy;
}
public boolean isMocha() {
return mocha;
}
public void setMocha(boolean mocha) {
this.mocha = mocha;
}
public boolean isBubble() {
return bubble;
}
public void setBubble(boolean bubble) {
this.bubble = bubble;
}
}
// 2. 咖啡子类
// 2.1 无咖啡因咖啡
class Decaf extends Beverage{
public Decaf() {
super("无咖啡因咖啡");
}
@Override
public double cost() {
// 无咖啡因咖啡价格 + 调料价格
return 1 + super.cost();
}
}
// 2.2 浓缩咖啡
class Espresso extends Beverage{
public Espresso() {
super("浓缩咖啡");
}
@Override
public double cost() {
return 2+ super.cost();
}
}
// 2.3 深度烘焙咖啡
class DrakRoast extends Beverage{
public DrakRoast() {
super("深度烘焙咖啡");
}
@Override
public double cost() {
return 3 + super.cost();
}
}
// 2.4 混合咖啡
class HouseBlend extends Beverage{
public HouseBlend() {
super("混合咖啡");
}
@Override
public double cost() {
return 4 + super.cost();
}
}
// ========================客户端=====================
// 扩展饮料
class Tea extends Beverage{
public Tea(){
super("茶");
}
public double cost(){
return 2 + super.cost();
}
}
public class Test02 {
public static void main(String[] args) {
Beverage b1 = new Decaf();
b1.setMilk(true);
Beverage b2 = new Espresso();
Beverage b3 = new DrakRoast();
Beverage b4 = new HouseBlend();
System.out.println(b1.getDescription() + ": " + b1.cost());
System.out.println(b2.getDescription() + ": " + b2.cost());
System.out.println(b3.getDescription() + ": " + b3.cost());
System.out.println(b4.getDescription() + ": " + b4.cost());
}
}
上述优点:
- 类没有爆炸
- 可以扩展饮料类型,如Tea,符合开闭原则
缺点
- 添加调料的时候需要修改源代码,违反开闭原则
修改成下面,下面代码则是装饰器模式
// 0. 业务
// 星巴克卖咖啡,一开始,只有四种咖啡:Decaf,Espresso,DrakRoast,HouseBlend。
// 因为所有咖啡都有共性,所以可以继承自一个父类,Beverage(饮料)。
// 1. 咖啡父类
abstract class Beverage{
private String description;
// 父类有有参构造器了,所以没有默认的无参构造器,所以子类需要重写有参构造器,如果不重写
// 则子类默认调用自己的无参构造器,从而调用父类的无参构造器(super()),但是父类没有无参(被有参代替),所以子类报错。
public Beverage(String description) {
this.description = description;
}
public abstract double cost();
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
// 2. 咖啡子类
// 2.1 无咖啡因咖啡
class Decaf extends Beverage{
public Decaf() {
super("无咖啡因咖啡");
}
@Override
public double cost() {
// 无咖啡因咖啡价格 + 调料价格
return 1;
}
}
// 2.2 浓缩咖啡
class Espresso extends Beverage{
public Espresso() {
super("浓缩咖啡");
}
@Override
public double cost() {
return 2;
}
}
// 2.3 深度烘焙咖啡
class DrakRoast extends Beverage{
public DrakRoast() {
super("深度烘焙咖啡");
}
@Override
public double cost() {
return 3;
}
}
// 2.4 混合咖啡
class HouseBlend extends Beverage{
public HouseBlend() {
super("混合咖啡");
}
@Override
public double cost() {
return 4;
}
}
// 3. 调料父类
// 判断2个类能不能继承,看is a,还要满足里氏替换原则。此处调料不是饮料,但是为了实现装饰器模式,我们只能这样
// 继承满足条件is a和里氏替换原则,只是原则,不是规定,特定条件下可以违反。
abstract class Condiment extends Beverage{
protected Beverage beverage;
public Condiment(Beverage beverage) {
super("调料");
// 第一次的时候是饮料,后面传入的都是调料
// Beverage b1 = new Decaf();
// Beverage b2 = new Milk(b1); 此时b1是Decaf饮料
// Beverage b3 = new Soy(b2); 此时b2是Milk,并且此时Soy中继承自Condiment的字段beverage ==> Milk
// Beverage b4 = new Mocha(b3);
// Beverage b5 = new Bubble(b4);
this.beverage = beverage;
}
}
// 3.2 调料子类
class Milk extends Condiment{
// 父类有 protected Beverage beverage;
public Milk(Beverage beverage) {
super(beverage);
}
@Override
public double cost() {
// 饮料的价格+牛奶(调料)价格
return beverage.cost()+0.2;
}
@Override
public String getDescription() {
// 调用的传入饮料的描述
return beverage.getDescription()+" + 牛奶";
}
}
class Soy extends Condiment{
public Soy(Beverage beverage) {
super(beverage);
}
@Override
public double cost() {
// 饮料的价格+牛奶(调料)价格
return beverage.cost()+0.3;
}
@Override
public String getDescription() {
return beverage.getDescription()+" + 豆浆";
}
}
class Mocha extends Condiment{
public Mocha(Beverage beverage) {
super(beverage);
}
@Override
public double cost() {
// 饮料的价格+牛奶(调料)价格
return beverage.cost()+0.4;
}
@Override
public String getDescription() {
return beverage.getDescription()+" + 摩卡";
}
}
class Bubble extends Condiment{
public Bubble(Beverage beverage) {
super(beverage);
}
@Override
public double cost() {
// 饮料的价格+牛奶(调料)价格
return beverage.cost()+0.5;
}
@Override
public String getDescription() {
return beverage.getDescription()+" + 泡沫";
}
}
// ========================客户端=====================
// 扩展饮料和调料
class Tea extends Beverage{
public Tea(){
super("茶");
}
@Override
public double cost() {
return 2;
}
}
// 调料枸杞
class GouQi extends Condiment{
public GouQi(Beverage beverage) {
super(beverage);
}
@Override
public double cost() {
return beverage.cost()+1;
}
@Override
public String getDescription() {
return beverage.getDescription()+" + 枸杞";
}
}
public class Test02 {
public static void main(String[] args) {
Beverage b = new Decaf();
// 不要理解成调料里面加饮料,理解成混合在一起
Milk milk = new Milk(b);
Soy soy = new Soy(milk);
Mocha mocha = new Mocha(soy);
Bubble bubble = new Bubble(mocha);
System.out.println(bubble.getDescription()+" = "+bubble.cost());
// 这种写法方便理解,milk等调料继承自饮料
Beverage b1 = new Decaf();
Beverage b2 = new Milk(b1);
Beverage b3 = new Soy(b2);
Beverage b4 = new Mocha(b3);
Beverage b5 = new Bubble(b4);
// 还可以加2份泡沫
Beverage b6 = new Bubble(b5);
System.out.println(b5.getDescription()+" = "+b5.cost());
System.out.println(b6.getDescription()+" = "+b6.cost());
// 扩展的饮料和调料
Beverage b11 = new Tea();
Beverage b22 = new GouQi(b11);
Beverage b33 = new Milk(b22);
System.out.println(b33.getDescription()+ " = " + b33.cost());
}
}
无咖啡因咖啡 + 牛奶 + 豆浆 + 摩卡 + 泡沫 = 2.4
无咖啡因咖啡 + 牛奶 + 豆浆 + 摩卡 + 泡沫 = 2.4
无咖啡因咖啡 + 牛奶 + 豆浆 + 摩卡 + 泡沫 + 泡沫 = 2.9
茶 + 枸杞 + 牛奶 = 3.2
一直套娃罢了!!!!
装饰器模式核心:继承(每个调料类内部都有父类Condiment中的字段protected Beverage beverage,但是却会根据情况指向不同对象,但他们都是Beverage)+关联(Condiment中的字段protected Beverage beverage)
以上部分便是装饰器模式,其实jdk中的流就用了装饰器模式