C++设计模式_06_Decorator 装饰模式
本篇将会介绍Decorator 装饰模式,它是属于一个新的类别,按照C++设计模式_03_模板方法Template Method中介绍的划分为“单一职责”模式
。
“单一职责”模式讲的是在软件组件的设计中,如果责任划分的不清晰,使用继承得到的结果往往是随着需求的变化,子类急剧膨胀,同时充斥着重复代码,这时候的关键是划清责任。
典型的模式包括:Decorator
和 Bridge
。这是因为这两种模式在责任的问题上表现得特别突出,但不意味着其他模式没有责任问题。
本篇主要介绍Decorator 装饰模式
文章目录
- 1. 代码演示Decorator 装饰模式
- 1.1 基于继承的常规思维处理
- 1.2 基于组合关系的重构优化
- 1.3 采用Decorator 装饰模式的实现
- 2. 动机(Motivation)
- 3. 模式定义
- 4. 结构(Structure)
- 5. 要点总结
- 6. 其他参考博文
1. 代码演示Decorator 装饰模式
首先结合实际场景和代码进行分析
设计场景:设计一些IO库,涉及一些流操作,针对流的操作,我们具有很多需求,比如文件流、网络流、内存流等,也有对流进行加密,进行缓存等操作。
1.1 基于继承的常规思维处理
首先我们可能会想到流的设计首先需要一个基类,基类有一些例如:Read()、Seek()、Write()等方法的公共操作,作为纯虚函数,放到Stream基类中。文件流FileStream继承自Stream基类,override Read()、Seek()、Write()等虚函数。网络流NetworkStream、内存流MemoryStream操作也是类似的
当我们进行加密操作时可以发现,首先我们需要对流有个主体的操作才能加密,我们对其中的文件流进行加密,首先去继承文件流FileStream,在Read()中进行额外的加密操作,调用基类的方法FileStream::Read(number);
,Seek()、Write()方法也是在其前面做额外的加密操作。
上述过程是对文件流进行加密操作,对网络流、内存流也有加密需求,也需要重复上面在文件流加密中的动作,我们可以发现加密操作是一样的,只是流的读取操作等不一样。
相应的对流的缓冲操作BufferedFileStream,也需要考虑文件流、网络流、内存流的操作,代码并未详细写
对文件流既加密又缓冲的双重操作CryptoBufferedFileStream,此处是继承了FileStream,当然有些朋友可以直接在这里继承一个CryptoFileStream也是可以的。此处进行额外的加密和缓冲操作。
//业务操作
class Stream{
public:virtual char Read(int number)=0;virtual void Seek(int position)=0;virtual void Write(char data)=0;virtual ~Stream(){}
};//主体类
class FileStream: public Stream{
public:virtual char Read(int number){//读文件流}virtual void Seek(int position){//定位文件流}virtual void Write(char data){//写文件流}};class NetworkStream :public Stream{
public:virtual char Read(int number){//读网络流}virtual void Seek(int position){//定位网络流}virtual void Write(char data){//写网络流}};class MemoryStream :public Stream{
public:virtual char Read(int number){//读内存流}virtual void Seek(int position){//定位内存流}virtual void Write(char data){//写内存流}};//扩展操作
class CryptoFileStream :public FileStream{
public:virtual char Read(int number){//额外的加密操作...FileStream::Read(number);//读文件流}virtual void Seek(int position){//额外的加密操作...FileStream::Seek(position);//定位文件流//额外的加密操作...}virtual void Write(byte data){//额外的加密操作...FileStream::Write(data);//写文件流//额外的加密操作...}
};class CryptoNetworkStream : :public NetworkStream{
public:virtual char Read(int number){//额外的加密操作...NetworkStream::Read(number);//读网络流}virtual void Seek(int position){//额外的加密操作...NetworkStream::Seek(position);//定位网络流//额外的加密操作...}virtual void Write(byte data){//额外的加密操作...NetworkStream::Write(data);//写网络流//额外的加密操作...}
};class CryptoMemoryStream : public MemoryStream{
public:virtual char Read(int number){//额外的加密操作...MemoryStream::Read(number);//读内存流}virtual void Seek(int position){//额外的加密操作...MemoryStream::Seek(position);//定位内存流//额外的加密操作...}virtual void Write(byte data){//额外的加密操作...MemoryStream::Write(data);//写内存流//额外的加密操作...}
};class BufferedFileStream : public FileStream{//...
};class BufferedNetworkStream : public NetworkStream{//...
};class BufferedMemoryStream : public MemoryStream{//...
}class CryptoBufferedFileStream :public FileStream{
public:virtual char Read(int number){//额外的加密操作...//额外的缓冲操作...FileStream::Read(number);//读文件流}virtual void Seek(int position){//额外的加密操作...//额外的缓冲操作...FileStream::Seek(position);//定位文件流//额外的加密操作...//额外的缓冲操作...}virtual void Write(byte data){//额外的加密操作...//额外的缓冲操作...FileStream::Write(data);//写文件流//额外的加密操作...//额外的缓冲操作...}
};void Process(){//编译时装配CryptoFileStream *fs1 = new CryptoFileStream();BufferedFileStream *fs2 = new BufferedFileStream();CryptoBufferedFileStream *fs3 =new CryptoBufferedFileStream();}
针对上述代码,我们来分析是否存在问题。
整理设计类图结构,Stream是基类,FileStream、NetworkStream、MemoryStream均继承自Syream,并对Read()、Seek()、Write()等进行了override,并且对各个基类进行了CryptoFileStream、BufferedFileStream和CryptoBufferedFileStream。
大家可以考虑下,当存在n个流类型的需求时,这个类的规模有多少呢?
Stream记作1
,FileStream、NetworkStream、MemoryStream…等n
种流类型,对于每个流类型有CryptoFileStream、BufferedFileStream和CryptoBufferedFileStream…等m
种操作。总共加起来的个数为:1+n+(m!/2)
,(很多人很容易想到其为1+nm,但实际在操作中会有既加密又等组合的情况,实际就是n(m+(m-1)+(m-2)…+1),数学中对应的就是m的阶乘除以2)。这个类的规模就会变得十分大。
再研究上面的代码,发现对于加密操作,不管是哪个类方法的加密方法,都是一样的,大量重复的代码就造成了代码冗余。
1.2 基于组合关系的重构优化
为了消除重复带来的代码冗余,对代码进行重构。
利用组合关系替代继承关系(设计原则中也有提到组合由于继承),在重构中的规则:当一个变量的声明类型都是某一个类型的子类的时候,那么在声明时就将其声明为该类型
例如上面代码转化组合关系时,从某一个类的FileStream* stream,改定义为Stream* stream;,利用多态在运行时定义为子类型既可以(实现方法:Stream* stream = new FileStream();),这也就使得编译时的东西变为运行时的东西,这里可以悟出一个设计模式的真谛就是“编译时一样,运行时不一样”
。
经过对代码的重构,就将class CryptoFileStream :public FileStream
、class CryptoNetworkStream : :public NetworkStream和class CryptoMemoryStream : public MemoryStream的3段重复性代码重构为以下的一段代码。
class CryptoStream: public Stream {Stream* stream;//...public:CryptoStream(Stream* stm):stream(stm){}virtual char Read(int number){//额外的加密操作...stream->Read(number);//读文件流}virtual void Seek(int position){//额外的加密操作...stream::Seek(position);//定位文件流//额外的加密操作...}virtual void Write(byte data){//额外的加密操作...stream::Write(data);//写文件流//额外的加密操作...}
};
以上代码中Read()、Seek()、Write()均采用虚函数的方式,这是因为遵循流的规范,才能使用虚函数,因此class CryptoStream需要继承public Stream,是完善Read()、Seek()、Write()虚函数的接口规范,也就是Stream基类定义了CryptoStream的接口规范。
class CryptoStream: public Stream {
virtual char Read(int number){...}
virtual void Seek(int position){...}
virtual void Write(byte data){...}
}
这时就有一个特别有意思的变化,既有一个Stream基类,又有Stream* stream
的字段。
同样的道理class BufferedStream
也可以重构为以下代码
class BufferedStream : public Stream{Stream* stream;//...public:BufferedStream(Stream* stm):stream(stm){}//...
};
写到此处,最初代码中的问题已经得到了极大的缓解,真正使用的时候。
void Process(){//运行时装配FileStream* s1=new FileStream();CryptoStream* s2=new CryptoStream(s1);BufferedStream* s3=new BufferedStream(s1);//既加密又缓存 BufferedStream* s4=new BufferedStream(s2);
}
编译时不存在加密文件流,缓存文件流的类,但是运行时可以通过组合的方式把他们装配起来满足需求。
这样最终的代码为:
//业务操作
class Stream{public:virtual char Read(int number)=0;virtual void Seek(int position)=0;virtual void Write(char data)=0;virtual ~Stream(){}
};//主体类
class FileStream: public Stream{
public:virtual char Read(int number){//读文件流}virtual void Seek(int position){//定位文件流}virtual void Write(char data){//写文件流}};class NetworkStream :public Stream{
public:virtual char Read(int number){//读网络流}virtual void Seek(int position){//定位网络流}virtual void Write(char data){//写网络流}};class MemoryStream :public Stream{
public:virtual char Read(int number){//读内存流}virtual void Seek(int position){//定位内存流}virtual void Write(char data){//写内存流}};//扩展操作class CryptoStream: public Stream {Stream* stream;//...public:CryptoStream(Stream* stm):stream(stm){}virtual char Read(int number){//额外的加密操作...stream->Read(number);//读文件流}virtual void Seek(int position){//额外的加密操作...stream::Seek(position);//定位文件流//额外的加密操作...}virtual void Write(byte data){//额外的加密操作...stream::Write(data);//写文件流//额外的加密操作...}
};class BufferedStream : public Stream{Stream* stream;//...public:BufferedStream(Stream* stm):stream(stm){}//...
};void Process(){//运行时装配FileStream* s1=new FileStream();CryptoStream* s2=new CryptoStream(s1);BufferedStream* s3=new BufferedStream(s1);BufferedStream* s4=new BufferedStream(s2);}
到这里问题已经解决的差不多了,这个版本在很多类库中也是比较常见的,这样其实已经OK了
1.3 采用Decorator 装饰模式的实现
如果根据马丁福勒经典意义上的重构理论,重构理论中还有一条:“如果某一个类它有多个子类具有同样的字段时,应该往上提”,Stream* stream;
如果放到class Stream
,但是在其子类class FileStream
并不需要这个字段,FileStream本身就是主体,并不需要Stream* stream字段。
这个时候怎么去做呢?此时需要一个中间类,这个中间类就是class DecoratorStream
。
class DecoratorStream: public Stream{
protected:Stream* stream;//...DecoratorStream(Stream * stm):stream(stm){}};
整体代码为:
//业务操作
class Stream{public:virtual char Read(int number)=0;virtual void Seek(int position)=0;virtual void Write(char data)=0;virtual ~Stream(){}
};//主体类
class FileStream: public Stream{
public:virtual char Read(int number){//读文件流}virtual void Seek(int position){//定位文件流}virtual void Write(char data){//写文件流}};class NetworkStream :public Stream{
public:virtual char Read(int number){//读网络流}virtual void Seek(int position){//定位网络流}virtual void Write(char data){//写网络流}};class MemoryStream :public Stream{
public:virtual char Read(int number){//读内存流}virtual void Seek(int position){//定位内存流}virtual void Write(char data){//写内存流}};//扩展操作class DecoratorStream: public Stream{
protected:Stream* stream;//...DecoratorStream(Stream * stm):stream(stm){}};class CryptoStream: public DecoratorStream {public:CryptoStream(Stream* stm):DecoratorStream(stm){}virtual char Read(int number){//额外的加密操作...stream->Read(number);//读文件流}virtual void Seek(int position){//额外的加密操作...stream::Seek(position);//定位文件流//额外的加密操作...}virtual void Write(byte data){//额外的加密操作...stream::Write(data);//写文件流//额外的加密操作...}
};class BufferedStream : public DecoratorStream{Stream* stream;//...public:BufferedStream(Stream* stm):DecoratorStream(stm){}//...
};void Process(){//运行时装配FileStream* s1=new FileStream();CryptoStream* s2=new CryptoStream(s1);BufferedStream* s3=new BufferedStream(s1);BufferedStream* s4=new BufferedStream(s2);}
梳理一下,就会发现很巧妙,FileStream、NetworkStream和MemoryStream始终是没有动的,他们是可以单独行驶行为的,但是加密流必须传一个流的对象。这些操作的本质上就是扩展,这就是Decorator 的含义,装饰是附着在其他上的操作。
Stream基类,FileStream、NetworkStream、MemoryStream作为Stream的子类也是不变,设计了DecoratorStream,CryptoStream和BufferedStream继承自DecoratorStream。这种情况下类的规模就是1+n+m
,对比前面1+n+(m!/2)
减少了很多。
对两者进行分析发现:出现1+n+(m!/2)
的规模是由于重复代码的多次使用,继承的不良使用
,我们现在想一下CryptoFileStream和BufferedFileStream一定要继承FileStream来完成加密和缓存操作吗?不是的,组合更好,class DecoratorStream: public Stream{ protected: Stream* stream;//... }
中的Stream* stream; 是设计的核心,就是用组合的方式,来引出未来多态的支持。
2. 动机(Motivation)
-
在某些情况下我们可能会
“过度地使用继承来扩展对象的功能”
,由于继承为类型引入的静态特质,使得这种扩展方式缺乏灵活性;并且随着子类的增多(扩展功能的增多),各种子类的组合(扩展功能的组合)会导致更多子类的膨胀。
例如:在decorator1.cpp
代码中,class CryptoFileStream中的FileStream::Read(number);就是静态特质,是没有变化的可能性的,这是由继承实现的;而在decorator3.cpp
代码中class CryptoStream中的stream->Read(number);中stream是基类的多态指针就是有变化的,这也就是动态特质,这是由组合实现的 -
如何使“对象功能的扩展”能够根据需要来动态地实现?同时避免“扩展功能的增多”带来的子类膨胀问题?从而使得任何“功能扩展变化”所导致的影响将为最低?
3. 模式定义
动态(组合
)地给一个对象增加一些额外的职责。就增加功能而言,Decorator模式比生成子类(继承
)更为灵活(消除重复代码 & 减少子类个数)。 ——《设计模式》GoF
4. 结构(Structure)
上图是《设计模式》GoF中定义的Decorator 装饰模式的设计结构。结合上面的代码看图中最重要的是看其中稳定和变化部分,也就是下图中红框和蓝框框选的部分。
5. 要点总结
-
通过采用组合而非继承的手法, Decorator模式实现了在
运行时
动态扩展对象功能的能力,而且可以根据需要扩展多个功能。避免了使用继承带来的“灵活性差”和“多子类衍生问题”。 -
Decorator类在接口上表现为
is-a
Component的继承关系,即Decorator类继承了Component类所具有的接口。但在实现上又表现为has-a
Component的组合关系,即Decorator类又使用了另外一个Component类。
这是一种同时继承与组合的模式,以后如果看到一个类,它的父类是一个类,例如父类是Stream,又有一个字段是Stream* stream,这个时候就高度怀疑是Decorator 装饰模式。有的时候看不到内部字段,至少可以从类的外部接口进行推测,父类是一个类,构造器的参数也是某一个类型,例如:class CryptoStream: public DecoratorStream {public:CryptoStream(Stream* stm):DecoratorStream(stm){ }}
- Decorator模式的目的并非解决“多子类衍生的多继承”问题,Decorator模式应用的要点在于解决“主体类在多个方向上的扩展功能”——是为“装饰”的含义。
6. 其他参考博文
Decorator 装饰模式
相关文章:
![](https://img-blog.csdnimg.cn/4360148a565a41adb3b73ada67667beb.png)
C++设计模式_06_Decorator 装饰模式
本篇将会介绍Decorator 装饰模式,它是属于一个新的类别,按照C设计模式_03_模板方法Template Method中介绍的划分为“单一职责”模式。 “单一职责”模式讲的是在软件组件的设计中,如果责任划分的不清晰,使用继承得到的结果往往是随…...
![](https://img-blog.csdnimg.cn/339df71e7a0d40d6a493564f6bf86189.png)
MySQL 8.0数据库主从搭建和问题处理
错误处理: 在从库通过start slave启动主从复制时出现报错 Last_IO_Error: error connecting to master slaveuser10.115.30.212:3306 - retry-time: 60 retries: 1 message: Authentication plugin caching_sha2_password reported error: Authentication require…...
![](https://img-blog.csdnimg.cn/img_convert/0e8592b365e001b9e920e50677ad7231.jpeg)
公众号迁移多久可以完成?
公众号账号迁移的作用是什么?只能变更主体吗?长期以来,由于部分公众号在注册时,主体不准确的历史原因,或者公众号主体发生合并、分立或业务调整等现实状况,在公众号登记主体不能对应实际运营人的情况下&…...
![](https://www.ngui.cc/images/no-images.jpg)
Spring Cloud Stream Kafka(3.2.2版本)使用
问题 正在尝试只用Spring Cloud Stream Kafka。 步骤 配置 spring:cloud:function:definition: project2Building stream:kafka:binder:brokers: xxxx:9002configuration:enable.auto.commit: falsesession.timeout.ms: 30000max.poll.records: 30allow.auto.create.top…...
![](https://www.ngui.cc/images/no-images.jpg)
8位微控制器上的轻量级SM2加密算法实现:C语言详细指南与完整代码解析
引言 在当今的数字化世界中,安全性是每个系统的核心。无论是智能家居、医疗设备还是工业自动化,每个设备都需要确保数据的安全性和完整性。对于许多应用来说,使用高级的微控制器或处理器可能是不切实际的,因为它们可能会增加成本…...
![](https://img-blog.csdnimg.cn/ad0b11caf57040f8bbefe2bd577bcea5.png)
neo4j下载安装配置步骤
目录 一、介绍 简介 Neo4j和JDK版本对应 二、下载 官网下载 直接获取 三、解压缩安装 四、配置环境变量 五、启动测试 一、介绍 简介 Neo4j是一款高性能的图数据库,专门用于存储和处理图形数据。它采用节点、关系和属性的图形结构,非常适用于…...
![](https://img-blog.csdnimg.cn/76328db329e24e15b9d916bfef2094cd.png)
【机组】计算机系统组成课程笔记 第二章 计算机中的信息表示
2.1 无符号数和有符号数 2.1.1 无符号数 没有符号的数,其实就是非负数。在计算机中用字节码表示,目前最常用的是八位和十六位的。 2.1.2 有符号数 将正负符号数字化,0代表 ,1代表 - ,并把代表符号的数字放在有效数…...
![](https://img-blog.csdnimg.cn/5c2623926be44410bd6e87696effdfeb.png)
指针笔试题详解
个人主页:点我进入主页 专栏分类:C语言初阶 C语言程序设计————KTV C语言小游戏 C语言进阶 C语言刷题 欢迎大家点赞,评论,收藏。 一起努力,一起奔赴大厂。 目录 1.前言 2.指针题写出下列程序的结…...
![](https://img-blog.csdnimg.cn/a51c6e1d7bda46e6a9d03d7ec3b5bc09.png)
MySQL 日志管理、备份与恢复
目录 1 数据备份的重要性 2 MySQL 日志管理 3 备份类型 3.1 数据备份的分类 3.2 备份方式比较 3.3 合理值区间 3.4 常见的备份方法 4 MySQL 完全备份与恢复 4.1 MySQL 完全备份 5 mysqldump 备份与恢复 5.1 MySQL 完全恢复 6 MySQL 增量备份与恢复 6.1 MySQL 增量…...
![](https://img-blog.csdnimg.cn/5ac4ee9f08a44eb38be64c2ce0af8931.png)
vtk- 数据类型(一) 三角链实例代码
三角链实例代码 #include <iostream> #include <string> #include <regex> #include "tuex.h" #include "vtkCylinderSource.h" #include "vtkPolyDataMapper.h" #include "vtkActor.h" #include "vtkRendere…...
![](https://img-blog.csdnimg.cn/4686a983c2694bf7a990f03b4a97839f.png)
Git大全
目录 一、Git概述 1.1Git简介 1.2Git工作流程图 1.3查看Git的版本 1.4 Git 使用前配置 1.5为常用指令配置别名(可选) 1.5.1打开用户目录,创建 .bashrc 文件 1.5.2在 .bashrc 文件中输入如下内容: 1.5.3打开gitBash,执行…...
![](https://www.ngui.cc/images/no-images.jpg)
Touch命令使用指南:创建、更新和修改文件时间戳
文章目录 教程:touch命令的使用指南一、介绍1.1 什么是touch命令?1.2 touch命令的作用1.3 touch命令的语法 二、基本用法2.1 创建新文件2.2 更新文件时间戳2.3 创建多个文件2.4 修改文件访问时间2.5 修改文件修改时间2.6 修改文件创建时间 三、高级用法3…...
![](https://www.ngui.cc/images/no-images.jpg)
Windows开启 10 Telnet
在Windows 10中,Telnet客户端默认是不安装的。要在Windows 10上使用Telnet客户端,您需要手动启用它。以下是启用Telnet客户端的步骤: 打开控制面板。您可以通过在开始菜单中搜索"控制面板"来找到它。在控制面板中,选择…...
![](https://www.ngui.cc/images/no-images.jpg)
高教杯数学建模A题程序设计要点与思路
2023 年是我最后一次参加 高教杯大学生数学建模竞赛 以后不会再参加了(大四参加意义不太,研究生有研究生的数学建模大赛) 很遗憾 由于各种原因 我们没有能够完成赛题2022 年 美赛 2022年 Mathor Cup 2022 年国赛 2022 亚太杯 2023年 美赛 202…...
![](https://img-blog.csdnimg.cn/d7565cfc032647bfb69e7971731a06a2.gif#pic_center)
Spring Boot的新篇章:探索2.0版的创新功能
文章目录 引言1. Spring Boot 2.0的响应式编程2. 自动配置的改进3. Spring Boot 2.0的嵌入式Web服务器4. Spring Boot 2.0的Actuator端点5. Spring Boot 2.0的Spring Data改进6. Spring Boot 2.0的安全性增强7. Spring Boot 2.0的监控和追踪8. Spring Boot 2.0的测试改进结论 &…...
![](https://img-blog.csdnimg.cn/f8403cacc6744791978da517d97f806d.png)
5、SpringBoot_热部署
六、热部署 1.热部署概述 概述:程序更改后,不需要重新启动服务器也能够实现动态更新 springboot 项目如何实现热部署? tomcat 已经内置到项目容器中了希望tomcat监听外部程序变化通过新建一个程序来监控你代码的变化 2.依赖导入 依赖 <…...
![](https://img-blog.csdnimg.cn/cb137f335bbd4820a77035b7415b8d92.png)
【kohya】训练自己的LoRA模型
文章目录 序言准备环境准备图片处理图片下载kohya_ss代码修改pyvenv.cfg启动界面访问地址生成字幕准备训练的文件夹配置训练参数开始训练遇到的问题: 序言 在把玩stable diffusion的webUI和comfyUI后,思考着自己也微调一个个性化风格的checkpoint、LyCO…...
![](https://img-blog.csdnimg.cn/71ab08e3e5ee4605b91a69c446153826.png)
[尚硅谷React笔记]——第1章 React简介
目录: 第1章 React简介 React的基本使用:虚拟DOM的两种创建方式: 使用jsx创建虚拟DOM使用js创建虚拟DOM(一般不用)虚拟DOM与真实DOM:React JSX:JSX练习:模块与组件、模块化与组件化的理解 模块组件模块化组件化 第1章 React简介 中文官网: …...
![](https://www.ngui.cc/images/no-images.jpg)
Debezium系列之:快照参数详解
Debezium系列之:快照参数详解 一、snapshot.select.statement.overrides二、min.row.count.to.stream.results三、snapshot.delay.ms四、snapshot.fetch.size五、snapshot.lock.timeout.ms六、incremental.snapshot.allow.schema.changes七、incremental.snapshot.chunk.size八…...
![](https://www.ngui.cc/images/no-images.jpg)
redis单机版搭建
title: “Redis单机版搭建” createTime: 2022-01-04T20:43:1108:00 updateTime: 2022-01-04T20:43:1108:00 draft: false author: “name” tags: [“redis”] categories: [“install”] description: “测试的” redis单机版搭建 安装环境 redis版本redis-5.0.7虚拟机系统…...
![](https://www.ngui.cc/images/no-images.jpg)
物联网边缘网关
物联网边缘网关 边缘网关的定义边缘网关的分类边缘计算网关平台相关产品有哪些 百度边缘计算平台(BIE)华为边缘计算平台(IEF)产品应用拓扑图产品价格区间...
![](https://www.ngui.cc/images/no-images.jpg)
docker部署springboot程序时遇到的network问题
对应问题,因为刚开始接触docker,所以问题可能比较简单,但是做个记录 1、启动一个springboot项目获取本地ip的时候获取到的是172.17.0.x这个ip;在使用一些注册中心,mq的时候又要表明自己的本机器ip的时候会比较头疼&…...
![](https://img-blog.csdnimg.cn/img_convert/73ca4b1f8f76744a5d544926b1323f24.png)
RASP hook插桩原理解析
javaagent技术,实现提前加载类字节码实现hook,插桩技术 javassist技术ASM字节码技术 像加载jar,有两种方式 premain启动前加载:每次变动jar包内容,都需要进行重启服务器利用java的动态attch加载原理,采用pr…...
![](https://www.ngui.cc/images/no-images.jpg)
Pygame中Sprite的使用方法6-5
3 碰撞检测 蓝色方块会随着鼠标移动,当碰到绿色方块时,则当前分数加1,当碰到红色方块时,当前分数减1。因为要随时进行碰撞检测,因此需要在while True循环中实现以下功能。 3.1 蓝色方块随鼠标移动 将蓝色方块的位置…...
![](https://img-blog.csdnimg.cn/7ef9f81898134c3ab1255554c8824362.png)
浅谈为什么多态只能是指针或引用
其实在很早之前,我一直没有注意到这个问题,直到今天碰见了一道题,顺便前面的博客中,继承写到,子类中不包含父类,子类只是继承了父类的成员变量和函数,由这一点,引发了我对切片以及赋…...
![](https://www.ngui.cc/images/no-images.jpg)
js看代码说输出
目录 原型 Function与Object new fn() 原型链 constructor function.length 默认参数:第一个具有默认值之前的参数个数 剩余参数:不算进length 闭包 循环中 函数工厂:形参传递 IIFE:匿名闭包 let:闭包 forEach()&am…...
![](https://www.ngui.cc/images/no-images.jpg)
Java笔记:使用javassist修改class文件内方法
1.前言 在工作突然有一个需求。线上运维的一个tomcat的web项目,运行的程序不正常。需要修改代码。可是这个项目代码非常的老,并且公司存储的源代码跟线上的不一致。 我了个擦,没有源代码但是还要结局客户的问题。只能到线上将对应程序的clas…...
![](https://img-blog.csdnimg.cn/37300bd5eefe4ec3a8b13d759333dcaf.png)
华为云云耀云服务器L实例评测 |云服务器性能评测
通过上一篇文章华为云云耀云服务器 L 实例评测 |云服务器选购,我已经购买了一台 Centos 系统的云耀云服务器 L 实例。 在获得云耀云服务器 L 实例后,首要任务是熟悉云耀云服务器 L 实例的性能,对云耀云服务器 L 实例的性能进行测…...
![](https://img-blog.csdnimg.cn/7a299db3cae84b6194c514f021d31d71.png)
iphone的safari浏览器实现全屏的pwa模式,并修改顶部状态栏背景颜色
要想修改顶部背景颜色,需要用到这个属性:content就是你要设置的颜色 <!-- 状态栏的背景色 --><meta name"theme-color" content"#f8f8f8" /> 然后再加上下面的设置: <!-- 网站开启对 web app 程序的支持…...
![](https://img-blog.csdnimg.cn/81b31a89f4394372b762d66ed627c1e1.png)
springboot对接rabbitmq并且实现动态创建队列和消费
背景 1、对接多个节点上的MQ(如master-MQ,slave-MQ),若读者需要自己模拟出两个MQ,可以部署多个VM然后参考 docker 安装rabbitmq_Steven-Russell的博客-CSDN博客 2、队列名称不是固定的,需要接受外部参数&…...
![](https://img-blog.csdnimg.cn/81b45e101b554ef5940a6bc272633342.png)
同一个ip的网站做链接有用/一个平台怎么推广
随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,校园跑腿系统被用户普遍使用,为方便用户能够可以随时进行校园跑腿系统的数据信息管理,特开发了基于校园跑腿系统的管理系统。…...
![](https://img-blog.csdnimg.cn/20210317165242993.png)
wordpress未登录用户重定向/站长网站大全
重启nginx命令失败解决 查询Nginx安装目录 whereis nginx进入Nginx的sbin目录 cd /usr/sbin/重启nginx命令 ./nginx -s reload...
中心网站建设/青岛神马排名优化
LeetCode1.两数之和JavaScript 给定一个整数数组和一个目标值,找出数组中和为目标值的两个数。你可以假设每个输入只对应一种答案,且同样的元素不能被重复利用。实例: 给定 nums [2, 7, 11, 15], target 9因为 nums[0] nums[1] 2 7 9所…...
![](https://img-blog.csdnimg.cn/20200510174445458.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQwNDE3MDcw,size_16,color_FFFFFF,t_70)
wordpress三级导航菜单调用/sem和seo是什么职业岗位
1.为什么选择 NASA下载数据? 在遥感研究中,有一个不得不提到的传感器,那就是Modis ,Modis的数据在国内可以下载的到,但是数据更新的比较慢,比如地理空间数据云的遥感产品数据,想要获取到最新的…...
![](/images/no-images.jpg)
网站制作建设有哪些/软文客
写在最前:该篇文章还未完成,待进一步更新...。。一、初始 MycatMHALVS1、为什么选择 Mycat?互联网的高速发展使分布式技术兴起,当系统的压力越来越大时,人们首先想到的解决方案就是想上扩展(scale up),简单…...
![](/images/no-images.jpg)
网站开发功能简介/定制网站开发
http://codeforces.com/contest/278/problem/B /**题目:字符串数目n 1≤n≤30,每个字符串的长度不超过20 *含有两个字符的字符串有676种,大于600,*所以不是它的子串的字符串在两位中就产生了 *多谢大牛指点。。。*/#include <i…...