设计模式3-装饰者模式

0. 序

装饰模式–> 使用对象组合的方式,在运行时装饰类。 可以在不修改任何底层代码的情况下,给对象赋予新的职责

1. 咖啡店实例

​ 问题所在:一个饮料抽象父类Beverage , 当子类过多时,出现类的爆炸性复杂

在这里插入图片描述
​ 很明显,这是难以维护的,出现一个新的饮料就要去继承父类。

​ 一种特例:双倍奶泡咖啡,算一种饮料,三倍奶泡咖啡,… 无尽种类的饮料出现了。

​ 违反了两个原则???

  • 面向实现编程
  • 变化的部分没有抽离出,如cost()方法是变化的

原则:开发-闭合(免于改变 能够扩展)

目标:允许类容易扩展,在不修改现有代码的情况下,就可搭配新的行为。

​ OK, 这里针对上述饮料的继承问题:类数量爆炸,设计死板,难以维护,且基类的功能不适用所有子类。

​ 装饰模式如何装饰?假设顾客要摩卡和奶泡深培咖啡,以饮料为主体,使用调料来装饰:

  • 拿一个深培咖啡对象DarkRoast(继承自Beverage)

  • 以摩卡对象装饰Mocha

  • 以奶泡装饰Whip

  • 调用cost()方法,应依赖委托(delegate)将调料价格加上去

    我的疑问:如何去装饰?如何去委托?什么是委托?

    装饰模式我只记得JDK中的输入输出流是装饰模式下的。但是是如何去实现的?理解好理解:就是一层一层的包裹,但实现是什么情况?以及我最后在咖啡这个装饰下如何去计算总价???

在这里插入图片描述
​ 这里的例子是外层的cost依赖里面的cost的结果。

**显然:**装饰者可以在被委托的装饰者的行为之前与之后,加上自己的行为,达到特定的目的。–>如何去实现???

装饰者与被装饰者超类相同。–> 要使装饰者能取代被装饰者,多态。

2. 定义装饰者模式

​ 装饰者模式动态把职责附加到对象上。在扩展上,比继承更有弹性。

在这里插入图片描述

针对caffe实例的观察者类图:

在这里插入图片描述

显然:要把饮料当成被装饰者,调料当初装饰者。

Ques:既然面向接口编程,为什么不把beverage设计成接口,而要当作一个抽象类?

Ans:(书上的解答)从最初的程序开始,beverage就是一个抽象类,通常装饰者模式是采用抽象类,也可以使用接口。但是,**避免去修改现有的代码。**毕竟现有的代码一般问题少,修改后可能会产生新的问题。。。

3. 代码

这章节理论上理解是好理解,但之前没有实现过 我觉得实现还是有点难,故写一下代码。

/**
 * 抽象基类
 */
public abstract class Beverage {
    protected String description="Unknown Beverage";//protected是子类可以访问 无 是 默认情况 同一个包下的类可以访问

    public String getDescription() {
        return description;
    }

    public abstract double cost();
}

被装饰者

/**
 * 浓缩咖啡
 */
public class Espresso   extends Beverage {

    public Espresso() {
        description="Espresso--浓缩咖啡";
    }

    @Override
    public double cost() {
        return 1.99;
    }
}
/**
 * 混合--黑咖啡
 */
public class HouseBlend extends Beverage {
    @Override
    public double cost() {
        return 0.89;
    }

    public HouseBlend() {
        description="house blend coffee";
    }
}

装饰者–抽象出一个基类

/**
 * 装饰类 --> 调料
 */
public abstract class Condiment extends Beverage {
    public abstract String getDescription();

    public double cost() {
        return 0;
    }
}

装饰者

/**
 * 装饰者 摩卡
 */
public class Mocha extends Condiment {
    Beverage beverage;

    public Mocha(Beverage beverage) {
        this.beverage = beverage;
    }

    @Override
    public String getDescription() {
        return beverage.getDescription()+" , Mocha";
    }
    public double cost(){
        return 0.20+beverage.cost(); //价格计算从内到外
    }
}
//其他装饰者类似 

实现starbuzzcoffee

public class StarbuzzCoffee {
    public static void main(String[] args) {
        Beverage beverage=new Espresso();//浓缩咖啡 被装饰对象
        System.out.println(beverage.getDescription()+" "+beverage.cost()); //Espresso--浓缩咖啡 1.99

        //开始装饰
        beverage=new Mocha(beverage);//加上Mocha
        System.out.println(beverage.getDescription()+" "+beverage.cost());//Espresso--浓缩咖啡 , Mocha 2.19

        beverage=new Sony(beverage);
        System.out.println(beverage.getDescription()+" "+beverage.cost());//Espresso--浓缩咖啡 , Mocha , Sony 2.49

        beverage=new Whip(beverage);
        System.out.println(beverage.getDescription()+" "+beverage.cost());//Espresso--浓缩咖啡 , Mocha , Sony , Whip 2.9

        //双倍Mocha
        beverage=new Mocha(beverage);
        System.out.println(beverage.getDescription()+" "+beverage.cost());//Espresso--浓缩咖啡 , Mocha , Sony , Whip , Mocha 3.1
    }
}

实现小杯,中杯,大杯

public  abstract class CupSize extends Beverage {

    public  abstract String getDescription() ;
    @Override
    public double cost() {
        return 0;
    }
}
public class SmallCup   extends CupSize {
    Beverage beverage;

    @Override
    public String getDescription() {
        return beverage.getDescription()+" , 小杯 多收0.1";
    }

    @Override
    public double cost() {
        return beverage.cost()+0.1;
    }

    public SmallCup(Beverage beverage) {
        this.beverage = beverage;
    }
}

这个例子相当于是把杯子大小当成一种配料,但问题很明显:有三个类。书上的例子是在超类Beverage中加入size属性,然后只用一个SizeDe类来装饰,在里面判断size==small|mid|big来增加价格。

上述问题剩下:如何去委托?什么是委托?

我的理解:就如计算caffee价格,实际上 咖啡被配料委托,咖啡的价格最终是依赖配料的价格计算的。将caffee委托给配料,在配料(即装饰者)中加入caffee(组合)。这样就通过cost方法一层层调用得到价格。

发布了9 篇原创文章 · 获赞 4 · 访问量 4247

猜你喜欢

转载自blog.csdn.net/qq_42239081/article/details/104869055
今日推荐