一、装饰模式简介
装饰模式(Decorator Pattern)是一种结构型设计模式,它允许向一个现有的对象添加新的功能,同时又不改变其结构。这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。
二、星巴克咖啡系统设计
根据提供的UML类图,我们来设计一个星巴克咖啡下单系统,该系统可以计算不同咖啡和调料组合的价格。
系统组成
-
抽象组件(Beverage):相当于Component类,是所有咖啡和调料的基类
-
具体组件:HouseBlend、Expresso、DarkRoast、Decaf,代表不同类型的咖啡
-
装饰器(CondimentDecorator):抽象装饰类
-
具体装饰器:Milk、Mocha、Soy、Whip,代表不同的调料
三、代码实现
1. 抽象组件(Beverage)
/**
* 抽象组件 - 饮料基类
* 相当于装饰模式中的Component角色
*/
public abstract class Beverage {
// 饮料描述,初始为"Unknown Beverage"
String description = "Unknown Beverage";
/**
* 获取饮料描述
* @return 饮料描述字符串
*/
public String getDescription() {
return description;
}
/**
* 计算饮料价格 - 抽象方法,由子类实现
* @return 饮料价格
*/
public abstract double cost();
}
2. 具体组件(各种咖啡类型)
2.1 HouseBlend 咖啡
/**
* 具体组件 - 混合咖啡
*/
public class HouseBlend extends Beverage {
public HouseBlend() {
description = "House Blend Coffee";
}
@Override
public double cost() {
return 0.89; // 基础价格0.89美元
}
}
2.2 Expresso 咖啡
/**
* 具体组件 - 浓缩咖啡
*/
public class Expresso extends Beverage {
public Expresso() {
description = "Expresso";
}
@Override
public double cost() {
return 1.99; // 基础价格1.99美元
}
}
2.3 DarkRoast 咖啡
/**
* 具体组件 - 深焙咖啡
*/
public class DarkRoast extends Beverage {
public DarkRoast() {
description = "Dark Roast Coffee";
}
@Override
public double cost() {
return 0.99; // 基础价格0.99美元
}
}
2.4 Decaf 咖啡
/**
* 具体组件 - 低因咖啡
*/
public class Decaf extends Beverage {
public Decaf() {
description = "Decaf Coffee";
}
@Override
public double cost() {
return 1.05; // 基础价格1.05美元
}
}
3. 抽象装饰器(CondimentDecorator)
/**
* 抽象装饰器 - 调料装饰器基类
* 继承自Beverage,所以装饰器可以嵌套装饰器
*/
public abstract class CondimentDecorator extends Beverage {
/**
* 获取完整描述 - 抽象方法
* 每个具体装饰器需要实现如何添加自己的描述
*/
@Override
public abstract String getDescription();
}
4. 具体装饰器(各种调料)
4.1 Milk 牛奶
/**
* 具体装饰器 - 牛奶
*/
public class Milk extends CondimentDecorator {
// 被装饰的饮料
Beverage beverage;
public Milk(Beverage beverage) {
this.beverage = beverage;
}
@Override
public String getDescription() {
return beverage.getDescription() + ", Milk"; // 添加牛奶描述
}
@Override
public double cost() {
return beverage.cost() + 0.10; // 增加0.10美元
}
}
4.2 Mocha 摩卡
/**
* 具体装饰器 - 摩卡
*/
public class Mocha extends CondimentDecorator {
Beverage beverage;
public Mocha(Beverage beverage) {
this.beverage = beverage;
}
@Override
public String getDescription() {
return beverage.getDescription() + ", Mocha"; // 添加摩卡描述
}
@Override
public double cost() {
return beverage.cost() + 0.20; // 增加0.20美元
}
}
4.3 Soy 豆浆
/**
* 具体装饰器 - 豆浆
*/
public class Soy extends CondimentDecorator {
Beverage beverage;
public Soy(Beverage beverage) {
this.beverage = beverage;
}
@Override
public String getDescription() {
return beverage.getDescription() + ", Soy"; // 添加豆浆描述
}
@Override
public double cost() {
return beverage.cost() + 0.15; // 增加0.15美元
}
}
4.4 Whip 奶泡
/**
* 具体装饰器 - 奶泡
*/
public class Whip extends CondimentDecorator {
Beverage beverage;
public Whip(Beverage beverage) {
this.beverage = beverage;
}
@Override
public String getDescription() {
return beverage.getDescription() + ", Whip"; // 添加奶泡描述
}
@Override
public double cost() {
return beverage.cost() + 0.10; // 增加0.10美元
}
}
5. 客户端使用示例
/**
* 星巴克咖啡店 - 客户端代码
*/
public class StarbuzzCoffee {
public static void main(String args[]) {
// 示例1:一杯纯Espresso
Beverage beverage1 = new Expresso();
System.out.println(beverage1.getDescription() + " $" + beverage1.cost());
// 示例2:DarkRoast加双份Mocha和Whip
Beverage beverage2 = new DarkRoast();
beverage2 = new Mocha(beverage2); // 第一次装饰:加Mocha
beverage2 = new Mocha(beverage2); // 第二次装饰:再加Mocha
beverage2 = new Whip(beverage2); // 第三次装饰:加Whip
System.out.println(beverage2.getDescription() + " $" + beverage2.cost());
// 示例3:HouseBlend加Soy、Mocha和Whip
Beverage beverage3 = new HouseBlend();
beverage3 = new Soy(beverage3); // 第一次装饰:加Soy
beverage3 = new Mocha(beverage3); // 第二次装饰:加Mocha
beverage3 = new Whip(beverage3); // 第三次装饰:加Whip
System.out.println(beverage3.getDescription() + " $" + beverage3.cost());
}
}
四、代码结构说明
-
Beverage 是所有饮料的基类,定义了基本接口
-
具体咖啡类型(HouseBlend、Expresso等)继承Beverage,实现具体价格
-
CondimentDecorator是装饰器基类,也继承自Beverage
-
具体调料(Milk、Mocha等)继承CondimentDecorator,包装一个Beverage对象
-
客户端可以自由组合咖啡和调料,通过层层装饰实现复杂组合
五、装饰模式的优势
-
灵活性:可以动态地添加或删除功能,比继承更灵活
-
避免类爆炸:不需要为每种组合创建子类
-
符合开闭原则:对扩展开放,对修改关闭
-
运行时添加功能:可以在运行时决定添加哪些装饰
六、总结
通过这个星巴克咖啡系统的例子,我们看到了装饰模式在实际应用中的强大之处。它让我们能够轻松地组合各种咖啡和调料,而不需要创建大量的子类。这种模式特别适合那些需要动态、透明地添加对象功能的场景。