装饰模式
装饰模式指的是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。 --百度百科
装饰者模式主要是为一个对象增加新的行为,效果与子类扩展父类类似,但实现方式与继承不同,且更加灵活。
装饰模式类图
Componet: 定义接口基类。
ConcreteComponent: 具体被装饰的目标对象。
Decorator: 装饰类基类。
ConcreteDecoratorA: 具体的装饰类A
ConcreteDecoratorB: 具体的装饰类B
应用场景
实际需求
实现一款定制日志,包括日志打印增加时间戳、能够打印到终端和文件等功能。
分析
一个最基础的日志功能是将代码调试打印输出至指定文件,现需要在此基础上增加时间戳、终端显示功能。按照以往的写法,直接修改日志功能代码,可能会引起一下问题:
- 日志功能被引用过多,稍有不慎会引起编译问题。
- 日志功能以趋于稳定,直接修改,可能会增添潜藏Bug。
解决方案
引入装饰模式,在原有的日志的接口上封装一层修饰代码。这样就无需修改原先的日志代码,同时保证了新需求的开发。
日志类图
效果
客户端代码
代码语言:c 复制int main(int argc, char *argv[])
{
LOGI("main", "Welcome kaiyuan519!n");
LOGD("main", "Welcome kaiyuan519!n");
return 0;
}
终端打印
通过上图可以发现,不仅将客户端打印的字符串输出到同级目录中,同时增加了时间戳、终端显示功能。
源码
代码过长,本篇仅贴主要代码功能。可在公众号后台输入标题获取所有源码。
客户端代码
代码语言:c 复制int main(int argc, char *argv[])
{
LOGI("main", "Welcome kaiyuan519!n");
LOGD("main", "Welcome kaiyuan519!n");
return 0;
}
引用修饰类给日志增加新特性
代码语言:c 复制// 定制log:增加时间戳、终端打印
int sys_log(int level, const char *tag, const char *msg)
{
// 保存至文件
if (!pLog) {
pLog = CLog::GetInstance();
}
// 在终端显示
if (!pLog2Terminal) {
pLog2Terminal = CLog2Terminal::GetInstance(pLog);
}
// 增加log调试信息: 时间戳、等级、标签
if (!pLogAddInfo) {
pLogAddInfo = CLogAddInfo::GetInstance(pLog2Terminal);
}
// 完成装饰的接口
pLogAddInfo->SetLogLevel(level);
pLogAddInfo->WriteLog(tag, msg);
return 0;
}
日志增加信息代码
代码语言:c 复制int CLogAddInfo::WriteLog(const char *tag, const char *msg)
{
char logStr[LOG_STR_LENGTH] = {0}; // 一次log打印长度不得超过2000
int length = 0;
// log增加调试信息: time、level、Tag: 2022-01-14 10:09:51.106 I/ <main>
length = get_local_time(logStr);
snprintf(logStr length, LOG_STR_LENGTH - length, "%s <%s> %s", LEVEL_STR[levelIndex], tag, msg);
CCustomLog::WriteLog(tag, logStr);
return strlen(logStr);
}
输出至终端代码
代码语言:c 复制int CLog2Terminal::WriteLog(const char *tag, const char *msg)
{
// 输出至终端
fprintf(stdout, "%s", msg); // 添加新职能
return CCustomLog::WriteLog(tag, msg);
}
总结
- 装饰模式的实现方法,无非是将原本单一的功能,经过层层职能接口的封装,并将封装后的接口提供的客户端使用。由于职能接口与原生接口是相同的,所以客户端的使用代码无需改动。
- 装饰模式只是改变其外表的部分,原本的功能还是保留的。
- 在使用时,能够在不修改原有功能基础上,随意增加需要的功能。使用起来也比较灵活。
- 缺点在于,装饰模式对初始化要求较为严格,且代码难以理解。需求增多时,会导致增加许多的类。所以在使用时,需要谨慎对待新增的职能。