前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++设计模式 - 装饰模式

C++设计模式 - 装饰模式

原创
作者头像
开源519
修改2022-01-18 16:00:20
3110
修改2022-01-18 16:00:20
举报
文章被收录于专栏:开源519

装饰模式

装饰模式指的是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。 --百度百科

装饰者模式主要是为一个对象增加新的行为,效果与子类扩展父类类似,但实现方式与继承不同,且更加灵活。

装饰模式类图

装饰模式
装饰模式

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);
}

总结

  • 装饰模式的实现方法,无非是将原本单一的功能,经过层层职能接口的封装,并将封装后的接口提供的客户端使用。由于职能接口与原生接口是相同的,所以客户端的使用代码无需改动。
  • 装饰模式只是改变其外表的部分,原本的功能还是保留的。
  • 在使用时,能够在不修改原有功能基础上,随意增加需要的功能。使用起来也比较灵活。
  • 缺点在于,装饰模式对初始化要求较为严格,且代码难以理解。需求增多时,会导致增加许多的类。所以在使用时,需要谨慎对待新增的职能。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 装饰模式
  • 装饰模式类图
  • 应用场景
  • 效果
  • 源码
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档