当前位置: 首页 > news >正文

C++设计模式之装饰者模式(结构型模式)

学习软件设计,向OO高手迈进!
设计模式(Design pattern)是软件开发人员在软件开发过程中面临的一般问题的解决方案。
这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。
是前辈大神们留下的软件设计的"招式"或是"套路"。

什么是装饰者模式

动态地给一个对象添加一些额外的职责。就增加功能来说, 装饰者模式相比生成子类更为灵活。

有时我们希望给某个对象而不是整个类添加一些功能。比如有一个手机,允许你为手机添加特性,
比如增加挂件、屏幕贴膜等。一种灵活的设计方式是,将手机嵌入到另一对象中,由这个对象完成特性
的添加,我们称这个嵌入的对象为装饰。这个装饰与它所装饰的组件接口一致,因此它对使用该组件的
客户透明。

意图动态地给一个对象添加一些额外的职责
问题有时我们希望给某个对象而不是整个类添加一些功能。 即动态的为对象添加新的功能
解决方案采用组合而非单纯继承的方式,扩展一个对象的功能
实现创建一个原始抽象类和要添加类上的新功能。 让具体类装饰类分别继承自原始抽象类。 装饰类中包含原始抽象类的引用

UML类图

在这里插入图片描述

装饰者模式中的角色:

  1. 抽象构件(Component)角色 :定义一个抽象接口以规范准备接收附加责任的对象。
  2. 具体构件(ConcreteComponent)角色 :实现抽象构件,通过装饰角色为其添加一些职责。
  3. 抽象装饰(Decorator)角色 : 继承或实现抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
  4. 具体装饰(ConcreteDecorator)角色 :实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。

Version 1.0

下面我们使用买手机来讲解该模式:

我们在生活中去买手机,经常会有套餐的例子,比如,我们买一部手机祼机多少钱,外加一个耳机,构
成的套餐价,多少钱。外加一个保护膜,构成的另外套餐价多少钱,组合套餐呢?
这分明,是一个变化极为复杂的组合。

一、定义抽象类

Phone类:原始抽象类(Component)

class Phone {
public:
    virtual int cost() = 0;
};

二、定义具体类

IPhone类:具体类(ConcreteComponent)

class IPhone : public Phone {
public:
    int cost() {
        return 5000;
    }
};

三、定义抽象装饰类

DecoratePhone类:抽象装饰类(Decorator)

class DecoratePhone : public Phone {
public:
    DecoratePhone(Phone *ph) : m_Phone(ph) {}
protected:
    // 子类会用到这个成员, 所以声明为 protected
    Phone *m_Phone;
};

四、定义具体装饰类

ScreenProtectorPhone类:具体装饰类(ConcreteDecoratorA)

class ScreenProtectorPhone : public DecoratePhone {
public:
    ScreenProtectorPhone(Phone *ph) : DecoratePhone(ph) {}
    int cost() {
        return 39 + m_Phone->cost();
    }
};

HeadSetPhone类:具体装饰类(ConcreteDecoratorB)

class HeadSetPhone : public DecoratePhone {
public:
    HeadSetPhone(Phone *ph) : DecoratePhone(ph) {}
    int cost() {
        return 99 + m_Phone->cost();
    }
};

五、客户端

int main(int argc, char** argv) {
    // 单买手机 多少钱
    IPhone apple;
    cout << apple.cost() << endl;

    // 手机 + 钢化膜 多少钱
    ScreenProtectorPhone sp(&apple);
    cout << sp.cost() << endl;

    // 手机 + 钢化膜x2 多少钱
    ScreenProtectorPhone sp2(&sp);
    cout << sp2.cost() << endl;

    // 手机 + 钢化膜 + 耳机 多少钱
    HeadSetPhone hp(&sp);
    cout << hp.cost() << endl;

    // 手机 + 钢化膜x2 + 耳机x2 多少钱
    Phone *p = new ScreenProtectorPhone(new ScreenProtectorPhone(new HeadSetPhone(new HeadSetPhone(&apple))));
    cout << p->cost() << endl;

    return 0;
}

执行结果

5000
5039
5078
5138
5276

优点

  1. 装饰者模式可以带来比继承更加灵活的扩展功能,使用更加方便,可以通过组合不同的装饰者对象来获取具有不同行为状态的多样化的结果。
  2. 装饰者模式比继承更具良好的扩展性,完美的遵循开闭原则,继承是静态的附加责任,装饰者则是动态的附加责任。
  3. 装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。
  4. 通过使用不同的具体装饰类以及这些装饰类的排列组合,设计师可以创造出很多不同行为的组合。

缺点

  1. 这种比继承更加灵活机动的特性,也同时意味着更加多的复杂性。
  2. 装饰者模式会导致设计中出现许多小类,如果过度使用,会使程序变得很复杂。
  3. 装饰者模式是针对抽象组件(Component)类型编程。但是,如果你要针对具体组件编程时,就应该重新思考你的应用架构,以及装饰者是否合适。当然也可以改变Component接口,增加新的公开的行为,实现“半透明”的装饰者模式。在实际项目中要做出最佳选择。

适用场合

当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展和维护时。
不能采用继承的情况主要有两类:

  1. 第一类是系统中存在大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长;
  2. 第二类是因为类定义不能继承(如final类)

在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
当对象的功能要求可以动态地添加,也可以再动态地撤销时。

相关文章:

  • 数据结构中,索引存储和散列存储区别较为详细的介绍
  • 基于ssm+vue的邮票收藏鉴赏系统 elementui
  • 去中心化标志符在DID中的核心地位
  • C++设计模式之适配器模式(结构型模式)
  • 3-面试官:说说线程池的 7 大参数
  • 猿创征文|HCIE-Security Day50:网络攻击介绍
  • 一个基于NetCore开发的前后端分离CMS系统
  • centos7安装docker和docker-compose
  • 子查询与内联结分别应该怎么写?
  • Shell编程之第一讲——基础知识认识
  • Java-基于SSM的校园点餐管理系统
  • WLAN与WiFi各是什么意思有什么区别
  • Linux基础-常见问题 xrandr屏幕操作命令详解
  • Jenkins部署springboot项目至远程服务器
  • 商业化广告--体系学习-- 2 -- 行业蓝图篇 -- 广告产品与商业模式
  • 深入了解以太坊
  • [ 一起学React系列 -- 8 ] React中的文件上传
  • Fundebug计费标准解释:事件数是如何定义的?
  • Java 11 发布计划来了,已确定 3个 新特性!!
  • java多线程
  • Java面向对象及其三大特征
  • Redis 懒删除(lazy free)简史
  • Redis学习笔记 - pipline(流水线、管道)
  • Selenium实战教程系列(二)---元素定位
  • Wamp集成环境 添加PHP的新版本
  • 个人博客开发系列:评论功能之GitHub账号OAuth授权
  • 关于 Cirru Editor 存储格式
  • 基于HAProxy的高性能缓存服务器nuster
  • 算法之不定期更新(一)(2018-04-12)
  • 问题之ssh中Host key verification failed的解决
  • 项目实战-Api的解决方案
  • ​无人机石油管道巡检方案新亮点:灵活准确又高效
  • #HarmonyOS:Web组件的使用
  • #NOIP 2014#Day.2 T3 解方程
  • (8)STL算法之替换
  • (function(){})()的分步解析
  • (独孤九剑)--文件系统
  • (附源码)python旅游推荐系统 毕业设计 250623
  • (附源码)springboot 房产中介系统 毕业设计 312341
  • (附源码)springboot宠物管理系统 毕业设计 121654
  • (附源码)计算机毕业设计SSM智慧停车系统
  • (转)iOS字体
  • (转)memcache、redis缓存
  • ***监测系统的构建(chkrootkit )
  • *++p:p先自+,然后*p,最终为3 ++*p:先*p,即arr[0]=1,然后再++,最终为2 *p++:值为arr[0],即1,该语句执行完毕后,p指向arr[1]
  • .bat批处理(八):各种形式的变量%0、%i、%%i、var、%var%、!var!的含义和区别
  • .NET C# 使用 SetWindowsHookEx 监听鼠标或键盘消息以及此方法的坑
  • .NET Framework 4.6.2改进了WPF和安全性
  • .NET开发人员必知的八个网站
  • .NET正则基础之——正则委托
  • ?
  • @SpringBootApplication 包含的三个注解及其含义
  • [ C++ ] 继承
  • [ 云计算 | AWS 实践 ] 基于 Amazon S3 协议搭建个人云存储服务
  • [100天算法】-每个元音包含偶数次的最长子字符串(day 53)