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

设计模式学习2

代理模式:Proxy

  1. 动机

“增加一层间接层”是软件系统中对许多复杂问题的一种常见解决方案。在面向对象系统中,直接食用某些对象会带来很多问题,作为间接层的proxy对象便是解决这一问题的常见手段。

2.伪代码:

class ISubject{
public:virtual void process();
};//Proxy的设计
class SubjectProxy: public ISubject{public:virtual void process(){//对RealSubject的一种间接访问//....}
};class ClientApp{ISubject* subject;public:ClientApp(){subject=new SubjectProxy();}void DoTask(){//...subject->process();//....}
};

3.理解:

copy on write就是这个思想。看类图以及代码简单,但是它真正的实现可能会很复杂。

4.类图:

适配器:Adapter

  1. 动机

需要将一些现存的对象放在新的环境中应用,但是新环境要求的接口是这些现存对象所不能满足的。如何应对这种迁移的变化?如何既能利用现存对象的良好实现,同时又能满足新的应用环境的要求接口?

2.伪代码:

//目标接口(新接口)
class ITarget{
public:virtual void process()=0;
};//遗留接口(老接口)
class IAdaptee{
public:virtual void foo(int data)=0;virtual int bar()=0;
};//遗留类型
class OldClass: public IAdaptee{//....
};//对象适配器
class Adapter: public ITarget{ //继承
protected:IAdaptee* pAdaptee;//组合public:Adapter(IAdaptee* pAdaptee){this->pAdaptee=pAdaptee;}virtual void process(){int data=pAdaptee->bar();pAdaptee->foo(data);}
};//类适配器
class Adapter: public ITarget,protected OldClass{ //多继承                            
}int main(){IAdaptee* pAdaptee=new OldClass();ITarget* pTarget=new Adapter(pAdaptee);pTarget->process();}class stack{deqeue container;};class queue{deqeue container;};

3.类图:

中介者模式:Mediator

  1. 动机

会出现多个对象互相交互的情况,对象之间通常会维持一种复杂的引用关系,如果遇到一些需求的更改,这种直接的引用关系将会面临不断变化。

2.伪代码:

与facade相同,思想,无代码

3.理解:

Colleague和Mediator两个类之间相互依赖,Colleague之间不相互依赖,但是会通过Mediator间接依赖。把直接依赖关系变为间接依赖关系。facade用于解耦内部与外部之间的关系,中介者相当于解耦facade内部之间的关系。

4.类图:

状态变化

在组件变化构建过程中,某些对象的状态经常面临变化,如何对这些变化进行有效的管理?同时又维持高层模块的稳定?

状态模式:State

  1. 动机

某些对象的状态如果改变,其行为也会随之改变。如何在运行时根据对象的状态来透明的更改对象的行为?而不会为对象操作和状态转变之间引入紧耦合?

2.伪代码:

class NetworkState{public:NetworkState* pNext;virtual void Operation1()=0;virtual void Operation2()=0;virtual void Operation3()=0;virtual ~NetworkState(){}
};class OpenState :public NetworkState{static NetworkState* m_instance;
public:static NetworkState* getInstance(){if (m_instance == nullptr) {m_instance = new OpenState();}return m_instance;}void Operation1(){//**********pNext = CloseState::getInstance();}void Operation2(){//..........pNext = ConnectState::getInstance();}void Operation3(){//$$$$$$$$$$pNext = OpenState::getInstance();}};class CloseState:public NetworkState{ }
//...class NetworkProcessor{NetworkState* pState;public:NetworkProcessor(NetworkState* pState){this->pState = pState;}void Operation1(){//...pState->Operation1();pState = pState->pNext;//...}void Operation2(){//...pState->Operation2();pState = pState->pNext;//...}void Operation3(){//...pState->Operation3();pState = pState->pNext;//...}};

备忘录模式:Memento

  1. 动机

某些对象的状态在转化过程中,可能有某种需要,要求程序能够回溯到对象之前处于某个点时的状态。如何在不破坏封装性的前提下实现这个任务呢?

2.伪代码:


class Memento
{string state;//..
public:Memento(const string & s) : state(s) {}string getState() const { return state; }void setState(const string & s) { state = s; }
};class Originator
{string state;//....
public:Originator() {}Memento createMomento() {Memento m(state);return m;}void setMomento(const Memento & m) {state = m.getState();}
};int main()
{Originator orginator;//捕获对象状态,存储到备忘录Memento mem = orginator.createMomento();//... 改变orginator状态//从备忘录中恢复orginator.setMomento(mem);
}

3.理解:

备忘录就是存储一个类的状态快照,在需要的时候设置为备忘录中的状态。核心是隐藏信息。这个模式有些过时。其实就是对象序列化。

4.类图:

数据结构

一些组件在内部具有特定的数据结构,如果让客户程序依赖于这些特定的数据结构,将破坏组件的复用性。这个时候,将这些数据结构封装在内部,对外提供统一的接口,来实现与特定的数据结构无关的访问。

组合模式:Composite

  1. 动机

如何将客户代码与复杂的对象容器结构解耦?让对象容器自己实现自身的复杂结构,从而使得客户代码就像处理简单对象一样来处理复杂的对象容器?

2.伪代码:

#include <iostream>
#include <list>
#include <string>
#include <algorithm>using namespace std;class Component
{
public:virtual void process() = 0;virtual ~Component(){}
};//树节点
class Composite : public Component{string name;list<Component*> elements;
public:Composite(const string & s) : name(s) {}void add(Component* element) {elements.push_back(element);}void remove(Component* element){elements.remove(element);}void process(){//1. process current node//2. process leaf nodesfor (auto &e : elements)e->process(); //多态调用}
};//叶子节点
class Leaf : public Component{string name;
public:Leaf(string s) : name(s) {}void process(){//process current node}
};void Invoke(Component & c){//...c.process();//...
}int main()
{Composite root("root");Composite treeNode1("treeNode1");Composite treeNode2("treeNode2");Composite treeNode3("treeNode3");Composite treeNode4("treeNode4");Leaf leaf1("left1");Leaf leaf2("left2");root.add(&treeNode1);treeNode1.add(&treeNode2);treeNode2.add(&leaf1);root.add(&treeNode3);treeNode3.add(&treeNode4);treeNode4.add(&leaf2);process(root);process(leaf2);process(treeNode3);}

3.理解:

对象组合成树形结构。和装饰器有点像,不仅has-a,而且is-a。将一对多的关系替换成一对一的关系。

4.类图:

迭代器:Iterator

  1. 动机

提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。

2.伪代码:

template<typename T>
class Iterator
{
public:virtual void first() = 0;virtual void next() = 0;virtual bool isDone() const = 0;virtual T& current() = 0;
};template<typename T>
class MyCollection{public:Iterator<T> GetIterator(){//...}};template<typename T>
class CollectionIterator : public Iterator<T>{MyCollection<T> mc;
public:CollectionIterator(const MyCollection<T> & c): mc(c){ }void first() override {}void next() override {}bool isDone() const override{}T& current() override{}
};void MyAlgorithm()
{MyCollection<int> mc;Iterator<int> iter= mc.GetIterator();for (iter.first(); !iter.isDone(); iter.next()){cout << iter.current() << endl;}}

3.理解:

对C++来说,面向对象的迭代器已经过时了。STL中的迭代器是泛型编程思想。运行时绑定是没有编译时绑定效率高的。

4.类图:

职责链模式:Chain Of Responsibility

  1. 动机

一个请求可能有多个接收者,但是真正的接受者,也就是去处理这个请求的只有一个。这时,请求发送者与接受者的耦合可能出现变化脆弱的情况。

2.伪代码:

#include <iostream>
#include <string>using namespace std;enum class RequestType
{REQ_HANDLER1,REQ_HANDLER2,REQ_HANDLER3
};class Reqest
{string description;RequestType reqType;
public:Reqest(const string & desc, RequestType type) : description(desc), reqType(type) {}RequestType getReqType() const { return reqType; }const string& getDescription() const { return description; }
};class ChainHandler{ChainHandler *nextChain;void sendReqestToNextHandler(const Reqest & req){if (nextChain != nullptr)nextChain->handle(req);}
protected:virtual bool canHandleRequest(const Reqest & req) = 0;virtual void processRequest(const Reqest & req) = 0;
public:ChainHandler() { nextChain = nullptr; }void setNextChain(ChainHandler *next) { nextChain = next; }void handle(const Reqest & req){if (canHandleRequest(req))processRequest(req);elsesendReqestToNextHandler(req);}
};class Handler1 : public ChainHandler{
protected:bool canHandleRequest(const Reqest & req) override{return req.getReqType() == RequestType::REQ_HANDLER1;}void processRequest(const Reqest & req) override{cout << "Handler1 is handle reqest: " << req.getDescription() << endl;}
};class Handler2 : public ChainHandler{
protected:bool canHandleRequest(const Reqest & req) override{return req.getReqType() == RequestType::REQ_HANDLER2;}void processRequest(const Reqest & req) override{cout << "Handler2 is handle reqest: " << req.getDescription() << endl;}
};class Handler3 : public ChainHandler{
protected:bool canHandleRequest(const Reqest & req) override{return req.getReqType() == RequestType::REQ_HANDLER3;}void processRequest(const Reqest & req) override{cout << "Handler3 is handle reqest: " << req.getDescription() << endl;}
};int main(){Handler1 h1;Handler2 h2;Handler3 h3;h1.setNextChain(&h2);h2.setNextChain(&h3);Reqest req("process task ... ", RequestType::REQ_HANDLER3);h1.handle(req);return 0;
}

3.理解:

应用的不多。使得多个接收者都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理请求为止。

4.类图:

行为变化

在组件的构建过程中,组件行为的变化经常导致组件本身的剧烈变化。下面的模式将组件本身和组件的行为进行解耦

命令模式:Command

  1. 动机

如何将行为请求者和行为实现者解耦?

2.伪代码:

#include <iostream>
#include <vector>
#include <string>
using namespace std;class Command
{
public:virtual void execute() = 0;
};class ConcreteCommand1 : public Command
{string arg;
public:ConcreteCommand1(const string & a) : arg(a) {}void execute() override{cout<< "#1 process..."<<arg<<endl;}
};class ConcreteCommand2 : public Command
{string arg;
public:ConcreteCommand2(const string & a) : arg(a) {}void execute() override{cout<< "#2 process..."<<arg<<endl;}
};class MacroCommand : public Command
{vector<Command*> commands;
public:void addCommand(Command *c) { commands.push_back(c); }void execute() override{for (auto &c : commands){c->execute();}}
};int main()
{ConcreteCommand1 command1(receiver, "Arg ###");ConcreteCommand2 command2(receiver, "Arg $$$");MacroCommand macro;macro.addCommand(&command1);macro.addCommand(&command2);macro.execute();}

3.理解:

有点和仿函数思想类似。将一个请求封装成一个对象,从而使你可用不同的请求对客户进行参数化。

4.类图:

访问器模式:Visitor

  1. 动机

在软件构件的过程中,由于需求的改变,某些类层次结构中常常需要增加新的行为,如果直接在基类中作出修改,将会给子类带来繁重的变更负担,甚至破坏原有设计。

2.伪代码:

#include <iostream>
using namespace std;class Visitor;class Element
{
public:virtual void accept(Visitor& visitor) = 0; //第一次多态辨析virtual ~Element(){}
};class ElementA : public Element
{
public:void accept(Visitor &visitor) override {visitor.visitElementA(*this);}};class ElementB : public Element
{
public:void accept(Visitor &visitor) override {visitor.visitElementB(*this); //第二次多态辨析}};class Visitor{
public:virtual void visitElementA(ElementA& element) = 0;virtual void visitElementB(ElementB& element) = 0;virtual ~Visitor(){}
};//==================================//扩展1
class Visitor1 : public Visitor{
public:void visitElementA(ElementA& element) override{cout << "Visitor1 is processing ElementA" << endl;}void visitElementB(ElementB& element) override{cout << "Visitor1 is processing ElementB" << endl;}
};//扩展2
class Visitor2 : public Visitor{
public:void visitElementA(ElementA& element) override{cout << "Visitor2 is processing ElementA" << endl;}void visitElementB(ElementB& element) override{cout << "Visitor2 is processing ElementB" << endl;}
};int main()
{Visitor2 visitor;ElementB elementB;elementB.accept(visitor);// double dispatchElementA elementA;elementA.accept(visitor);return 0;
}

3.理解:

Visitor中要求具体的Element稳定,这个条件通常很难保证,这是这个模式的重大缺点。accept方法表示接受一个visitor,这个visitor能够为该类增添新的方法。一般不用,因为前提条件很苛刻。
Visitor通过双重分发来实现不更改Element层次结构的前提下,在运行时透明的为类层次结构上的各个类添加新的操作。
适用于:Element类层次结构稳定,而其中的操作确实频繁改动的。(设计思想:有动的,有不动的)

4.类图:

领域规则

某些领域,变化虽然频繁,但是可以抽象为某种规则。要给出该领域下的对于变化的一般性解决方案。

解析器模式:Interpreter

  1. 动机

如果特定领域的某一问题比较复杂,类似的结构不断出现,如果使用普通的编程方式来实现将会面临非常频繁的变化。在这种情况下,将特定领域的问题表达为某种语法规则下的句子,然后构建一个解释器来解释这样的句子,从而达到解决问题的目的。

2.伪代码:


#include <iostream>
#include <map>
#include <stack>using namespace std;class Expression {
public:virtual int interpreter(map<char, int> var)=0;virtual ~Expression(){}
};//变量表达式
class VarExpression: public Expression {char key;public:VarExpression(const char& key){this->key = key;}int interpreter(map<char, int> var) override {return var[key];}};//符号表达式
class SymbolExpression : public Expression {// 运算符左右两个参数
protected:Expression* left;Expression* right;public:SymbolExpression( Expression* left,  Expression* right):left(left),right(right){}};//加法运算
class AddExpression : public SymbolExpression {public:AddExpression(Expression* left, Expression* right):SymbolExpression(left,right){}int interpreter(map<char, int> var) override {return left->interpreter(var) + right->interpreter(var);}};//减法运算
class SubExpression : public SymbolExpression {public:SubExpression(Expression* left, Expression* right):SymbolExpression(left,right){}int interpreter(map<char, int> var) override {return left->interpreter(var) - right->interpreter(var);}};Expression*  analyse(string expStr) {stack<Expression*> expStack;Expression* left = nullptr;Expression* right = nullptr;for(int i=0; i<expStr.size(); i++){switch(expStr[i]){case '+':// 加法运算left = expStack.top();right = new VarExpression(expStr[++i]);expStack.push(new AddExpression(left, right));break;case '-':// 减法运算left = expStack.top();right = new VarExpression(expStr[++i]);expStack.push(new SubExpression(left, right));break;default:// 变量表达式expStack.push(new VarExpression(expStr[i]));}}Expression* expression = expStack.top();return expression;
}void release(Expression* expression){//释放表达式树的节点内存...
}int main(int argc, const char * argv[]) {string expStr = "a+b-c+d-e";map<char, int> var;var.insert(make_pair('a',5));var.insert(make_pair('b',2));var.insert(make_pair('c',1));var.insert(make_pair('d',6));var.insert(make_pair('e',10));Expression* expression= analyse(expStr);int result=expression->interpreter(var);cout<<result<<endl;release(expression);return 0;
}

3.理解:

可以理解为四则运算的运算树。使用场景为:业务规则频繁变化,且类似的结构重复出现,并且容易(简单简单,小而简单的问题)抽象为有规则的问题。

4.类图:

总结

  • 模版方法:用于制定一套模板,别人来用的时候按照模板实现
  • 策略模式:方便新增策略,只需新增一个子类
  • 观察者模式:方便通知消息
  • 装饰模式:方便拓展功能
  • 桥模式:用于桥接两个向不同纬度变化的类,这个桥,就是一个类中组合另外一个类
  • 工厂方法:创建对象解耦合
  • 抽象工厂:工厂的工厂,要生产一系列的产品,这一系列产品需要配套
  • 原型方法:有clone方法来完成对象创建
  • 构建者:分步骤构建一个对象,将表示和构建分离
  • 单件模式:保证在内存中一个类只存在一个实例
  • 享元模式:运用共享技术有效地支持大量细粒度的对象,大量大量!线程池
  • 门面模式:不管facade内部怎么变化,对于用户来说,接口都是一样的
  • 代理模式:就像VPN一样,间接控制。
  • 适配器:改变原始类接口,适配新类。比如STL中的stack
  • 中介者模式:把直接依赖关系变为间接依赖关系
  • 状态模式:将策略模式的策略改为状态
  • 备忘录模式:存储一个类的状态快照,在需要的时候设置为备忘录中的状态
  • 组合模式:对象组合成树形结构。将一对多的关系替换成一对一的关系
  • 迭代器:STL中的迭代器思想
  • 职责链:处理请求的对象连成一条链
  • 命令模式:将行为抽象为对象,有点和仿函数类似
  • 访问器模式:使用双重分发的机制,透明的添加新的操作
  • 解释器模式:四则运算树
     

参考文章:侯捷C++八部曲笔记(三、设计模式)_侯捷c++设计模式-CSDN博客

相关文章:

设计模式学习2

代理模式&#xff1a;Proxy 动机 “增加一层间接层”是软件系统中对许多复杂问题的一种常见解决方案。在面向对象系统中&#xff0c;直接食用某些对象会带来很多问题&#xff0c;作为间接层的proxy对象便是解决这一问题的常见手段。 2.伪代码&#xff1a; class ISubject{ pu…...

Rust:如何判断位置结构的JSON串的成员的数据类型

如何判断位置结构的JSON串的成员的数据类型&#xff0c;给一个Rust的例子&#xff0c;其中包含对数组的判断&#xff1f; 在Rust中&#xff0c;你可以使用serde_json库来处理JSON数据&#xff0c;并通过serde_json::Value类型的方法来判断JSON串中成员的数据类型。以下是一个示…...

Kafka(五)生产者

目录 Kafka生产者1 配置生产者bootstrap.serverskey.serializervalue.serializerclient.id""acksallbuffer.memory33554432(32MB)compression.typenonebatch.size16384(16KB)max.in.flight.requests.per.connection5max.request.size1048576(1MB)receive.buffer.byte…...

【Leetcode】242.有效的字母异位词

一、题目 1、题目描述 给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。 注意:若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t 互为字母异位词。 示例1: 输入: s = "anagram", t = "nagaram" 输出: true示例2: 输入: …...

【数据库原理】(16)关系数据理论的函数依赖

一.函数依赖的概念 函数依赖是关系数据库中核心的概念&#xff0c;它指的是在属性集之间存在的一种特定的关系。这种关系表明&#xff0c;一个属性集的值可以唯一确定另一个属性集的值。 属性子集&#xff1a;在关系模式中&#xff0c;X和Y可以是单个属性&#xff0c;也可以是…...

脆弱的SSL加密算法漏洞原理以及修复方法

漏洞名称&#xff1a;弱加密算法、脆弱的加密算法、脆弱的SSL加密算法、openssl的FREAK Attack漏洞 漏洞描述&#xff1a;脆弱的SSL加密算法&#xff0c;是一种常见的漏洞&#xff0c;且至今仍有大量软件支持低强度的加密协议&#xff0c;包括部分版本的openssl。其实&#xf…...

SVN迁移至GitLab,并附带历史提交记录(二)

与《SVN迁移至GitLab&#xff0c;并附带历史提交记录》用的 git svn clone不同&#xff0c;本文使用svn2git来迁移项目代码。 一、准备工作 安装Git环境&#xff0c;配置本地git账户信息&#xff1a; git config --global user.name "XXX" git config --global us…...

如何创建容器搭建节点

1.注册Discord账号 https://discord.com/这是登录网址: https://discord.com/ 2.点击startnow注册,用discord注册或者邮箱注册都可,然后登录tickhosting Tick Hosting这是登录网址:Tick Hosting 3.创建servers 4.点击你创建的servers,按照图中步骤进行...

微众区块链观察节点的架构和原理 | 科普时间

践行区块链公共精神&#xff0c;实现更好的公众开放与监督&#xff01;2023年12月&#xff0c;微众区块链观察节点正式面向公众开放接入功能。从开放日起&#xff0c;陆续有多个观察节点在各地运行&#xff0c;同步区块链数据&#xff0c;运行区块链浏览器观察检视数据&#xf…...

React Admin 前端脚手架之ant-design-pro

文章目录 一、React Admin 前端脚手架选型二、React Admin 前端脚手架之ant-design-pro三、ant-design-pro使用步骤四、调试主题五、常用总结(持续更新)EditableProTable组件 常用组件EditableProTable组件 编辑某行后,保存时候触发发送请求EditableProTable组件,添加记录提…...

向爬虫而生---Redis 基石篇1 <拓展str>

前言: 本来是基于scrapy-redis进行讲解的,需要拓展一下redis; 包含用法,设计,高并发,阻塞等; 要应用到爬虫开发中,这些基础理论我觉得还是有必要了解一下; 所以,新开一栏! 把redis这个环节系统补上,再转回去scrapy-redis才好深入; 正文: Redis是一种内存数据库&#xff0c…...

【野火i.MX6ULL开发板】利用microUSB线烧入Debian镜像

0、前言 烧入Debian镜像有两种方式&#xff1a;SD卡、USB SD卡&#xff1a;需要SD卡&#xff08;不是所有型号都可以&#xff0c;建议去了解了解&#xff09;、SD卡读卡器 USB&#xff1a;需要microUSB线 由于SD卡的网上资料很多了&#xff0c;又因为所需硬件&#xff08;SD卡…...

“我在大A炒自己”

嘻嘻嘻&#xff0c;大伙儿好像还挺喜欢我闲聊&#xff0c;今天太忙&#xff0c;没得空精进技术&#xff0c;那咱还是接着闲聊吧&#x1f602;&#x1f602; 看到标题点进来的各位大A真爱粉&#xff0c;请先收下我的崇高敬意&#xff01;&#xff01;别误会&#xff0c;标题说的…...

js 颜色转换,RGB颜色转换为16进制,16进制颜色转为RGB格式

颜色转换&#xff0c;RGB颜色转换为16进制,16进制颜色转为RGB格式&#xff0c;可以自己设置透明度。 //十六进制颜色值的正则表达式 var reg /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/; /*RGB颜色转换为16进制*/ String.prototype.colorHex function () {var that this;if (/^…...

uniapp中用户登录数据的存储方法探究

Hello大家好&#xff01;我是咕噜铁蛋&#xff01;作为一个博主&#xff0c;我们经常需要在应用程序中实现用户登录功能&#xff0c;并且需要将用户的登录数据进行存储&#xff0c;以便在多次使用应用程序时能够方便地获取用户信息。铁蛋通过科技手段帮大家收集整理了些知识&am…...

引导过程与服务控制

文章目录 一、Linux操作系统引导过程1、开机启动的完整过程1.1 开机自检&#xff08;BIOS&#xff09;1.2 MBR引导1.3 GRUB菜单1.4 加载内核&#xff08;kernel&#xff09;1.5 init进程初始化 2、系统初始化进程2.1 init进程2.2 systemdinit与systemd区别 3、Systemd单元类型4…...

《矩阵分析》笔记

来源&#xff1a;【《矩阵分析》期末速成 主讲人&#xff1a;苑长&#xff08;5小时冲上90&#xff09;】https://www.bilibili.com/video/BV1A24y1p76q?vd_sourcec4e1c57e5b6ca4824f87e74170ffa64d 这学期考矩阵论&#xff0c;使用教材是《矩阵论简明教程》&#xff0c;因为没…...

『App自动化测试之Appium应用篇』| Appium常用API及操作

『App自动化测试之Appium应用篇』| Appium常用API及操作 1 press_keycode1.1 键盘操作1.2 关于KeyCode1.3 press_keycode源码1.4 电话键相关1.5 控制键相关1.6 基本按键相关1.7 组合键相关1.8 符号键相关1.9 使用举例 2 swip方法2.1 swip说明2.2 swip使用方法2.3 使用示例 3 sc…...

VSCode搭建 .netcore 开发环境

一、MacOS 笔者笔记本电脑上安装的是macOS High Sierra(10.13)&#xff0c;想要尝试一下新版本的.netcore&#xff0c;之前系统是10.12时&#xff0c;.netcore 3.1刚出来时安装过3.1版本&#xff0c;很久没更新了&#xff0c;最近.net8出来了&#xff0c;想试一下&#xff0c;…...

python 写自动点击爬取数据

今天来点不一样的&#xff01;哥们 提示&#xff1a; 这里只是用于自己学习的 &#xff0c;请勿用违法地方 效果图 会进行点击下一页 进行抓取 需要其他操作也可以自己写 文章目录 今天来点不一样的&#xff01;哥们前言一、上代码&#xff1f;总结 前言 爬虫是指通过编程自动…...

CSDN博客重新更新

说来惭愧&#xff0c;好久没更新博客文章&#xff0c;导致个人博客网站&#xff1a;https://lenky.info/ 所在的网络空间和域名都过期了都没发觉&#xff0c;直到有个同事在Dim上问我我的个人博客为啥打不开了。。。幸好之前有做整站备份&#xff0c;后续慢慢把内容都迁回CSDN上…...

《剑指 Offer》专项突破版 - 面试题 5 : 单词长度的最大乘积(C++ 实现)

目录 前言 方法一 方法二 前言 题目链接&#xff1a;318. 最大单词长度乘积 - 力扣&#xff08;LeetCode&#xff09; 题目&#xff1a; 输入一个字符串数组 words&#xff0c;请计算不包含相同字符的两个字符串 words[i] 和 words[j] 的长度乘积的最大值。如果所有字符串…...

【Java集合篇】HashMap的get方法是如何实现的?

HashMap的get方法是如何实现的 ✔️典型解析✔️拓展知识仓✔️如何避免HashMap get方法的哈希重✔️HashMap get方法的优缺点有哪些✔️HashMap get方法的是线程安全的吗✔️什么是ConcurrentHashMap✔️ConcurrentHashMap有哪些应用场景✔️ConcurrentHashMap的优缺点 ✔️源…...

Java学习苦旅(二十二)——MapSet

本篇博客将详细讲解Map和Set。 文章目录 搜索概念模型 MapMap.Entry<K, V>Map的常用方法说明TreeMap和HashMap的区别 Set常用方法说明TreeSet和HashSet的区别 结尾 搜索 概念 Map和set是一种专门用来进行搜索的容器或者数据结构&#xff0c;其搜索的效率与其具体的实例…...

【Linux Shell】12. 文件包含

和其他语言一样&#xff0c;Shell 也可以包含外部脚本&#xff0c;这样可以很方便的封装一些公用的代码作为一个独立的文件。可以理解为在第2个文件中包含第1个文件&#xff0c;执行第1个文件的代码。 被包含的文件 不需要可执行权限 。Shell 文件包含的语法格式如下&#xff1…...

前端-基础 常用标签-超链接标签( 锚点链接 )

锚点链接 &#xff1a; 点击链接&#xff0c;可以快速定位到 页面中的某个位置 如果不好理解&#xff0c;讲一个例子&#xff0c;您就马上明白了 >>> 这个是 刘德华的百度百科 &#xff0c;可以看到&#xff0c;页面里面有很多内容&#xff0c;那就得有个目录了 …...

2024--Django平台开发-基础信息(一)

一、前置知识点 - Python环境搭建 (Python解释器、Pycharm、环境变量等) - 基础语法(条件、循环、输入输出、编码等) - 数据类型(整型、布尔型、字符串、列表、字典、元组、集合等) - 函数(文件操作、返回值、参数、作用域等) - 面向对象 (类、对象、封装、继承、多态等)包和模…...

C++力扣题目--94,144,145二叉树递归遍历

思路 这次我们要好好谈一谈递归&#xff0c;为什么很多同学看递归算法都是“一看就会&#xff0c;一写就废”。 主要是对递归不成体系&#xff0c;没有方法论&#xff0c;每次写递归算法 &#xff0c;都是靠玄学来写代码&#xff0c;代码能不能编过都靠运气。 本篇将介绍前后…...

开源游戏引擎:创造无限可能 | 开源专题 No.56

godotengine/godot Stars: 62.6k License: MIT Godot Engine 是一个功能强大的跨平台游戏引擎&#xff0c;可用于创建 2D 和 3D 游戏。它提供了一套全面的常见工具&#xff0c;让用户可以专注于制作游戏而不必重复造轮子。该引擎支持将游戏一键导出到多个平台上&#xff0c;包…...

MyBatisPlus学习一:快速入门

前言 前面快速学习了Mybatis&#xff0c;现在开始快速学习MyBatisPlus 学习教程&#xff1a; 黑马mybatis教程全套视频教程&#xff0c;2天Mybatis框架从入门到精通 黑马程序员最新MybatisPlus全套视频教程&#xff0c;4小时快速精通mybatis-plus框架 简介 MyBatisPlus 是…...

网站建设多少钱十年乐云seo/新闻联播直播 今天

写在前面 目前笔者的主业是vue写网页&#xff0c;闲来无事想玩玩node后端&#xff0c;在经过多重比较下&#xff0c;选择了阿里的eggjs框架&#xff0c;目前属于刚入坑状态&#xff0c;由于感觉官方的文档亲民性差很多&#xff0c;因此准备开始着手eggjs入坑指南&#xff0c;给…...

南昌做建网站的/谷歌推广怎么操作

问题引入&#xff1a; 在进行一个条件查询的时候&#xff0c;没太注意&#xff1d;&#xff1d; 与equal之间的区别&#xff0c;之间用&#xff1d;&#xff1d;来判断两个字符串是否相等&#xff0c;明明都是中文“全部”&#xff0c;却没有结果&#xff0c;于是开始调试 case…...

wordpress模板 免费下载/百度合伙人官网app

C语言排序原理分析&#xff0c;源于先找最大值和最小值。1&#xff1a;找最大值原理&#xff1a;假定第1个为最大的&#xff1b;然后往后面看&#xff0c;如果后面的元素更大&#xff0c;就把后面那个更大的给假定的这个地方&#xff0c;这样始终保证这个地方总是最大的值&…...

给公司做门户网站/nba季后赛最新排名

关于血瓶为什么输出100&#xff0c;个人认为参考步骤2&#xff1a;引用与如果一个变量是基本类型比如 int hp 50;我们就直接管hp叫变量表示赋值的意思。如果一个变量是类类型比如 Hero h new Hero();我们就管h叫做引用。不再是赋值的意思表示指向的意思比如 Hero h new Hero…...

天河企业网站建设/当前疫情十大热点

最近开发的东西需要在路由器上使用rabbitmq的客户端来连接rabbitmq消息中间件&#xff0c;路由器的系统是openwrt交叉编译环境&#xff0c;在这里记录一下整个过程。1.下载rabbitmq源码https://github.com/alanxz/rabbitmq-c2.下载完源码后&#xff0c;在普通的操作系统上实验一…...

电商网站服务排名/西安百度公司开户

最近一直在看wse3.0&#xff0c;从一个例子中偶然的收获。虽然通过后台操作&#xff0c;从而减少用户交互时的“僵硬”体验一直是每个程序员的追求&#xff0c;在今天这样ajax的时代里面更加显的重要。一切为了用户&#xff0c;一切为了更丰富愉快的体验。本文并不是ajax相关的…...