前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >装饰模式--小美的生日蛋糕

装饰模式--小美的生日蛋糕

作者头像
zhanyd
发布2022-05-16 13:44:50
2730
发布2022-05-16 13:44:50
举报
文章被收录于专栏:编程我也会

生日蛋糕

过几天就是小美的生日了,小帅不敢怠慢,打算定一个奶油蛋糕,好好庆祝一下。

店里标准的奶油蛋糕上面就一层奶油,也没有其他的配料,看着不大气,万一小美不喜欢可就糟了。

小美喜欢吃水果,小帅就想在蛋糕上加点水果,就找老板商量:能不能多加几种水果,我可以另外加钱。

老板眉开眼笑:当然可以啊,我们这里有樱桃,草莓,火龙果你要加什么哦?

这是我们的价目表:

奶油蛋糕:200元 巧克力蛋糕:230元 樱桃:30元 草莓:15元 火龙果:10元

我们店有两种蛋糕,一种是奶油蛋糕,另一种是巧克力蛋糕,每种蛋糕都可以另外加水果哦。

正常套路

按照正常的套路,如果用代码实现买蛋糕的功能,我们一般会给每种蛋糕和水果组合设计一种类。

如果我们想要在奶油蛋糕上加樱桃就新建一个奶油蛋糕樱桃类(CreamCakeAndCherry);

如果奶油蛋糕上加草莓就新建一个奶油蛋糕草莓类(CreamCakeAndStrawberry);

如果巧克力蛋糕上加樱桃就新建一个巧克力蛋糕樱桃类(ChocolateCakeAndCherry)。

还有奶油蛋糕火龙果类,巧克力蛋糕草莓类,巧克力蛋糕火龙果类,如果蛋糕店又增加了一种千层蛋糕,还有千层蛋糕草莓类,千层蛋糕樱桃类。。。

如果再加几种水果。。。完蛋了,类已经爆炸了。。。

缺点

这么写有什么缺点呢?

  1. 最明显的是类太多了,看得眼花缭乱,增加了维护的成本。
  2. 一旦出现的新蛋糕或水果,又要增加一大堆类。
  3. 如果蛋糕和水果的价格改变了,要改很多很多类,严重违反了开闭原则:类应该对扩展开发,对修改封闭。

小帅作为一个有追求的程序员,当然不会满足这种做法,小帅想起了装饰模式,水果不是刚好装饰蛋糕的吗?

正好符合装饰模式的应用场景,赶紧试一下吧。

装饰模式救场

装饰模式(Decorator Pattern) :动态地给一个对象增加一些额外的职责(Responsibility),就增加对象功能来说,装饰模式比生成子类实现更为灵活。

装饰模式的类图如下:

Component: 抽象构件 ConcreteComponent: 具体构件 Decorator: 抽象装饰类 ConcreteDecorator: 具体装饰类

这里要注意一下Decorator装饰类,它维持了一个指向component对象的指针,接口方法的实现,都是调用这个component对象的方法实现的。

Decorator类本身只是调用了component对象已有的功能,这是装饰模式的精髓所在。

这么说有点不好理解,看一下下面的例子就能明白了。

蛋糕接口

代码语言:javascript
复制
public interface Cake {

    public String getDescription();

    public BigDecimal cost();
}

奶油蛋糕

代码语言:javascript
复制
public class CreamCake implements Cake{

    public String getDescription() {
        return "奶油蛋糕";
    }

    public BigDecimal cost() {
        return BigDecimal.valueOf(200);
    }

}

蛋糕装饰类

代码语言:javascript
复制
public class CakeDecorator implements Cake{

    Cake cake;

    public CakeDecorator(Cake cake) {
        this.cake = cake;
    }

    public String getDescription() {
        return cake.getDescription();
    }

    public BigDecimal cost() {
        return cake.cost();
    }

}

樱桃

代码语言:javascript
复制
public class Cherry extends CakeDecorator{

    public Cherry(Cake cake) {
        super(cake);
    }

    @Override
    public String getDescription() {
        return cake.getDescription() + ",添加樱桃";
    }

    @Override
    public BigDecimal cost() {
        return BigDecimal.valueOf(30).add(cake.cost());
    }
}

草莓

代码语言:javascript
复制
public class Strawberry extends CakeDecorator{

    public Strawberry(Cake cake) {
        super(cake);
    }

    @Override
    public String getDescription() {
        return cake.getDescription() + ",添加草莓";
    }

    @Override
    public BigDecimal cost() {
        return BigDecimal.valueOf(15).add(cake.cost());
    }
}

火龙果

代码语言:javascript
复制
public class DragonFruit extends CakeDecorator{

    public DragonFruit(Cake cake) {
        super(cake);
    }

    @Override
    public String getDescription() {
        return cake.getDescription() + ",添加火龙果";
    }

    @Override
    public BigDecimal cost() {
        return BigDecimal.valueOf(10).add(cake.cost());
    }
}

测试类

代码语言:javascript
复制
public class Test {

    public static void main(String[] args) {
        // 标准奶油蛋糕
        Cake cake = new CreamCake();
        // 添加樱桃
        cake = new Cherry(cake);
        // 添加草莓
        cake = new Strawberry(cake);
        // 添加火龙果
        cake = new DragonFruit(cake);
        // 双份樱桃,土豪啊
        cake = new Cherry(cake);

        System.out.println(cake.getDescription());
        System.out.println("合计:" + cake.cost());
    }
}

测试结果

代码语言:javascript
复制
奶油蛋糕,添加樱桃,添加草莓,添加火龙果,添加樱桃
合计:285

调用过程如下:

装饰模式的调用链,看起来有点像递归调用,装饰类都是附加在其他类上的,但是最重要的还是调用链最底层的实现类。

总结

装饰模式的优点:

  1. 比继承更灵活,用继承实现的类似功能的话,会产生许多新类,就像上文提到的”类爆炸”。装饰模式可以提供了更加灵活的添加职责的方式,可以在运行时动态地新增和删除职责。
  2. 通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合。可以使用多个具体装饰类来装饰同一对象,得到功能更为强大的对象。
  3. 具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,在使用时再对其进行组合,原有代码无须改变,符合“开闭原则”

装饰模式的缺点:

  1. 使用装饰模式进行系统设计时将产生很多小对象,同时还将产生很多具体装饰类。这些装饰类和小对象的产生将增加系统的复杂度,加大学习与理解的难度。
  2. 这种比继承更加灵活机动的特性,也同时意味着装饰模式比继承更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐。

最后

小美吃着蛋糕,露出开心的笑容:好多樱桃哦,我好喜欢!

小帅:知道你喜欢吃水果,我特意让老板加了两份樱桃呢。

小美:程序员的心思果然细腻,哈哈。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-07-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 编程我也会 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 生日蛋糕
  • 正常套路
    • 缺点
    • 装饰模式救场
      • 蛋糕接口
        • 奶油蛋糕
          • 蛋糕装饰类
            • 樱桃
              • 草莓
                • 火龙果
                  • 测试类
                    • 测试结果
                    • 总结
                    • 最后
                    领券
                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档