装饰者模式

一杯cafe问题

假设我们需要设计一个cafe订单系统,用户可以选择coffee类型以及需要添加的小料,然后计算出它的价格,那么最朴素的方法就是创建一个coffee基类,然后不同类型的coffee继承自这个基类,然后计算他的价格,类图可以如下所示:

coffeeDemo1

但是这样也是有个问题的,这会导致类爆炸(因为添加每一种小料就需要添加一个新的类),如下所示:

面对上面的问题,一种解决方法就是,我们可以在基类中增加小料的成员,那么就不会导致类型爆炸了,如下所示:

coffeeDemo2

但是如果当我们增加了新的小料的时候呢?我们需要对基类进行修改,这实际上就破坏了我们的设计原则

第四个设计原则

类应该对扩展开放,对修改关闭

为了解决上面的问题,我们引入装饰者模式

装饰者模式

动态的将责任附加到对象上,若要扩展功能,装饰者提供了比继承更加有弹性的替代方案。

对应到上面的问题,我们可以首先创建一个cafe子类的对象,然后用奶泡装饰它,然后用糖装饰它,最终再计算其价格。

类图如下所示:

coffeeDemo3

代码实现

首先创建一个Beverage基类

1
2
3
4
5
6
7
8
9
public abstract class Beverage {
String description = "Unknown Beverage";

public String getDescription() {
return description;
}

public abstract double cost();
}

然后创建一个小料的类,这个类要继承自Beverage类

1
2
3
public abstract class CondimentDecorator extends Beverage{
public abstract String getDescription();
}

然后我们创建我们的coffee类型

1
2
3
4
5
6
7
8
9
10
public class Espresso extends Beverage {
public Espresso() {
description = "Espresso";
}

@Override
public double cost() {
return 1.99;
}
}
1
2
3
4
5
6
7
8
9
10
public class HouseBlend extends Beverage {
public HouseBlend() {
description = "HouseBlend";
}

@Override
public double cost() {
return 2.99;
}
}

然后创建我们的小料的装饰类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Mocha extends CondimentDecorator {
Beverage beverage;
public Mocha(Beverage beverage) {
this.beverage = beverage;
}
public String getDescription() {
return beverage.getDescription() + ", Mocha";
}

@Override
public double cost() {
return 0.1 + beverage.cost();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Whip extends CondimentDecorator {
Beverage beverage;
public Whip(Beverage beverage) {
this.beverage = beverage;
}
public String getDescription() {
return beverage.getDescription() + ", Whip";
}

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

最后的测试代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class CoffeeDemo {
public static void main(String args[]) {
Beverage beverage = new Espresso();
System.out.println(beverage.getDescription() + " $ " + beverage.cost());

Beverage beverage1 = new HouseBlend();
beverage1 = new Venti(beverage1);
beverage1 = new Mocha(beverage1);
beverage1 = new Mocha(beverage1);
beverage1 = new Whip(beverage1);

System.out.println(beverage1.getDescription() + " $ " + beverage1.cost());
}
}

从上面的代码可以看出,我们创建了coffee对象,然后将其传给各个装饰类,这样就起到了装饰的作用。

装饰者模式的一个缺点

如果要吐槽装饰者模式的话,那么一个点就是这个模式的类太多了(而且都是小的类)就比如我们的coffeeDemo,每一种配料,每一个size都要一个类,确实有点多而杂,但是比最初的方案还是好太多了。

代码归档

https://github.com/changyuanchn/DesignPattern/tree/main/src/com/changyuan/decoratorPattern

显示 Gitment 评论