【设计模式】设计模式概述
😀大家好,我是白晨,一个不是很能熬夜😫,但是也想日更的人✈。如果喜欢这篇文章,点个赞👍,关注一下👀白晨吧!你的支持就是我最大的动力!💪💪💪
文章目录
- 前言
- 设计模式概述
- 一、设计模式的历史
- 二、理解面向对象
- 三、软件设计的复杂性
- 四、软件设计的目标
- 后记
前言
大家好,我是白晨。不知不觉已经🕊了很久了,白晨在这里给大家道个歉。
本次为大家带来的是全新的专栏——设计模式
,许多同学学习了面向对象的概念,但是面向对象在实际开发中到底好不好用、到底如何使用,这个问题可能很多人都无法回答。同时,在现在的面试中,设计模式被提及的概率越来越高,“你都使用过什么设计模式?”、“在xxx中都使用了哪些设计模式“,这些问题相信有过面试经历的人都会被问到,但是即使是一个编程的老鸟,做到准确无误的使用设计模式和理解设计模式也是非常困难的。
基于以上两点,白晨想详细介绍每种设计模式的设计思想(重点)、编程实现以及如何使用等,让大家可以准确理解设计模式的思想,力求做到通俗易懂。
本篇为引导篇
,在本篇中主要探讨一个问题:什么是设计模式
,我们将会从设计模式的历史出发,抽丝剥茧地分析设计模式
这个概念。
注:本系列文章不适合初学者,需要至少掌握一门面向对象语言(eg. C++\Java\Golang\C#…),并且本系列文章示例代码以C++为主,Java为辅(因为网络上的博客只要提到设计模式,一般都是Java,而C++的设计模式相对来说较少),本文章所有的源码都有C++和Java两个版本,源码见白晨的Gitee仓库,链接见下:
设计模式: 白晨博客专栏《设计模式》源码
设计模式概述
一、设计模式的历史
设计模式的概念最早起源于建筑领域。哈佛大学的建筑学博士克里斯托弗.亚历山大,是建筑学领域的模式之父。他与其研究团队用了约20年的时间,对住宅和周边环境进行了大量的调查研究,发现人们对舒适住宅和城市环境存在一些共同的认同规律,将它们归纳成253个模式。
所以,我们可以总结出
模式
的一个非常重要的特性:可复用是模式的精髓。在1994年,由Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides四人合著出版了一本名为《Design Patterns - Elements of Reusable Object-Oriented Software》(中文译名:设计模式 - 可复用的面向对象软件元素)的书,该书首次提到了软件开发中设计模式的概念。这四位作者合称GOF(四人帮,全拼 Gang of Four)。 他们所提出的设计模式主要是基于以下的面向对象设计原则:对接口编程而不是对实现编程,优先使用对象组合而不是继承。
![]()
总体来说,设计模式就是从大型软件架构出发、便于升级和维护的软件设计思想,它强调降低依赖,降低耦合。
虽然GoF距离现在已有近30年的历史,部分设计模式也因为种种原因被时代所抛弃,但是GoF中的大部分的设计模式依然被广泛的使用,书中所提出的23种经典面向对象,创立了模式在软件设计中的地位。
虽然GoF提出的设计模式被默认为“面向对象的设计模式”,但是这并不意味着“设计模式”就等同于“面向对象的设计模式”。
二、理解面向对象
面向对象编程的三大特性是:封装、继承和多态。这些特性是面向对象编程的基础,它们使得代码更加模块化、可重用和可维护。
封装
:封装是指将数据和基于数据的操作封装在一起,形成一个独立的实体。封装可以隐藏实现细节,使得代码模块化。良好的封装能够减少耦合,类内部的结构可以自由修改,可以对成员进行更精确的控制,隐藏信息,实现细节。
继承
:继承是使用已存在的类的定义作为基础建立新类的技术。通过使用继承我们能够非常方便地复用以前的代码,能够大大提高开发的效率。继承所描述的是“is-a”的关系,如果有两个对象A和B,若可以描述为“A是B”,则可以表示A继承B。
多态
:多态是指同一操作作用于不同的对象上面时,可以产生不同的解释和不同的执行结果。多态性分为编译时多态性和运行时多态性。编译时多态性又称为静态多态性,主要实现方式是函数重载和运算符重载;运行时多态性又称为动态多态性,主要实现方式是虚函数。
作为一名程序员我们应该懂得从编程底层,从微观角度去理解对象模型,常见的底层思维有:
- 语言构造
- 编译转换
- 内存模型
- 运行时机制
如果说向下的底层思维是一个程序员必修课,那么向上的抽象思维就是就是进化为一个优秀的程序员必备素养,什么是抽象思维呢?
简单地说,就是将我们周围的事物转化成为代码的能力。
举个例子,如果我们要将我们的学校抽象成代码,首先我们需要大致划分出学校中的人员:学生和老师;其次,要描述学校的建筑材料、建筑风格、具体布局;最后,要编写学校中的各种事件。
以上的过程,老师、学生这样的人员可以抽象为对象,学校的建材、建筑也可以抽象为对象,发生事件更不用说,为了方便管理和执行,得有个统一的对象模型将其组织起来。
对象抽象出来以后,要开始考虑如何实现这些对象,是每个对象都写一个对象模型,还是将其共同点提炼,具体实现一个对象时继承这个共同对象。
最后,要考虑代码的可维护性,因为学校总是要装修、扩建的,每年也都有毕业生和新生。如果代码设计不好,可维护性较差,就会出现牵一发而动全身的情况,明明是一点小改动,就要修改大部分代码,所以,选择合适的设计模式
非常重要。
程序员常见的抽象思维有:
- 面向对象
- 组件封装
- 设计模式
- 架构模式
对于程序员来说,底层思维和抽象思维相互依存,共同决定一个程序员的上限。向下,要能深入理解三大面向对象机制:
- 封装,隐藏内部实现
- 继承,复用现有代码
- 多态,改写对象行为
向上,深刻把握面向对象机制所带来的抽象意义,理解如何使用这些机制来表达现实世界,掌握什么是“好的面向对象设计” 。
三、软件设计的复杂性
建筑商从来不会去想给一栋已建好的100层高的楼房底下再新修一个小地下室——这样做花费极大而且注定要失败。然而令人惊奇的是,软件系统的用户在要求作出类似改变时却不会仔细考虑,而且他们认为这只是需要简单编程的事。——Object-Oriented Analysis and Designwith Applications
![]()
为什么软件设计会变得复杂?
根本原因是:
无论是什么原因引起的,但是只要用户有需求,就会有功能上的变化,一旦要满足相应的功能,我们的代码就得跟着变化。
所以,我们应该如何解决由变化引起的复杂性呢?
- 首先,我们应该将大问题进行分解,分解为一个个小问题,也就是我们在编程中经常使用的——分而治之,将复杂问题分解为独立的简单问题。
- 其次,我们应该从更高的维度去观察这个问题,将其这个问题抽象成一类问题,抓住其本质特征,构建一个理想化或者说泛化的模型,通过解决这个泛化模型的问题,我们解决一类问题,这也是一种复用逻辑。
所以,软件设计的复杂性的一般性的解决方法为分解
和抽象
。
下面我们通过一个demo来理解上面内容,假设我们要维护一个桌面绘图软件,原代码见下(Java版本原代码点击跳转):
MainForm.cpp
:
class MainForm : public Form { // MainForm类,继承自Form类
private:Point p1; // 鼠标按下时的点Point p2; // 鼠标抬起时的点vector<Line> lineVector; // 存储所有线段的向量vector<Rect> rectVector; // 存储所有矩形的向量public:MainForm(){ // 构造函数//...}
protected:virtual void OnMouseDown(const MouseEventArgs& e); // 鼠标按下事件处理函数virtual void OnMouseUp(const MouseEventArgs& e); // 鼠标抬起事件处理函数virtual void OnPaint(const PaintEventArgs& e); // 绘图事件处理函数
};void MainForm::OnMouseDown(const MouseEventArgs& e){ // 鼠标按下事件处理函数实现p1.x = e.X; // 记录鼠标按下时的x坐标p1.y = e.Y; // 记录鼠标按下时的y坐标//...Form::OnMouseDown(e); // 调用父类的鼠标按下事件处理函数
}void MainForm::OnMouseUp(const MouseEventArgs& e){ // 鼠标抬起事件处理函数实现p2.x = e.X; // 记录鼠标抬起时的x坐标p2.y = e.Y; // 记录鼠标抬起时的y坐标if (rdoLine.Checked){ // 如果选择了线段工具Line line(p1, p2); // 创建一个新的线段lineVector.push_back(line); // 将新线段添加到线段向量中}else if (rdoRect.Checked){ // 如果选择了矩形工具int width = abs(p2.x - p1.x); // 计算矩形宽度int height = abs(p2.y - p1.y); // 计算矩形高度Rect rect(p1, width, height); // 创建一个新的矩形rectVector.push_back(rect); // 将新矩形添加到矩形向量中}this->Refresh(); // 刷新窗体,触发绘图事件Form::OnMouseUp(e); // 调用父类的鼠标抬起事件处理函数
}void MainForm::OnPaint(const PaintEventArgs& e){ // 绘图事件处理函数实现for (int i = 0; i < lineVector.size(); i++){ // 遍历所有线段并绘制e.Graphics.DrawLine(Pens.Red,lineVector[i].start.x, lineVector[i].start.y,lineVector[i].end.x,lineVector[i].end.y);}for (int i = 0; i < rectVector.size(); i++){ // 遍历所有矩形并绘制e.Graphics.DrawRectangle(Pens.Red,rectVector[i].leftUp,rectVector[i].width,rectVector[i].height);}Form::OnPaint(); // 调用父类的绘图事件处理函数
}
Shape.h
:
// 点类定义,包含x和y两个属性
class Point{
public:int x;int y;
};// 直线类定义,包含起点和终点两个属性,并有构造函数初始化这两个属性
class Line{
public:Point start;Point end;// 直线类构造函数,初始化起点和终点属性Line(const Point& start, const Point& end){this->start = start;this->end = end;}
};// 矩形类定义,包含左上角点、宽度和高度三个属性,并有构造函数初始化这三个属性
class Rect{
public:Point leftUp;int width;int height;// 矩形类构造函数,初始化左上角点、宽度和高度属性Rect(const Point& leftUp, int width, int height){this->leftUp = leftUp;this->width = width;this->height = height;}
};
下面是这段代码的主要执行逻辑:
MainForm
类是主窗体类,继承自Form
类。它有两个私有成员变量p1
和p2
,分别用于存储鼠标按下和抬起时的坐标。它还有两个向量成员变量lineVector
和rectVector
,分别用于存储所有的线段和矩形。- 当鼠标按下时,会触发
OnMouseDown
事件处理函数。该函数会记录鼠标按下时的坐标(即点p1
)。 - 当鼠标抬起时,会触发
OnMouseUp
事件处理函数。该函数会记录鼠标抬起时的坐标(即点p2
),然后根据当前选择的工具(线段或矩形),创建相应的图形并添加到对应的向量中。 - 在鼠标抬起事件处理函数中,还会调用窗体的刷新方法(即
this->Refresh()
),这将触发绘图事件。 OnPaint
是绘图事件处理函数。当窗体刷新时,会遍历所有的线段和矩形,并在窗体上绘制出来。Point
,Line
,Rect
是一些基础的图形类。其中,点类包含x和y两个属性;直线类包含起点和终点两个属性,并有构造函数初始化这两个属性;矩形类包含左上角点、宽度和高度三个属性,并有构造函数初始化这三个属性。
现在要在原本绘制图形的基础上添加一个绘制“圆形”的功能,
- 首先,应该在
Shape.h
中添加一个圆类
:
- 其次,要修改
MainForm.cpp
中的MainForm
类,在其中添加一个存储所有圆形的数组:
- 再者,要修改
MainForm.cpp
中MainForm::OnMouseUp
函数,添加圆形工具的检测,如果选择工具为圆形,添加此圆形到数组:
- 最后,要修改
MainForm.cpp
中的MainForm::OnPaint
函数,添加圆形的绘制逻辑:
由上可见,我们为了添加一个圆形绘制功能,至少要修改四处地方,如果换成更加复杂的逻辑,其中牵扯的类和交互会变得更多,这就是由于变化
所引起的软件设计的复杂性。
所以,我们要来降低软件设计的复杂性,首先,我们想到的是分解
,但是分解面对现在的问题已经没有什么效果了,因为每个对象都已经解决一个小任务了,并且绘制功能也已经正常工作的,现在要面对的是维护和进一步开发的问题。
所以,我们现在需要抽象
,也即重新构筑代码结构,使代码变得可维护性高、可拓展性高。
现在来观察一下原本的代码有什么问题:
- 见下图,
MainForm
依赖了一个经常变化的Shape.h
,或者说MainForm
依赖了图形类的具体实现,这样设计是非常差劲的,一旦在Shape.h
添加新图像类,就必须更改MainForm
中的多个成员,导致MainForm
更改过于频繁,这是我们不想看到的,我们理想中的MainForm
应该是长期可以不用修改,如果我们要增添绘制功能,应该只用在Shape.h
中添加新类即可。

古代的大禹治水告诉了我们一个经验方法:堵不如疏
。对于变化,如果变化遍布多个类之间,那么一旦发生变化,连带的所有类都必须修改,但是,我们可以将变化疏导到个别类中,使得变化只能影响个别类,而不能对多个类造成影响。
观察现在的依赖,MainForm
是依赖于不稳定的Shape
的,如果要让MainForm
保持稳定,那么它必须依赖一个稳定的类。
什么类是稳定的呢?一种是长时间保持不变的类,另一种就是抽象类
。长时间保持稳定的类在我们这个demo中是没有了,但是我们可以给Shape.h
中的具体类抽象出一个父类Shape
,让具体图形类全部继承这个Shape
抽象类。
- 重构后的
Shape.h
:
class Shape{
public:virtual void Draw(const Graphics& g) = 0; virtual ~Shape() { }
};class Point{
public:int x;int y;
};class Line: public Shape{
public:Point start;Point end;Line(const Point& start, const Point& end){this->start = start;this->end = end;}//实现自己的Draw,负责画自己virtual void Draw(const Graphics& g){g.DrawLine(Pens.Red, start.x, start.y,end.x, end.y);}};class Rect: public Shape{
public:Point leftUp;int width;int height;Rect(const Point& leftUp, int width, int height){this->leftUp = leftUp;this->width = width;this->height = height;}//实现自己的Draw,负责画自己virtual void Draw(const Graphics& g){g.DrawRectangle(Pens.Red,leftUp,width,height);}};//增加
class Circle : public Shape{
public:point center; int radius; Circle(const Point& center, int radius){this->center = center;this->radius = radius;}//实现自己的Draw,负责画自己virtual void Draw(const Graphics& g){g.DrawCircle(Pens.Red,center,radius);}
};
再来重构MainForm
:
- 首先,将
MainForm
中的成员对象不能依赖具体类,应该依赖抽象类:

- 其次,
MainForm::OnPaint
不应该依赖于具体实现,应该让图像类自己绘制自己:

-
最后,还有
MainForm::OnMouseUp
依赖具体实现,如果要消除MainForm::OnMouseUp
中的变化,可以使用我们后续文章所讲到的其他设计模式,这里先按下不表。 -
重构后的
MainForm.cpp
:
class MainForm : public Form {
private:Point p1;Point p2;//针对所有形状vector<Shape*> shapeVector;
public:MainForm(){//...}
protected:virtual void OnMouseDown(const MouseEventArgs& e);virtual void OnMouseUp(const MouseEventArgs& e);virtual void OnPaint(const PaintEventArgs& e);
};void MainForm::OnMouseDown(const MouseEventArgs& e){p1.x = e.X;p1.y = e.Y;//...Form::OnMouseDown(e);
}// 通过后续的学习也可以消除其变化,这里先按下不表
void MainForm::OnMouseUp(const MouseEventArgs& e){p2.x = e.X;p2.y = e.Y;if (rdoLine.Checked){shapeVector.push_back(new Line(p1,p2));}else if (rdoRect.Checked){int width = abs(p2.x - p1.x);int height = abs(p2.y - p1.y);shapeVector.push_back(new Rect(p1, width, height));}//改变else if (rdoCircle.checked){//...int radius = (int)sqrt(pow(p2.x - p1.x, 2) + pow(p2.y - p1.y, 2));shapeVector.push_back(new Circle(p1, radius));}//...this->Refresh();Form::OnMouseUp(e);
}void MainForm::OnPaint(const PaintEventArgs& e){//针对所有形状for (int i = 0; i < shapeVector.size(); i++){shapeVector[i]->Draw(e.Graphics); //多态调用,各负其责}//...Form::OnPaint();
}
重构后的代码的依赖关系变为:
由上图可得,MainForm
和Line
都依赖于Shape
这个抽象类,这个关系是稳定的,变化被集中到Shape
的具体实现中,如果现在要添加一个绘制图像,只需要在Shape.h
中添加shape
的子类即可(这里假设MainForm::OnMouseUp
中的变化也被消除)。
这里要注意:变化依然存在,我们也不可能完全消除变化,我们能做的只能是将变化集中管理。
我们可以从上面的demo中总结两条原则:
- 高层模块(稳定)不应该依赖于低层模块(变化),二者都应该依赖于抽象(稳定) 。
MainForm
不应该依赖于变化的具体图形类,而应该依赖抽象的Shape
类,相应的,具体的图像类也应该依赖于抽象的Shape
类。
- 类模块应该是可扩展的,但是不可修改。
发生变化时,我们不应该过多的修改
MainForm
的具体实现细节,如果经常修改其实现细节,就得考虑代码的依赖关系是否出现了问题;同样的,Shape
的结构也应该是不可修改的,但是对于拓展,也即实现其子类应该是被允许的。
这两条原则非常重要,我们将会在下一篇文章中详细讲解设计模式的原则。
四、软件设计的目标
什么是好的软件设计?软件设计的金科玉律:
复用
正如我们前文提到的:可复用是模式的精髓,前人在面对软件设计的常见问题时,抽象出了适用于同类问题的通用模式,后人可以直接复用前人的模式进行设计,减少了试错的成本,加速了软件的开发,这就是可复用的优势。
所以,在这里我们可以给设计模式下个定义:
设计模式是可复用的经典解决方案,用于解决软件设计中的常见问题。
后记
在本文中,我们探讨了设计模式的历史、面向对象编程的基本概念、软件设计的复杂性以及软件设计的目标。设计模式作为一种强大的工具,可以帮助我们更好地应对日益复杂的软件开发挑战。
通过分解和抽象,我们可以降低软件设计的复杂性,提高代码的可维护性和可扩展性。设计模式的应用能够使我们的代码更具稳定性,减少耦合,提高代码的复用性,以更加高效和灵活的方式开发软件。
下一篇中,我们将着重探讨面向对象设计原则
,它是设计模式中最形而上的东西,也是所有设计模式所要遵守的金科玉律。
如果大家喜欢这个系列,还请大家多多支持啦😋!
如果这篇文章有帮到你,还请给我一个大拇指
👍和小星星
⭐️支持一下白晨吧!喜欢白晨【设计模式】系列的话,不如关注
👀白晨,以便看到最新更新哟!!!
我是不太能熬夜的白晨,我们下篇文章见。
- 参考资料
《C++设计模式》——李建忠
《HeadFirst设计模式(第二版)》
C++多态_drogon c+±CSDN博客
相关文章:

【设计模式】设计模式概述
😀大家好,我是白晨,一个不是很能熬夜😫,但是也想日更的人✈。如果喜欢这篇文章,点个赞👍,关注一下👀白晨吧!你的支持就是我最大的动力!Ǵ…...

第六届“中国法研杯”司法人工智能挑战赛进行中!
第六届“中国法研杯”司法人工智能挑战赛 赛题上新! 第六届“中国法研杯”司法人工智能挑战赛(LAIC2023)目前已发布司法大模型数据和服务集成调度 、证据推理、司法大数据征文比赛、案件要素识别四大任务。本届大赛中,“案件要素…...

关于 passing ‘const xx’ as ‘this’ argument of 的错误
今天在写一个简单的函数时,编译时出现了如下的错误: 这个很简单的函数是这样的: struct bundle_set {uint32_t baseId;uint32_t endId;bool operator< (const bundle_set &a){return baseId < a.baseId;} }; 在网上搜索到都是说什…...

数据结构和算法(13):优先级队列
概述 按照事先约定的优先级,可以始终高效查找并访问优先级最高数据项的数据结构,也统称作优先级队列 优先级队列将操作对象限定于当前的全局极值者。 根据数据对象之间相对优先级对其进行访问的方式,与此前的访问方式有着本质区别…...

面试经典150题——Day15
文章目录 一、题目二、题解 一、题目 135. Candy There are n children standing in a line. Each child is assigned a rating value given in the integer array ratings. You are giving candies to these children subjected to the following requirements: Each chil…...

web APIs——第一天(上)
变量声明的时候建议 const优先,尽量使用const 原因: const语义化更好很多变量我们声明的时候就知道他不会被更改了,那为什么不用const呢?实际开发中也是,比如react框架,基本const如果你有纠结的时候&…...

【Leetcode】215. 数组中的第K个最大元素
一、题目 1、题目描述 给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。 请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。 你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。 示例1: 输入: [3,2,1,5,6,4], k = 2 输出…...

服务器数据恢复-RAID5常见故障的数据恢复方案
raid5阵列常见故障: 1、服务器硬件故障或者RAID阵列卡故障; 2、服务器意外断电导致的磁盘阵列故障; 3、服务器RAID阵列阵列磁盘出现物理故障,如:电路板坏、磁头损坏、盘面划伤、坏扇区、固件坏等; 4、误操作…...

12个VIM编辑器的高级玩法
vim 是一个很好用的编辑器,应用十分广泛。但关于 vim,总有一些你不知道的事情,我们需要持续不断的学习。 我经常使用 vim,也经常在各大社区、论坛看到 vim 专家用户分享经验,今天我们就总结其中常用的一部分ÿ…...

⽜客论坛的笔记
项目描述: 一个基本功能完整的论坛项目。项目主要功能有: 基于邮件激活的注册方式,基于MD5加密与加盐的密码存储方式,登录功能加入了随机验证码的验证,实现登陆状态检查、为游客与已登录用户展示不同界面与功能。支持用户上传头像,…...

JS逆向分析某枝网的HMAC加密、wasm模块加密
这是我2022年学做JS逆向成功的例子,URL:(脱敏处理)aHR0cHM6Ly93d3cuZ2R0di5jbi9hdWRpb0NoYW5uZWxEZXRhaWwvOTE 逆向分析: 1、每次XHR的GET请求携带的headers包括: {"X-ITOUCHTV-Ca-Timestamp":…...

论坛介绍|COSCon'23开源商业(V)
众多开源爱好者翘首期盼的开源盛会:第八届中国开源年会(COSCon23)将于 10月28-29日在四川成都市高新区菁蓉汇举办。本次大会的主题是:“开源:川流不息、山海相映”!各位新老朋友们,欢迎到成都&a…...

Flink学习笔记(三):Flink四种执行图
文章目录 1、Graph 的概念2、Graph 的演变过程2.1、StreamGraph (数据流图)2.2、JobGraph (作业图)2.3、ExecutionGraph (执行图)2.4、Physical Graph (物理图) 1、Graph 的概念 Flink 中的执行图可以分成四层:StreamGraph -> JobGraph -> ExecutionGraph -&g…...

堆-----数据结构
引言 什么是堆?堆是一种特殊的数据结构(用数组表示的树)。 为什么要使用到堆?比如一场比赛,如果使用擂台赛的方式来决出冠军(实力第一),就很难知道实力第二的队伍是什么了。 但是…...

震撼登场 | 拓世科技集团新品亮相成为2023世界VR产业大会全场焦点
在当今世界,新一轮科技革命和产业变革蓬勃发展,虚拟现实作为这一浪潮中的代表性技术,伴随着5G商用及元宇宙概念的迅速兴起,已经成为推动数字经济发展和产业转型升级的关键技术,深刻地改变着人类的生产和生活方式。 10…...

后端接口的查询方式
在与前端对接过程中一直都会遇到一个问题,就是我们后端接口提供好了,自测也通过了,前端却说接口不通,当我们去排查时却发现大都不是接口不通,很多情况是前端使用的姿势不对,比如接口明明写的参数是放到ULR路…...

Maven首次安装配置
所有版本下载地址 http://archive.apache.org/dist/maven/ 配置环境变量 变量名: MAVEN_HOME 值: D:\apache-maven-3.9.5 Path:%MAVEN_HOME%\bin 是否安装成功 mvn -v 出现版本号就安装成功 配置本地仓库 也就是从服务器上下载的JAR包地址&a…...

使用html2canvas将html转pdf,由于table表的水平和竖直有滚动条导致显示不全(或者有空白)
结果: 业务:将页面右侧的table打印成想要的格式的pdf,首先遇到的问题是table表上下左右都有滚轮而html2canvas相当于屏幕截图,那滚动区域如何显示出来是个问题? gif有点模糊,但是大致功能可以看出 可复制…...

EDID详解
文章目录 字节含义一些概念YCC位 文章目录 字节含义一些概念YCC位 字节含义 EDID通常由128个字节组成,这些字节提供了关于显示器的各种详细信息。以下是EDID中每个字节位表示的一般含义: Header(头部): 字节0: Headerÿ…...

浅谈云原生
目录 1. 云原生是什么? 2. 云原生四要素 2.1 微服务 2.2 容器化 2.3 DevOps 2.4 持续交付 3. 具体的云原生技术有哪些? 3.1 容器 (Containers) 3.2 微服务 (Microservices) 3.3 服务网格 (Service Meshes) 3.4 不可变基础设施 (Immutable Inf…...

【K8S】Kubernetes
mesos apache基金会,后来是推特公司 mesos分布式资源管理框架2019淘汰 marathon 容器编排框架 用来调度、编排运行的常驻服务 mesos marathon 容器管理 k8s容器或云平台两种趋势(工资好) 1.K8s是什么 K8s全称为 Kubernetesÿ…...

面试题 01.01. 判定字符是否唯一
题目来源: leetcode题目,网址:面试题 01.01. 判定字符是否唯一 - 力扣(LeetCode) 解题思路: 遍历计数即可。 解题代码: class Solution { public:bool isUnique(string astr) {if(astr.l…...

C++(Qt)软件调试---linux使用dmesg定位程序崩溃位置(14)
C(Qt)软件调试—linux使用dmesg定位程序崩溃位置(14) 文章目录 C(Qt)软件调试---linux使用dmesg定位程序崩溃位置(14)1、前言2、ELF文件3、常用工具4、使用dmesg定位异常位置1.1 异常发生在可执行程序中1.2 异常发生在动态库中 1、…...

38 WEB漏洞-反序列化之PHPJAVA全解(下)
目录 Java中的API实现序列化和反序列化演示案例WebGoat_Javaweb靶场反序列化测试2020-网鼎杯-朱雀组-Web-think java真题复现 文章参考: https://www.cnblogs.com/zhengna/p/15737517.html https://blog.csdn.net/MCTSOG/article/details/123819548 ysoserial生成攻…...

LeetCode 面试题 10.10. 数字流的秩
文章目录 一、题目二、C# 题解 一、题目 假设你正在读取一串整数。每隔一段时间,你希望能找出数字 x 的秩(小于或等于 x 的值的个数)。请实现数据结构和算法来支持这些操作,也就是说: 实现 track(int x) 方法,每读入一个数字都会调…...

Vue3项目上线打包优化
之前整理过 Vue2项目上线打包优化,在vue3中,使用vite打包,配置稍微改了改。 1 开启gzip压缩 1.1 安装依赖 npm i vite-plugin-compression -D1.2 vite.config.ts 配置 import viteCompression from vite-plugin-compressionexport defaul…...

【算法题】2525. 根据规则将箱子分类
题目: 给你四个整数 length ,width ,height 和 mass ,分别表示一个箱子的三个维度和质量,请你返回一个表示箱子 类别 的字符串。 如果满足以下条件,那么箱子是 “Bulky” 的: 箱子 至少有一个…...

python字典
字典 字典定义创建字典 字典定义 字典是python语言中唯一的映射类型。这种映射类型由键(key)和值(value)组成,是“键值对”的无序可变序列 定义字典时,每个元组的键和值用冒号隔开,相邻元素用…...

thinkphp队列的使用?
1.安装队列依赖 由于框架版本原因可以选择适合的版本 composer require topthink/think-queue 由于我是tp框架5.1的,所以选择了think-queue 1.1.6 composer require topthink/think-queue 1.1.6 判断安装成功 php think queue:work -h image.png 2.配置文件…...