设计模式之装饰器模式(C++)

简介: 设计模式之装饰器模式(C++)

一、装饰器模式是什么?

      装饰器模式是一种结构型的软件设计模式,在不改变原类文件或使用继承的前提下,动态地扩展一个对象,进而达到增强或者增加对象功能的目的。


      装饰器模式的优点:


  1. 灵活性好。相比较继承,装饰模式扩展对象功能更加灵活。
  2. 扩展性好。不同装饰组合,可以创造出各式各样的对象,且避免了类爆炸。
  3. 满足设计模式要求的开闭原则和合成复用原则。
  4. 透明性好。客户端针对抽象操作,对具体实现的内容不可见。

     装饰器模式的缺点:


  1. 复杂性高。装饰模式的设计往往具备较高复杂度,对开发者的水平要求高。


二、装饰器模式

2.1 结构图

      客户端即Main主函数,装饰器和具体构件有共同接口-抽象构件,因此客户端可以给构件添加多层装饰;另外,装饰器中存放了一个抽象构件的指针,可以区分装饰前和装饰后的状态。

2.2 代码示例

      场景描述:我要炒菜,每个菜会有不同的调料添加方案(装饰)。

//Cooking.h
/****************************************************/
#pragma once
#include <iostream>
#include <list>
using namespace std;
// 抽象构件-烹饪
class Cooking
{
public:
  // 构造函数
  Cooking(string name = "") :m_name(name) {};
  // 析构函数
  virtual ~Cooking() {};
  // 获取名字
  virtual string getName() = 0;
protected:
  string m_name;
};
// 具体构件-红烧肉
class SoyBraisedPork :public Cooking
{
public:
  // 构造函数
  explicit SoyBraisedPork() :Cooking("红烧肉") {};
  // 析构函数
  virtual ~SoyBraisedPork() {};
  // 获取名字
  virtual string getName() {
    return m_name;
  }
};
//Seasoning.h
/****************************************************/
#pragma once
#include <iostream>
#include <list>
#include "Cooking.h"
using namespace std;
// 抽象调料
class Seasoning :public Cooking
{
public:
  // 构造函数
  Seasoning(Cooking *cooking){
    m_cooking = cooking;
  };
  // 析构函数
  virtual ~Seasoning() {
    cout << "开始析构。" << endl;
    if (m_cooking != nullptr) {
      cout << "地址:" << m_cooking << endl;
      delete m_cooking;
      m_cooking = nullptr;
    }
    cout << "结束析构。" << endl;
  };
protected:
  Cooking *m_cooking;
};
// 具体调料-盐
class Salt :public Seasoning
{
public:
  // 构造函数
  Salt(Cooking *cooking) :Seasoning(cooking) {};
  // 获取名字
  virtual string getName() {
    m_name = m_cooking->getName() + ",加盐";
    return m_name;
  }
};
// 具体调料-糖
class Sugar :public Seasoning
{
public:
  // 构造函数
  Sugar(Cooking *cooking) :Seasoning(cooking) {};
  // 获取名字
  virtual string getName() {
    m_name = m_cooking->getName() + ",加糖";
    return m_name;
  }
};
//main.cpp
/****************************************************/
#include <iostream>
#include <string>
#include "Seasoning.h"
using namespace std;
int main()
{
  Cooking *cook = new SoyBraisedPork();
  cout << "点菜:" << cook->getName() << endl;
  cout << "地址:" << cook << endl;
  cook = new Salt(cook);
  cout << "点菜:" << cook->getName() << endl;
  cout << "地址:" << cook << endl;
  cook = new Sugar(cook);
  cout << "点菜:" << cook->getName() << endl;
  cout << "地址:" << cook << endl;
  delete cook;
  cook = nullptr;
  return 0;
}

程序结果如下。

      有些同学可能会疑惑这种多层装饰的方法,会不会让new的内存没能释放掉。因此在上述示例中,我特地将每次装饰前的地址做了记录,用于复盘整个流程。其实只要在抽象装饰器中,对存放的抽象构件指针进行释放,即可确保没有内存泄漏。


      地址000001C529820910是Sugar后cook的地址,客户端中对它析构后,它释放的m_cooking其实和Salt同一内容,也就是000001C52981A3F0,所以析构先把这块释放了,释放它的时候,它里面也有一个m_cooking,这个就是起初红烧肉new出来的地址000001C529819510,因而又出现了开始析构的字样,红烧肉析构完就到底了,这样一套下来,你会发现所有new出来的内容都回收了。


      另外,上文介绍的装饰器模式也被成为透明装饰器,也是装饰器标准的样式,客户只操作抽象接口。除此之外,还有一种叫半透明式装饰器,它们的区别在于,半透明装饰器中某些行为是非常规的,这就使得它们无法用同一抽象接口进行迭代,进而无法进行多层装饰,所以在我个人看来,半透明装饰器不具备装饰器模式最特殊的多层装饰属性,文章也就不展开了,大家感兴趣的可以看看其他博主的文章。

三、总结

      我尽可能用较通俗的话语和直观的代码例程,来表述我对装饰器模式的理解,或许有考虑不周到的地方,如果你有不同看法欢迎评论区交流!希望我举的例子能帮助你更好地理解装饰器模式。

      如果文章帮助到你了,可以点个赞让我知道,我会很快乐~加油!

相关文章
|
7月前
|
设计模式 安全 测试技术
【C/C++ 设计模式 单例】单例模式的选择策略:何时使用,何时避免
【C/C++ 设计模式 单例】单例模式的选择策略:何时使用,何时避免
147 0
|
2月前
|
设计模式 XML Java
【设计模式】装饰器模式(定义 | 特点 | Demo入门讲解)
【设计模式】装饰器模式(定义 | 特点 | Demo入门讲解)
38 0
|
3天前
|
设计模式 前端开发 JavaScript
前端必须掌握的设计模式——装饰器模式
装饰器模式是一种结构型设计模式,通过创建新类来包装原始对象,实现在不修改原有结构的前提下扩展新行为。其核心在于“组合”思想,使新功能可“即插即拔”。该模式具有解耦性、灵活性和动态性等特点,广泛应用于类的面向对象编程语言中,如JavaScript的注解和TypeScript的写法。示例中,通过装饰器模式为游戏角色动态添加装备,展示了其强大的扩展性和灵活性。
|
7月前
|
设计模式 Java
Java一分钟之-设计模式:装饰器模式与代理模式
【5月更文挑战第17天】本文探讨了装饰器模式和代理模式,两者都是在不改变原有对象基础上添加新功能。装饰器模式用于动态扩展对象功能,但过度使用可能导致类数量过多;代理模式用于控制对象访问,可能引入额外性能开销。文中通过 Java 代码示例展示了两种模式的实现。理解并恰当运用这些模式能提升代码的可扩展性和可维护性。
69 1
|
3月前
|
设计模式 Java
Java设计模式-装饰器模式(10)
Java设计模式-装饰器模式(10)
|
5月前
|
设计模式 C++
C++一分钟之-设计模式:工厂模式与抽象工厂
【7月更文挑战第14天】设计模式是解决软件设计问题的通用方案。工厂模式与抽象工厂模式是创建型模式,用于对象创建而不暴露创建逻辑。工厂模式推迟实例化到子类,但过度使用会增加复杂性。抽象工厂则创建相关对象族,但过度抽象可能造成不必要的复杂度。两者均应按需使用,确保设计灵活性。代码示例展示了C++中如何实现这两种模式。
47 3
|
5月前
|
设计模式 安全 C++
C++一分钟之-C++中的设计模式:单例模式
【7月更文挑战第13天】单例模式确保类只有一个实例,提供全局访问。C++中的实现涉及线程安全和生命周期管理。基础实现使用静态成员,但在多线程环境下可能导致多个实例。为解决此问题,采用双重检查锁定和`std::mutex`保证安全。使用`std::unique_ptr`管理生命周期,防止析构异常和内存泄漏。理解和正确应用单例模式能提升软件的效率与可维护性。
65 2
|
7月前
|
设计模式 开发框架 算法
C++中的设计模式:基本概念与应用
C++中的设计模式:基本概念与应用
72 2
|
6月前
|
设计模式 Java
Java设计模式:深入装饰器模式的三种写法(六)
Java设计模式:深入装饰器模式的三种写法(六)
|
6月前
|
设计模式 架构师 安全
设计模式第五讲-装饰器模式和代理模式详解
远程代理,这种方式通常是为了隐藏目标对象存在于不同地址空间的事实,方便客户端访问。例如,用户申请某些网盘空间时,会在用户的文件系统中建立一个虚拟的硬盘,用户访问虚拟硬盘时实际访问的是网盘空间。
275 0

热门文章

最新文章

  • 1
    设计模式转型:从传统同步到Python协程异步编程的实践与思考
    59
  • 2
    C++一分钟之-设计模式:工厂模式与抽象工厂
    47
  • 3
    《手把手教你》系列基础篇(九十四)-java+ selenium自动化测试-框架设计基础-POM设计模式实现-下篇(详解教程)
    54
  • 4
    C++一分钟之-C++中的设计模式:单例模式
    65
  • 5
    《手把手教你》系列基础篇(九十三)-java+ selenium自动化测试-框架设计基础-POM设计模式实现-上篇(详解教程)
    43
  • 6
    《手把手教你》系列基础篇(九十二)-java+ selenium自动化测试-框架设计基础-POM设计模式简介(详解教程)
    70
  • 7
    Java面试题:结合设计模式与并发工具包实现高效缓存;多线程与内存管理优化实践;并发框架与设计模式在复杂系统中的应用
    62
  • 8
    Java面试题:设计模式在并发编程中的创新应用,Java内存管理与多线程工具类的综合应用,Java并发工具包与并发框架的创新应用
    43
  • 9
    Java面试题:如何使用设计模式优化多线程环境下的资源管理?Java内存模型与并发工具类的协同工作,描述ForkJoinPool的工作机制,并解释其在并行计算中的优势。如何根据任务特性调整线程池参数
    52
  • 10
    Java面试题:请列举三种常用的设计模式,并分别给出在Java中的应用场景?请分析Java内存管理中的主要问题,并提出相应的优化策略?请简述Java多线程编程中的常见问题,并给出解决方案
    121