告别裸奔读写:用STM32CubeMX和FatFs给你的W25Q64 SPI Flash穿上‘文件系统’外衣
2026/6/6 16:27:58
在不修改原有对象的前提下,运行时动态、透明地给对象层层添加额外行为,保持接口不变。
场景:你有一个基本的网络 Socket 发送数据,但实际项目中经常需要层层叠加功能:
客户端代码只关心stream->write(data),完全不知道中间加了多少层处理!
不用装饰器会怎样?继承爆炸:
classEncryptedSocket:publicSocket{...}classEncryptedCompressedSocket:publicEncryptedSocket{...}classEncryptedCompressedLoggedSocket:publicEncryptedCompressedSocket{...}// 3种功能 → 8种子类!明天加“校验和”功能?直接崩溃。错得离谱!下面这张表10秒彻底撕开:
| 项目 | 普通继承多态(你觉得像的) | 装饰器模式(真相) |
|---|---|---|
| 添加行为时机 | 静态:编译时固定在子类里 | 动态:运行时随意叠加、顺序随意、数量随意 |
| 类数量 | 功能组合爆炸(2^n 个子类) | 只有 n + 1 个类(基础 + n种装饰) |
| 灵活性 | 想临时加/减一层功能?不可能 | 运行时随意包装:想加双重加密?包两层就行 |
| 关系本质 | is-a(EncryptedSocket “是一种” Socket) | has-a(装饰器“持有”一个被装饰的对象) |
| 软件真实场景 | 固定类型:TcpSocket、UdpSocket | 临时增强:加密流、压缩流、日志流、缓冲流 |
| 口号 | “焊死在子类里” | “运行时层层叠加,想加就加,想拆就拆” |
狠话:继承是生孩子——生完就定型;装饰器是穿外套——想穿几件、哪件先穿都行,还能脱!
#include<iostream>#include<memory>#include<string>#include<vector>usingnamespacestd;// 1. 统一数据流接口classDataStream{public:virtual~DataStream()=default;virtualvoidwrite(constvector<uint8_t>&data)=0;virtualstringname()const=0;// 用于调试描述};// 2. 基础流(真正干活的核心)classTcpSocketStream:publicDataStream{public:voidwrite(constvector<uint8_t>&data)override{cout<<"[TcpSocket] 真正发送数据,长度: "<<data.size()<<" 字节"<<endl;}stringname()constoverride{return"TCP Socket";}};// 3. 装饰器基类(关键:持有一个流指针)classStreamDecorator:publicDataStream{protected:unique_ptr<DataStream>stream;// ← 包装链的核心public:explicitStreamDecorator(unique_ptr<DataStream>s):stream(move(s)){}stringname()constoverride{returnstream->name();// 默认转发,子类可追加}};// 4. 具体装饰器(真实功能)classLoggingDecorator:publicStreamDecorator{public:explicitLoggingDecorator(unique_ptr<DataStream>s):StreamDecorator(move(s)){}voidwrite(constvector<uint8_t>&data)override{cout<<"[Logging] 记录写操作,数据大小: "<<data.size()<<endl;stream->write(data);// 转发给下一层}stringname()constoverride{returnstream->name()+" + Logging";}};classCompressionDecorator:publicStreamDecorator{public:explicitCompressionDecorator(unique_ptr<DataStream>s):StreamDecorator(move(s)){}voidwrite(constvector<uint8_t>&data)override{cout<<"[Compression] 正在压缩数据... (原大小 "<<data.size()<<" -> 压缩后 "<<data.size()/2<<")"<<endl;autocompressed=compress(data);// 模拟压缩stream->write(compressed);}stringname()constoverride{returnstream->name()+" + Compression";}private:vector<uint8_t>compress(constvector<uint8_t>&d){return{1,2,3};}// 简化};classEncryptionDecorator:publicStreamDecorator{public:explicitEncryptionDecorator(unique_ptr<DataStream>s):StreamDecorator(move(s)){}voidwrite(constvector<uint8_t>&data)override{cout<<"[Encryption] 正在AES加密数据..."<<endl;autoencrypted=encrypt(data);// 模拟加密stream->write(encrypted);}stringname()constoverride{returnstream->name()+" + Encryption";}private:vector<uint8_t>encrypt(constvector<uint8_t>&d){return{9,9,9};}// 简化};intmain(){vector<uint8_t>payload={1,2,3,4,5,6,7,8,9,0};// 场景1:生产环境 - 全功能autostream=make_unique<TcpSocketStream>();stream=make_unique<LoggingDecorator>(move(stream));stream=make_unique<CompressionDecorator>(move(stream));stream=make_unique<EncryptionDecorator>(move(stream));cout<<"当前流: "<<stream->name()<<endl;// TCP Socket + Logging + Compression + Encryptionstream->write(payload);// 场景2:调试环境 - 只加日志,不加密不压缩autodebugStream=make_unique<TcpSocketStream>();debugStream=make_unique<LoggingDecorator>(move(debugStream));debugStream->write(payload);// 场景3:想双重加密?轻松!autosecureStream=make_unique<TcpSocketStream>();secureStream=make_unique<EncryptionDecorator>(move(secureStream));secureStream=make_unique<EncryptionDecorator>(move(secureStream));// 双重加密!secureStream->write(payload);}std::ostream→std::ofstream→std::stringstream(层层包装)“功能运行时叠,层层包装不继承;
接口不变真优雅,加密压缩随便配!”
当你在软件中需要“运行时给一个流/对象临时叠加多个处理行为”(日志、加密、压缩、缓冲等),且组合方式多变时,
别用继承爆炸,用装饰器——层层包装,接口统一,灵活到极致!
现在,装饰器模式在纯软件场景下彻底说透了吧?
这才是程序员每天都在用的真实模式!
下一期要外观模式(Facade),它专门治“子系统太复杂”的病!