一、装饰模式简介
装饰模式(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";}@Overridepublic double cost() {return 0.89; // 基础价格0.89美元}
}
2.2 Expresso 咖啡
/*** 具体组件 - 浓缩咖啡*/
public class Expresso extends Beverage {public Expresso() {description = "Expresso";}@Overridepublic double cost() {return 1.99; // 基础价格1.99美元}
}
2.3 DarkRoast 咖啡
/*** 具体组件 - 深焙咖啡*/
public class DarkRoast extends Beverage {public DarkRoast() {description = "Dark Roast Coffee";}@Overridepublic double cost() {return 0.99; // 基础价格0.99美元}
}
2.4 Decaf 咖啡
/*** 具体组件 - 低因咖啡*/
public class Decaf extends Beverage {public Decaf() {description = "Decaf Coffee";}@Overridepublic double cost() {return 1.05; // 基础价格1.05美元}
}
3. 抽象装饰器(CondimentDecorator)
/*** 抽象装饰器 - 调料装饰器基类* 继承自Beverage,所以装饰器可以嵌套装饰器*/
public abstract class CondimentDecorator extends Beverage {/*** 获取完整描述 - 抽象方法* 每个具体装饰器需要实现如何添加自己的描述*/@Overridepublic abstract String getDescription();
}
4. 具体装饰器(各种调料)
4.1 Milk 牛奶
/*** 具体装饰器 - 牛奶*/
public class Milk extends CondimentDecorator {// 被装饰的饮料Beverage beverage;public Milk(Beverage beverage) {this.beverage = beverage;}@Overridepublic String getDescription() {return beverage.getDescription() + ", Milk"; // 添加牛奶描述}@Overridepublic 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;}@Overridepublic String getDescription() {return beverage.getDescription() + ", Mocha"; // 添加摩卡描述}@Overridepublic 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;}@Overridepublic String getDescription() {return beverage.getDescription() + ", Soy"; // 添加豆浆描述}@Overridepublic 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;}@Overridepublic String getDescription() {return beverage.getDescription() + ", Whip"; // 添加奶泡描述}@Overridepublic double cost() {return beverage.cost() + 0.10; // 增加0.10美元}
}
5. 客户端使用示例
/*** 星巴克咖啡店 - 客户端代码*/
public class StarbuzzCoffee {public static void main(String args[]) {// 示例1:一杯纯EspressoBeverage beverage1 = new Expresso();System.out.println(beverage1.getDescription() + " $" + beverage1.cost());// 示例2:DarkRoast加双份Mocha和WhipBeverage beverage2 = new DarkRoast();beverage2 = new Mocha(beverage2); // 第一次装饰:加Mochabeverage2 = new Mocha(beverage2); // 第二次装饰:再加Mochabeverage2 = new Whip(beverage2); // 第三次装饰:加WhipSystem.out.println(beverage2.getDescription() + " $" + beverage2.cost());// 示例3:HouseBlend加Soy、Mocha和WhipBeverage beverage3 = new HouseBlend();beverage3 = new Soy(beverage3); // 第一次装饰:加Soybeverage3 = new Mocha(beverage3); // 第二次装饰:加Mochabeverage3 = new Whip(beverage3); // 第三次装饰:加WhipSystem.out.println(beverage3.getDescription() + " $" + beverage3.cost());}
}
四、代码结构说明
-
Beverage 是所有饮料的基类,定义了基本接口
-
具体咖啡类型(HouseBlend、Expresso等)继承Beverage,实现具体价格
-
CondimentDecorator是装饰器基类,也继承自Beverage
-
具体调料(Milk、Mocha等)继承CondimentDecorator,包装一个Beverage对象
-
客户端可以自由组合咖啡和调料,通过层层装饰实现复杂组合
五、装饰模式的优势
-
灵活性:可以动态地添加或删除功能,比继承更灵活
-
避免类爆炸:不需要为每种组合创建子类
-
符合开闭原则:对扩展开放,对修改关闭
-
运行时添加功能:可以在运行时决定添加哪些装饰
六、总结
通过这个星巴克咖啡系统的例子,我们看到了装饰模式在实际应用中的强大之处。它让我们能够轻松地组合各种咖啡和调料,而不需要创建大量的子类。这种模式特别适合那些需要动态、透明地添加对象功能的场景。