过几天就是小美的生日了,小帅不敢怠慢,打算定一个奶油蛋糕,好好庆祝一下。
店里标准的奶油蛋糕上面就一层奶油,也没有其他的配料,看着不大气,万一小美不喜欢可就糟了。
小美喜欢吃水果,小帅就想在蛋糕上加点水果,就找老板商量:能不能多加几种水果,我可以另外加钱。
老板眉开眼笑:当然可以啊,我们这里有樱桃,草莓,火龙果你要加什么哦?
这是我们的价目表:
奶油蛋糕:200元 巧克力蛋糕:230元 樱桃:30元 草莓:15元 火龙果:10元
我们店有两种蛋糕,一种是奶油蛋糕,另一种是巧克力蛋糕,每种蛋糕都可以另外加水果哦。
按照正常的套路,如果用代码实现买蛋糕的功能,我们一般会给每种蛋糕和水果组合设计一种类。
如果我们想要在奶油蛋糕上加樱桃就新建一个奶油蛋糕樱桃类(CreamCakeAndCherry);
如果奶油蛋糕上加草莓就新建一个奶油蛋糕草莓类(CreamCakeAndStrawberry);
如果巧克力蛋糕上加樱桃就新建一个巧克力蛋糕樱桃类(ChocolateCakeAndCherry)。
还有奶油蛋糕火龙果类,巧克力蛋糕草莓类,巧克力蛋糕火龙果类,如果蛋糕店又增加了一种千层蛋糕,还有千层蛋糕草莓类,千层蛋糕樱桃类。。。
如果再加几种水果。。。完蛋了,类已经爆炸了。。。
这么写有什么缺点呢?
小帅作为一个有追求的程序员,当然不会满足这种做法,小帅想起了装饰模式,水果不是刚好装饰蛋糕的吗?
正好符合装饰模式的应用场景,赶紧试一下吧。
装饰模式(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
调用过程如下:
装饰模式的调用链,看起来有点像递归调用,装饰类都是附加在其他类上的,但是最重要的还是调用链最底层的实现类。
装饰模式的优点:
装饰模式的缺点:
小美吃着蛋糕,露出开心的笑容:好多樱桃哦,我好喜欢!
小帅:知道你喜欢吃水果,我特意让老板加了两份樱桃呢。
小美:程序员的心思果然细腻,哈哈。