手撕设计模式——咖啡点单系统之装饰模式
1.业务需求
大家好,我是菠菜啊,好久不见,今天给大家带来的是——装饰模式。老规矩,在介绍这期内容前,我们先来看看这样的需求:现在有一个咖啡馆,有基础饮料:美式咖啡、红茶、拿铁等,配料:牛奶、奶泡、糖等,怎么样实现任意的基础饮料和配料的组合,并且能够输出组合描述以及结算金额?

2.代码实现
Talk is cheap,show me your code.
初版实现思路:
我们之前学习过桥接模式,可以用该模式实现。
初版代码如下:
//饮料抽象类
public abstract class Beverage2 {
protected ToppingImplementor toppingImplementor;
protected String description;
protected double cost;
public Beverage2(ToppingImplementor toppingImplementor)
{
this.toppingImplementor = toppingImplementor;
}
public void setToppingImplementor(ToppingImplementor toppingImplementor) {
this.toppingImplementor = toppingImplementor;
}
public String getDescription(){
return this.description+toppingImplementor.addTopping();
}
public double cost(){
return this.cost+toppingImplementor.addCost();
}
}
//配料接口
public interface ToppingImplementor {
String addTopping();
double addCost();
}
//红茶
public class BlackTea2 extends Beverage2{
public BlackTea2(ToppingImplementor toppingImplementor) {
super(toppingImplementor);
description = "红茶";
cost = 12;
}
}
//美式
public class AmericanoCoffee2 extends Beverage2{
public AmericanoCoffee2(ToppingImplementor toppingImplementor) {
super(toppingImplementor);
description = "美式咖啡";
cost = 18;
}
}
//拿铁
public class Latte2 extends Beverage2{
public Latte2(ToppingImplementor toppingImplementor) {
super(toppingImplementor);
description = "拿铁";
cost = 17;
}
}
//牛奶配料
public class Milk2 implements ToppingImplementor{
@Override
public String addTopping() {
return "加奶";
}
@Override
public double addCost() {
return 3;
}
}
//糖配料
public class Sugar2 implements ToppingImplementor{
@Override
public String addTopping() {
return "加糖";
}
@Override
public double addCost() {
return 2;
}
}
//奶泡配料
public class Whip2 implements ToppingImplementor{
@Override
public String addTopping() {
return "加奶泡";
}
@Override
public double addCost() {
return 4;
}
}
//客户端
public class Client2 {
public static void main(String[] args) {
Beverage2 beverage2 = new Latte2(new Sugar2());
System.out.println(beverage2.getDescription()+"花费:"+beverage2.cost());
beverage2.setToppingImplementor(new Milk2());
System.out.println(beverage2.getDescription()+"花费:"+beverage2.cost());
}
}
执行结果:

代码结构:

思考:
上述代码用桥接模式将基础饮料和配料的抽象和实现分离,但是我们也发现一些问题。比如,我们运行时要加双份配料无法实现,如果再添加一种维度(如杯型)无需要修改代码才能实现。于是,我们进一步优化代码。
3.代码优化
优化代码:
//饮料基类
public abstract class Beverage {
abstract String getDescription();
abstract double cost();
}
//基础饮料具体实现
public class BlackTea extends Beverage{
@Override
public String getDescription() {
return "红茶";
}
@Override
public double cost() {
return 12;
}
}
public class AmericanoCoffee extends Beverage{
@Override
public String getDescription() {
return "美式咖啡";
}
@Override
public double cost() {
return 18;
}
}
public class Latte extends Beverage{
@Override
public String getDescription() {
return "拿铁";
}
@Override
public double cost() {
return 17;
}
}
//配料装饰器抽象类
public abstract class BeverageDecorator extends Beverage{
protected Beverage beverage;
public BeverageDecorator(Beverage beverage){
this.beverage = beverage;
}
}
//具体配料装饰器实现
public class Milk extends BeverageDecorator{
public Milk(Beverage beverage) {
super(beverage);
}
@Override
String getDescription() {
return beverage.getDescription() + "加奶";
}
@Override
double cost() {
return beverage.cost() + 3;
}
}
public class Sugar extends BeverageDecorator{
public Sugar(Beverage beverage) {
super(beverage);
}
@Override
String getDescription() {
return beverage.getDescription() + "加糖";
}
@Override
double cost() {
return beverage.cost() + 2;
}
}
public class Whip extends BeverageDecorator{
public Whip(Beverage beverage) {
super(beverage);
}
@Override
String getDescription() {
return beverage.getDescription() + "加奶泡";
}
@Override
double cost() {
return beverage.cost() + 4;
}
}
//客户端
public class Client {
public static void main(String[] args) {
Beverage beverage = new Sugar(new Sugar(new Milk(new AmericanoCoffee())));
System.out.println(beverage.getDescription()+",价格"+beverage.cost());
Beverage beverage2 = new Latte();
beverage2 = new Milk(beverage2);
beverage2 = new Whip(beverage2);
beverage2 = new Sugar(beverage2);
System.out.println(beverage2.getDescription()+",价格"+beverage2.cost());
}
}
执行结果:

代码结构:

思考:
上述代码将配料种类和基础饮料解耦,N种饮料+M种配料只需(N+M)个类,支持运行时自由组合配料(如双倍加糖),新增配料或者基础饮料无需修改代码,满足开闭原则。这种纵向增强现有功能,层层叠加功能的实现方式,就是装饰模式。

4.定义与组成

装饰模式(Decorator Pattern)是一种结构型设计模式,它允许动态地向对象添加新功能,同时不改变其结构。该模式通过创建包装对象(装饰器)来扩展原始对象的功能,提供了比继承更灵活的功能扩展方式。
核心定义:
在不改变现有对象结构的情况下,动态地给对象添加额外职责。装饰模式比生成子类更为灵活。
- 组件接口(Component ):定义一个对象接口,给这些动态地添加职责
- 具体组件(Concrete Component):实现组件接口的基础功能
- 抽象装饰器(Decorator):维持对组件对象的引用并实现组件接口
- 具体装饰器(Concrete Decorator):添加具体的附加功能
5.应用示例
5.1 Java I/O流体系
// 经典装饰模式实现
InputStream fileStream = new FileInputStream("data.txt");
InputStream bufferedStream = new BufferedInputStream(fileStream); //添加缓冲
InputStream gzipStream = new GZIPInputStream(bufferedStream);
- 组件接口:
InputStream - 具体组件:
FileInputStream - 装饰器抽象类:
FilterInputStream - 具体装饰器:
BufferedInputStream,GZIPInputStream
5.2 Java GUI (Swing/AWT)
// 为组件添加滚动功能
JTextArea textArea = new JTextArea();
JScrollPane scrollPane = new JScrollPane(textArea);
5.3 服务层功能增强
日志/监控/权限校验:通过装饰器为业务逻辑添加非核心功能,保持业务类纯净
UserService service = new UserServiceImpl();
service = new LoggingDecorator(service); // 添加日志记录
service = new TimingDecorator(service); // 添加性能监控
5.4 动态配置组合
电商优惠系统:基础价格策略通过装饰器叠加满减、折扣券等功能
PriceStrategy base = new BasePriceStrategy();
base = new DiscountDecorator(base, 0.8); // 8折
base = new CouponDecorator(base, 100); // 满送100

6.适用场景
6.1 核心适用场景
✅ 需动态扩展功能:如运行时按需添加日志、加密等。
✅ 避免类爆炸:功能组合多时(如咖啡配料、电商优惠)。
✅ 保持接口一致性:所有对象(原始/装饰后)对外暴露相同接口。
❌ 基础功能修改:如需彻底改变核心逻辑,应使用策略模式或适配器模式。
6.2 注意事项
- 避免过度装饰
装饰层数过多会降低可读性和调试难度(如10层装饰调用栈深度增加)
- 顺序敏感:
装饰顺序影响最终结果
7.结构性模式对比
装饰器、代理、桥接 都属于结构型模式,都涉及对象组合,但它们解决的问题、目的和实现方式有显著区别:
| 特性 | 装饰器模式 (Decorator) | 代理模式 (Proxy) | 桥接模式 (Bridge) |
|---|---|---|---|
| 主要目的 | 动态添加职责 (增强功能) | 控制访问 (间接访问、延迟加载等) | 分离抽象与实现 (解耦两个维度) |
| 关注点 | 为对象添加新功能/行为 | 管理对对象的访问方式 | 管理类结构的扩展维度 |
| 关系对象 | 包装同一接口的对象 (通常层次深) | 代表一个具体对象 (通常1对1) | 连接抽象角色和实现角色 |
| 功能变化 | 运行时动态添加/移除功能 | 隐藏/控制原有功能 | 编译时确定抽象和实现的组合 |
| 继承替代 | 是 (避免子类爆炸) | 不一定是 (也可用于控制) | 是 (防止多维度继承爆炸) |
| UML 关键 | 装饰器与被装饰者实现同一接口 | 代理与真实对象实现同一接口 | 抽象持有实现的接口引用 |
| 典型应用 | Java I/O 流、GUI 组件增强 | 虚拟代理、保护代理、远程代理 | 跨平台UI库、驱动接口 |
一句话总结:“加功能(装饰)、控访问(代理)、解耦合(桥接)”。
8.总结
装饰模式是Java开发中最常用且强大的设计模式之一,特别适用于需要动态扩展功能的场景。它通过组合代替继承,完美解决了功能扩展中的类爆炸问题,同时保持了对开闭原则的遵守。
使用装饰模式当:
- 需要在不影响其他对象的情况下添加功能
- 需要动态、透明地添加或撤销功能
- 继承扩展不可行或不实际(如final类)
- 系统需要多层次的功能组合
技术需要沉淀,同样生活也是~
个人链接:博客,欢迎一起交流











Honestly, I expected more fluff based on the description, but this UX flow is surprisingly solid. rwwin casino really nailed mobile accessibility; check out their dedicated sections!
This is a perfect illustration of structural flexibility. The Decorator Pattern’s strength lies in its ability to compose functionality dynamically, which is crucial when dealing with layered business rules or complex user journeys-much like the diverse options available when navigating a platform such as winning plus casino. Modular design principles are universally valuable.
The math behind these platforms is fascinating, yet one must approach it with academic rigor. Remember, checking the fine print on bdko is key; never assume security just from the name.
The emphasis on structured play over blind spins resonates. For true edge, bankroll discipline is key, much like understanding pot odds. Check out phspin vip games for a more rigorous feel.