ITPub博客

首页 > 数据库 > 数据库开发技术 > 装饰模式--小美的生日蛋糕

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

原创 数据库开发技术 作者:zhanyd 时间:2020-07-06 20:32:41 0 删除 编辑

生日蛋糕

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

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

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

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

这是我们的价目表:

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

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

正常套路

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

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

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

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

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

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

缺点

这么写有什么缺点呢?

  1. 最明显的是类太多了,看得眼花缭乱,增加了维护的成本。

  2. 一旦出现的新蛋糕或水果,又要增加一大堆类。

  3. 如果蛋糕和水果的价格改变了,要改很多很多类,严重违反了开闭原则:类应该对扩展开发,对修改封闭。

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

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

装饰模式救场

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

装饰模式的类图如下:

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


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

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

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

蛋糕接口

public interface Cake {

public String getDescription();

public BigDecimal cost();
}

奶油蛋糕

public class CreamCake implements Cake{

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

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

}

蛋糕装饰类

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

}

樱桃

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

草莓

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

火龙果

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

测试类

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

测试结果

奶油蛋糕,添加樱桃,添加草莓,添加火龙果,添加樱桃
合计:285

调用过程如下:


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


总结

装饰模式的优点:

  1. 比继承更灵活,用继承实现的类似功能的话,会产生许多新类,就像上文提到的”类爆炸”。装饰模式可以提供了更加灵活的添加职责的方式,可以在运行时动态地新增和删除职责。

  2. 通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合。可以使用多个具体装饰类来装饰同一对象,得到功能更为强大的对象。

  3. 具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,在使用时再对其进行组合,原有代码无须改变,符合“开闭原则”

装饰模式的缺点:

  1. 使用装饰模式进行系统设计时将产生很多小对象,同时还将产生很多具体装饰类。这些装饰类和小对象的产生将增加系统的复杂度,加大学习与理解的难度。

  2. 这种比继承更加灵活机动的特性,也同时意味着装饰模式比继承更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐。


最后

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

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

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

(公众号回复:设计模式代码,获取git代码链接)







原文作者:编程我也会;如有侵权,请联系管理员删除。

下一篇: centos7 NFS 配置
请登录后发表评论 登录
全部评论

注册时间:2018-06-20

  • 博文量
    120
  • 访问量
    51724