设计模式-装饰模式
结构图解释:
Component 可以是接口,也可以是抽象类,目的是指明Operation(),也就是规范要做什么。
ConcreteComponent 一个类,继承或者实现Component。是被装饰的代表,例如饭,奶茶。
Decorator类 装饰类,内部一定有Component或者ConcreteComponent的属性,表明用于装饰谁(扩展谁)。
ConcreteDecoratorA,ConcreteDecoratorB 我理解是各种装饰物(扩展)
如果只有一个ConcreteComponent而没有抽象的Component,那么Decorator类可以是ConcreteComponent的子类
何谓装饰模式?
例如,我现在有碗白饭,这饭上可以放土豆丝,放白菜,放豆干,放鸭腿,这就是鸭腿饭了。
那我放土豆丝,放白菜,放鸡腿,放豆干,这就是鸡腿饭了。
放不同的配菜,就是不同的套餐,如果我现在的菜单上有下述几种套餐:
鸡腿饭(鸡腿+青菜+香干)
肉丝饭(肉丝+青菜+香干)
大排饭(大排+青菜+香干)
鸭腿饭(鸭腿+青菜+香干)
东坡肉饭(东坡+青菜+香干)
要实现这几种饭
先用最容易想到的方式实现,每种套餐创建一个类,那么就有鸡腿饭类,肉丝饭类,大排饭类...
如果我现在鸡腿饭+一个大排你怎么办?
难道在创建一个鸡腿饭+一个大排的类?
那如果我要再加一个东坡肉呢?再加一个鸭腿呢?(这真有钱啊)
现在就发现我们这个实现方式就并不合适了。
我们先来分析一下,不同套餐其实就是不同的配菜加米饭对不对,像不像再给米饭的基础上一直叠加东西。
用装饰模式代码实现如下
/** * SX小吃 我这里卖 * 鸡腿饭(鸡腿+青菜+香干) * 肉丝饭(肉丝+青菜+香干) * 大排饭(大排+青菜+香干) * 鸭腿饭(鸭腿+青菜+香干) * 东坡肉饭(东坡+青菜+香干) * @author wrj * @description * @Date 2021/12/1 2:37 下午 */
abstract class Food { public abstract void has(); } //米饭 class Rice extends Food{ @Override public void has() { System.out.println("有米饭"); } } //装饰类 abstract class FoodDecorator extends Food{ public Food food; public void addFood(Food food){ this.food = food; } @Override public void has() { food.has(); } } //鸡腿 class Drumstick extends FoodDecorator{ @Override public void has() { System.out.println("有鸡腿"); super.has(); } } //肉丝 class ShreddedMeat extends FoodDecorator{ @Override public void has() { System.out.println("有肉丝"); super.has(); } } //大排 class PorkRibs extends FoodDecorator{ @Override public void has() { System.out.println("有大排"); super.has(); } } //青菜 class Vegetable extends FoodDecorator{ @Override public void has() { System.out.println("有青菜"); super.has(); } } //豆干 class DriedBeanCurd extends FoodDecorator{ @Override public void has() { System.out.println("有豆干"); super.has(); } } public class SXFood {
//变量写中文为了方便理解 并不标准 public static void main(String[] args) { //标准鸡腿饭的构建过程 System.out.println("鸡腿饭:\r\n"); Rice 米饭 = new Rice(); DriedBeanCurd 豆干 = new DriedBeanCurd(); Vegetable 青菜 = new Vegetable(); Drumstick 鸡腿 = new Drumstick(); 鸡腿.addFood(青菜); 青菜.addFood(豆干); 豆干.addFood(米饭); 鸡腿.has(); //特殊需求 鸡腿饭+大排 System.out.println("鸡腿饭+大排:\r\n"); Rice 米饭1 = new Rice(); DriedBeanCurd 豆干1 = new DriedBeanCurd(); Vegetable 青菜1 = new Vegetable(); Drumstick 鸡腿1 = new Drumstick(); PorkRibs 大排 = new PorkRibs(); 鸡腿1.addFood(大排); 大排.addFood(青菜1); 青菜1.addFood(豆干1); 豆干1.addFood(米饭1); 鸡腿1.has(); } }
最后输出:
装饰的过程感觉像构建一个链表的过程

可以看到 装饰的过程类似于构建一个链表的过程,每个链表的节点调用has方法时都会去调用super的has从而调用下一个节点的has直至最后一个节点

这种场景就适合用装饰模式了。回过头来再看装饰模式的概念:
动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。
上面的鸡腿饭+大排的场景就体现了动态添加的特性以及为什么比子类更灵活因为我们不用一直生成子类,我们可以任意加东西来实现功能
可以看到 不同的搭配最终组合成了不同的套餐,这样不管有什么奇葩的需求,都不用改类,只需要改装饰的内容和顺序即可。
这也体现了开闭原则,不用改变基础的类,只需要改变使用方法即可。
需要注意装饰的顺序,顺序地不同,产生的结果也是不同的。这点需要注意
总结:
装饰模式是为已有功能动态地添加更多功能的一种方式,当系统需要新功能时,是向旧的类中添加新的代码。这些新加的代码通常装饰了原有类的核心职责或主要行为。
在主类中加入了新的字段,新的方法和新的逻辑,从而增加了主类的复杂度,而这些新加入的东西仅仅是为了满足一些只在某种特定情况下才会执行的特殊行为的需要(参考鸡腿饭加一个大排的场景)。
装饰模式提供了一个非常好的解决办法,它把每个要装饰的功能放在单独的类中。并让这个类包装它所要装饰的对象。因此,当需要执行特殊行为时,客户代码就可以在运行时根据需要有选择地、按顺序地使用装饰功能包装对象。
优点:把类中的装饰功能从类中搬移出去,简化原有的类。有效地把类的核心职责和装饰功能区分开。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端