设计模式之装饰器模式,以C++为例。

news2024/11/18 3:47:44

        昨天更新了适配器,今天来盘一盘装饰器模式。装饰器模式以一种动态的方式给对象添加额外的职责,好似“装饰”在对象身上一样,通常通过继承和委托来实现。

目录

 一、装饰器模式介绍

二、游戏人物如何使用装饰器模式

三、进阶写法(使用智能指针)

四、使用模板类


代码链接:

Thomas_Lbw / Cpp_Design_Patterns · GitCode

 一、装饰器模式介绍

        设计模式始终遵循对“修改封闭,拓展开放”这样的特性,说白了就是尽量不要动源代码,去尽力适应源代码的方式去做拓展开发。装饰器模式同样遵循这样的原则,实现装饰器模式的主要手段同样为 C++最基本的特性:继承、C++11新特性:委托

        以一个简单的代码例子:

#include<iostream>
using namespace std;

class Component {
public:
	virtual void Operation() = 0;
};


class ConcreteComponent : public Component
{
public:
	void Operation() override {
		std::cout << "ConcreteComponent Operation" << std::endl;
	}
};

class Decorator : public Component {
public:
	Decorator(Component *component) : component_(component){}

	void Operation() override {
		component_->Operation();
	}
private:
	Component *component_;
};

class ConcreteDecoratorA : public Decorator
{
public:
	//委托构造函数
	ConcreteDecoratorA(Component *component) : Decorator(component){}

	void Operation() override//帮助检查重写时的参数错误
	{
		Decorator::Operation();
		AddedBehavior();
	}
private:
	void AddedBehavior() {
		std::cout << "ConcreteDecoratorA Addedbehavior" << std::endl;
	}
};

class ConcreteDecoratorB : public Decorator
{
public:
	//委托构造函数
	ConcreteDecoratorB(Component *component) : Decorator(component) {}

	void Operation() override
	{
		Decorator::Operation();
		AddedBehavior();
	}
private:
	void AddedBehavior() {
		std::cout << "ConcreteDecoratorB Addedbehavior" << std::endl;
	}
};


int main()
{
	Component *component = new ConcreteComponent();
	component = new ConcreteDecoratorA(component);
	component = new ConcreteDecoratorB(component);
	component->Operation();

}

        我们在代码中定义了一个组件类Component,其中有一个纯虚函数Operation,可以在其中定义一组操作。具体的组件ConcreteComponent类继承自该组件,并实现了Operation。装饰器Decorator继承自该组件,并具有一个指向组件类的指针component_,它的Operation方法调用了component_指向的Operation方法。

        具体的装饰器ConcreteDecoratorA和ConcreteDecoratorB继承自Decorator,并且重写了Operation方法,在调用父类的Operation方法后,再调用自己的AddedBehavior方法。

        main函数中创建了具体的组件对象,然后使用装饰器A和B去装饰该对象,最后调用装饰后的Operation,实现对组件动态地添加新功能。

        如果在老的项目中,Compoent和ConcreteComponent为不能修改的代码,但是还需要向上添加新的功能,装饰器模式就非常nice了。

        代码类图如下:

二、游戏人物如何使用装饰器模式

        

#include <iostream>

class Character {
 public:
  virtual int getAttackPower() = 0;
};

class Warrior : public Character {
 public:
  int getAttackPower() { return 10; }
};

class CharacterDecorator : public Character {
 protected:
  Character *character;

 public:
  CharacterDecorator(Character *c) : character(c) {}
  int getAttackPower() { return character->getAttackPower(); }
};

class FireAttack : public CharacterDecorator {
 public:
  FireAttack(Character *c) : CharacterDecorator(c) {}
  int getAttackPower() { return character->getAttackPower() + 5; }
};

class IceAttack : public CharacterDecorator {
 public:
  IceAttack(Character *c) : CharacterDecorator(c) {}
  int getAttackPower() { return character->getAttackPower() - 3; }
};

int main() {
  Character *warrior = new Warrior();
  Character *fire_warrior = new FireAttack(warrior);
  Character *ice_warrior = new IceAttack(fire_warrior);

  std::cout << ice_warrior->getAttackPower() << std::endl;

  delete ice_warrior;
  return 0;
}

        在上面的代码中,首先定义了一个组件类 Character,并在 Warrior 类中实现了这个类。接着定义了一个装饰器类 CharacterDecorator,并在两个具体的装饰器类 FireAttack 和 IceAttack 中覆盖了 getAttackPower 方法。最后,在 main 函数中,创建了一个战士对象,并将其用火攻击和冰攻击装饰器包装,最后输出了人物的攻击力。

        类图:

三、进阶写法(使用智能指针)

#include <iostream>
#include <memory>

class Character {
public:
	virtual int getAttackPower() = 0;
};

class Warrior : public Character {
public:
	int getAttackPower() { return 10; }
};

class CharacterDecorator : public Character {
public:
	CharacterDecorator(std::shared_ptr<Character> c) : character(c) {}
	int getAttackPower() { return character->getAttackPower(); }

public:
	std::shared_ptr<Character> character;
};

class FireAttack : public CharacterDecorator {
public:
	FireAttack(std::shared_ptr<Character> c) : CharacterDecorator(c) {}
	int getAttackPower() { return character->getAttackPower() + 5; }
};

class IceAttack : public CharacterDecorator {
public:
	IceAttack(std::shared_ptr<Character> c) : CharacterDecorator(c) {}
	int getAttackPower() { return character->getAttackPower() - 3; }
};

int main() {
	std::shared_ptr<Character> warrior = std::make_shared<Warrior>();
	std::shared_ptr<Character> fire_warrior = std::make_shared<FireAttack>(warrior);
	std::shared_ptr<Character> ice_warrior = std::make_shared<IceAttack>(fire_warrior);

	std::cout << ice_warrior->getAttackPower() << std::endl;

	return 0;
}

        在上面的代码中,使用了 shared_ptr 而不是普通的指针来管理对象的生存期,这更加安全。代码中使用了 make_shared 函数来创建对象,而不是手动分配堆内存。这样做可以简化内存管理,提高代码的可读性。此外,由于组件类和装饰器类的关系建立在 shared_ptr 上,所以在 main 函数结束时不需要手动释放内存。

四、使用模板类

#include <iostream>
#include <memory>

template <typename T>
class Character {
public:
	virtual int getAttackPower() = 0;
};

class Warrior : public Character<Warrior> {
public:
	int getAttackPower() { return 10; }
};

template <typename T>
class CharacterDecorator : public Character<T> {
public:
	CharacterDecorator(std::shared_ptr<Character<T>> c) : character(c) {}
	int getAttackPower() { return character->getAttackPower(); }

public:
	std::shared_ptr<Character<T>> character;
};

class FireAttack : public CharacterDecorator<Warrior> {
public:
	FireAttack(std::shared_ptr<Character<Warrior>> c) : CharacterDecorator<Warrior>(c) {}
	int getAttackPower() { return character->getAttackPower() + 5; }
};

class IceAttack : public CharacterDecorator<Warrior> {
public:
	IceAttack(std::shared_ptr<Character<Warrior>> c) : CharacterDecorator<Warrior>(c) {}
	int getAttackPower() { return character->getAttackPower() - 3; }
};

int main() {
	std::shared_ptr<Character<Warrior>> warrior = std::make_shared<Warrior>();
	std::shared_ptr<Character<Warrior>> fire_warrior = std::make_shared<FireAttack>(warrior);
	std::shared_ptr<Character<Warrior>> ice_warrior = std::make_shared<IceAttack>(fire_warrior);

	std::cout << ice_warrior->getAttackPower() << std::endl;

	return 0;
}

        这个例子演示了如何使用模板类实现装饰器模式。通过使用模板类,我们可以更灵活地处理不同类型的人物,并且可以避免类型的重复定义。如果你需要更多类型的人物,你只需要定义一个新的类,并在装饰器类上进行模板特化即可。

        类图:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/195310.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

VScode ssh远程登陆到服务器阅读代码

1、背景介绍在工作中经常使用ssh远程访问服务阅读代码&#xff0c;但是通过ssh远程访问后没有图形界面&#xff0c;阅读代码非常不方便&#xff0c;本文向大家介绍使用VScode通过ssh远程登陆到服务器&#xff0c;本地可视化阅读查看服务器的代码文件。2、安装VS CodeVisual Stu…

Nginx优化

一.设置Nginx运行进程个数 1.查看cpu个数 grep processor /proc/cpuinfo 使用top 按1,也可以查看cpu的核心数 2.设置Nginx进程数 我的cpu数量是4,修改为4 vim /usr/local/nginx/conf/nginx.conf 二.Nginx运行CPU亲和力 vim /usr/local/nginx/conf/nginx.conf 4核4线程配置 注…

叠氮试剂1379690-01-3,3-Azido-D-alanine HCl,3-叠氮-D-丙氨酸HCl

产品规格&#xff1a;3-叠氮-D-丙氨酸HCl&#xff0c;3-Azido-D-alanine HCl1.CAS号&#xff1a;1379690-01-32.分子式&#xff1a;C3H7ClN4O23.分子量&#xff1a;166.566284.包装规格&#xff1a;1g&#xff0c;5g&#xff0c;10g&#xff0c;包装灵活&#xff0c;有100mg包装…

06-Servlet

目录1.Servlet 技术a)什么是 Servletb)手动实现 Servlet 程序&#xff01;这里要自己引入jar包&#xff0c;配置一下tomcat服务器c)url 地址到 Servlet 程序的访问d)Servlet 的生命周期e)GET 和 POST 请求的分发处理f) 通过继承 HttpServlet 实现Servlet 程序g)使用 IDEA 创建S…

小程序项目学习--第八章:播放页功能-代码重构-分包优化-打包发布

第八章:播放页功能-代码重构-分包优化-打包发布 01_(掌握)播放页-歌词分页的上下滚动区域 隐藏滚动条 .lyric-list ::-webkit-scrollbar {display: none; }歌词分页的上下滚动区域动态设置样式 思考什么时候是第一句歌词&#xff0c;什么时候是最后一句歌词 <swiper-ite…

嘉明的云计算与大数据学习之大数据综合实验案例

1.实验环境 (1)Linux:Ubuntu 16.04。 虚拟机镜像下载链接&#xff1a;https://pan.baidu.com/s/1i_B-2rAfPM53jf7Besi0tw 提取码&#xff1a;WZJM (2)MySQL:5.7.16。 (3)Hadoop:2.7.1。 (4)HBase:1.1.5。 (5)Hive:1.2.1。 (6)Sqoop:1.4.6。 (7)R:4.1.2。 (8)Eclipse:3.8。 2.…

Docklight Scripting模拟串行端口

Docklight Scripting模拟串行端口 Docklight是测试、分析和模拟串行端口(RS232、RS485/422等)的工具。此程序允许您监视和测试两个串行端口之间的连接。该程序环境简单&#xff0c;安装在所有Windows XP/7/8/10版本上。Docklight Scripting是一种带有内部编辑器的脚本语言&…

电子会议桌牌——网络版

产品特征&#xff1a; 低功耗&#xff0c;常规使用3-5年电池寿命支持空中唤醒&#xff0c;刷新快速&#xff0c;几秒钟内看到结果点阵电子纸屏幕&#xff0c;视角接近180基于Web的应用界面&#xff0c;支持跨平台操作安装简单&#xff0c;快速布置电池供电不需要布线双面显示…

Python你绝对不知道的15个小技巧,知道的算我输

名字&#xff1a;阿玥的小东东 学习&#xff1a;Python语言、正在学习c语言 主页&#xff1a;阿玥的小东东 目录 前言 1.all or any 2.bashplotlib 3.collections 4.dir 5.from __future__ import 6.python的优点 7.geopy 8.python2.x中的编码不是 unicode &#xff…

转行自学Java没后悔,我的经历证明,改变永远没有错

我在北京工作6年&#xff0c;现在年薪42万&#xff08;仅代表个人收益&#xff09;&#xff0c;目前在搜狐畅游工作&#xff0c;参与过轩辕剑的项目研发&#xff0c;有几个大型项目实战的经历&#xff0c;不过最后这几款项目都失败了。我之所以选择当初学习Java&#xff0c;就一…

我靠CSGO搬砖项目,一个月净赚过万

作为一名四线小城市的平凡上班族&#xff0c;今年已经满30岁了&#xff0c;工资到手也才6000多&#xff0c;已经不记得有多少年没涨过了。 后来通过CSGO搬运项目&#xff0c;每月副业收益稳定在5万的收益。 废话不多说&#xff0c;先给大家瞅瞅我上个月带货的收益&#xff0c…

为什么要上机械设备ERP系统?对企业管理有什么帮助?

机械设备制造行业是国民经济增长的重要支撑&#xff0c;随着信息化的发展&#xff0c;机械设备制造企业面临各种各样的挑战&#xff0c;频繁地出现部门监管力度不够、生产计划难定、产品质量不过关、生产过程操作不规范以及原材料使用不当等&#xff0c;造成资源浪费的现象&…

规则引擎设计与实现

规则引擎设计与实现 「字节跳动第五届青训营」笔记 是什么 规则引擎是一种嵌入在应用程序中的组件&#xff0c;实现了将业务决策从应用程序代码中分离出来&#xff0c;并 使用预定义的语义模块编写业务决策。接受数据输入&#xff0c;解释业务规则&#xff0c;并根据业务规则…

用最简单的方法实现个人网站-wordpress结合LAMP架构实战

文章目录前言一、LAMP架构是什么&#xff1f;1.1 LAMP架构简介1.2 什么是apache?1.3 什么是MySQL&#xff1f;1.4 什么是php&#xff1f;二. wordpress是什么三. 搭建博客过程1. 实验环境2. 搭建LAMP架构3. 设置数据库并授权4. 上传wordpress并解压5. 修改配置文件6. 网页端进…

基于C#制作一个网址检测工具

此文主要基于C#制作一个网址检测工具。随着时间推移以及公司人员的变动&#xff0c;手头所负责的项目也越来越多&#xff0c;对网站运行状况的管理也是一件让人十分头疼的事&#xff0c;秉持着偷懒是第一生产力的原则&#xff0c;制作一个工具对网址一键监测。 实现流程1、创建…

支付测试怎么做?要点有哪些?

目录 支付测试 避不开的 公司有没有支付牌照的问题&#xff1f; 国内有支付牌照的公司&#xff08;搞一张支付牌照 &#xff0c;不是钱多少的事&#xff09;总共 200 多家而已​编辑 没有支付牌照 公司就不可以支付么&#xff1f; 没有支付牌照 的公司的支付 做的什么支付&…

文档存储Elasticsearch系列--1 ES介绍

前言&#xff1a;Elasticsearch 也是使用 Java 编写的&#xff0c;它的内部使用 Lucene 做索引与搜索&#xff0c;支持结构化文档数据的分布式存储&#xff0c;并提供准实时的查询&#xff0c;全文检索&#xff0c;数据聚合&#xff1b; 1 为什么要使用ES: ES 本身存在哪些特性…

第二章---进程的描述与控制

&#x1f31e;欢迎来到操作系统的世界 &#x1f308;博客主页&#xff1a;卿云阁 &#x1f48c;欢迎关注&#x1f389;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; &#x1f31f;本文由卿云阁原创&#xff01; &#x1f64f;作者水平很有限&#xff0c;如果发现错误&#xff…

打印机之——G3800故障维修

一 故障现象 5B00/1471/1472打印机进空气二 解决办法 2.1 5B00/1471/1472 解决办法——不需要软件清零,按键解决 步骤1 插上电源&#xff0c;打印机处于未开机状态&#xff0c;挡板处放入打印纸同时按住“停止”键和“电源”键&#xff0c;此时“电源”键亮起松开“停止”键…

python-opencv第六期:addWeighted函数详解

概要&#xff1a; 众嗦粥汁所周知&#xff0c;在如今计算机视觉&#xff08;Computer Version short for CV&#xff09;是人工智能与机器人技术发展的一个重大研究方向&#xff0c;而opencv作为一个专门为机器视觉编程提供技术与函数支持的第三方库&#xff0c;自然是一个需要…