设计模式-装饰器模式(6)

装饰器模式


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());
    }
}

上述优点:

  1. 类没有爆炸
  2. 可以扩展饮料类型,如Tea,符合开闭原则

缺点

  1. 添加调料的时候需要修改源代码,违反开闭原则

修改成下面,下面代码则是装饰器模式

// 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中的流就用了装饰器模式

2. 缺点

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值