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

【设计模式——学习笔记】23种设计模式——访问者模式Visitor(原理讲解+应用场景介绍+案例介绍+Java代码实现)

文章目录

  • 案例引入
    • 要求
    • 传统方案
  • 介绍
    • 基本介绍
    • 应用场景
    • 登场角色
      • 尚硅谷版本
      • 《图解设计模式》版本
  • 案例实现
    • 案例一
      • 实现
      • 拓展
    • 案例二(个人感觉这个案例较好)
      • 实现
      • 分析
      • 拓展一
      • 拓展二
      • 拓展三
  • 总结
  • 额外知识
    • 双重分发
  • 文章说明

案例引入

要求

测评系统需求:将观众分为男人和女人,对歌手进行测评,当看完某个歌手表演后,得到他们对该歌手不同的评价(比如 成功、失败 等)

传统方案

在这里插入图片描述

Man和Woman里面都有“成功”、“失败”的方法

【分析】

  • 如果系统比较小,这样设置是可以的,但是考虑系统增加越来越多新的功能时,对代码改动较大(如需要增加一个新的评价方式,就需要在Man和Woman类中同时添加),违反了ocp原则,不利于维护
  • 扩展性不好,比如增加了新的人员类型,或者增加新的评价,都需要修改很多代码

【改进】

使用访问者模式

介绍

基本介绍

  • 在数据结构中保存着许多元素,我们会对这些元素进行“处理”。这时,“处理”代码放在哪里比较好呢?通常的做法是将它们放在表示数据结构的类中。但是,如果“处理”有许多种呢?这种情况下,每当增加一种处理,我们就不得不去修改表示数据结构的类。在Visitor模式中,数据结构与处理被分离开来。我们编写一个表示“访问者”的类来访问数据结构中的元素,并把对各元素的处理交给访问者类。这样,当需要增加新的处理时,我们只需要编写新的访问者,然后让数据结构可以接受访问者的访问即可
  • 即访问者模式主要将数据结构数据操作分离,解决数据结构和操作耦合性问题
  • 访问者模式的基本工作原理是: 在被访问的类里面提供一个对外接待访问者的接口

应用场景

  • 需要对一个对象结构中的对象进行很多不同操作(而且这些操作彼此没有关联),需要避免让这些操作"污染"这些对象的类,可以选用访问者模式解决

登场角色

在这里插入图片描述

  • Visitor :是抽象访问者,为该对象结构中的ConcreteElement的每一个类声明一个visit方法
  • ConcreteVisitor:是一个具体的访问者,实现Visitor声明的每个方法
  • ObjectStructure:能枚举它的元素,可以提供一个高层的接口,用来允许访问者访问元素(比如案例一的ObjectStructure类的display方法)
  • Element:定义一个accept 方法,接收一个访问者对象
  • ConcreteElement:为具体元素,实现了accept 方法

尚硅谷版本

《图解设计模式》版本

在这里插入图片描述

  • Visitor(访问者):Visitor角色负责对数据结构中每个具体的元素(ConcreteElement角色)声明一个用于访问XXXXX的visit(XXXXX)方法。visit(XXXXX)是用于处理XXXXX的方法负责实现该方法的是ConcreteVisitor角色
  • ConcreteVisitor(具体的访问者):ConcreteVisitor角色负责实现 Visitor角色所定义的接口(API)。它要实现所有的visit(XXXXX)方法,即实现如何处理每个ConcreteElement角色
  • Element(元素):Element角色表示Visitor角色的访问对象。它声明了接受访问者的accept方法。accept 方法接收到的参数是Visitor角色
  • ConcreteElement(具体元素):ConcreteElement角色负责实现Element角色所定义的接口(API)
  • ObjectStructure(对象数据结构):ObjectStructur角色负责处理Element角色的集合,能够枚举它的元素(案例二的Directory类同时扮演该角色和ConcreteElement角色)

案例实现

案例一

在这里插入图片描述

实现

【Action(Visitor)】

package com.atguigu.visitor;public abstract class Action {/*** 得到男性 的测评* @param man*/public abstract void getManResult(Man man);/*** 得到女性 的测评* @param woman*/public abstract void getWomanResult(Woman woman);
}

【Success(ConcreteVisitor)】

package com.atguigu.visitor;public class Success extends Action {@Overridepublic void getManResult(Man man) {System.out.println(" 男人给的评价该歌手很成功 !");}@Overridepublic void getWomanResult(Woman woman) {System.out.println(" 女人给的评价该歌手很成功 !");}}

【Fail(ConcreteVisitor)】

package com.atguigu.visitor;public class Fail extends Action {@Overridepublic void getManResult(Man man) {System.out.println(" 男人给的评价该歌手失败 !");}@Overridepublic void getWomanResult(Woman woman) {System.out.println(" 女人给的评价该歌手失败 !");}}

【Person(Element)】

package com.atguigu.visitor;public abstract class Person {/*** 提供一个方法,让访问者可以访问** @param action*/public abstract void accept(Action action);
}

【Woman(ConcreteElement )】

package com.atguigu.visitor;/*** 这里我们使用到了双分派, 即首先在客户端程序中,将具体状态作为参数传递到Woman中(第一次分派)* 然后 Woman 类调用作为参数的 "具体方法" 中方法getWomanResult, 同时将自己(this)作为参数传入,完成第二次的分派* 即互为对方方法的参数*/
public class Woman extends Person{@Overridepublic void accept(Action action) {action.getWomanResult(this);}}

【Man(ConcreteElement )】

package com.atguigu.visitor;public class Man extends Person {@Overridepublic void accept(Action action) {// 自己认为是什么结果就是什么结果action.getManResult(this);}}

【ObjectStructure】

package com.atguigu.visitor;import java.util.LinkedList;
import java.util.List;/*** 数据结构,管理很多人(Man , Woman)*/
public class ObjectStructure {/*** 维护了一个集合*/private List<Person> persons = new LinkedList<>();/*** 将元素增加到list,在访问者模式中,一般使用attach,不使用add** @param p*/public void attach(Person p) {persons.add(p);}/*** 移除** @param p*/public void detach(Person p) {persons.remove(p);}/*** 显示测评情况* @param action*/public void display(Action action) {for (Person p : persons) {p.accept(action);}}
}

【客户端】

package com.atguigu.visitor;public class Client {public static void main(String[] args) {//创建ObjectStructureSystem.out.println("=======添加观众========");ObjectStructure objectStructure = new ObjectStructure();objectStructure.attach(new Man());objectStructure.attach(new Woman());//成功System.out.println("=======测评结果是成功晋级========");Success success = new Success();objectStructure.display(success);System.out.println("=======测评结果是失败========");Fail fail = new Fail();objectStructure.display(fail);}}

【运行】

=======添加观众========
=======测评结果是成功晋级========男人给的评价该歌手很成功 !女人给的评价该歌手很成功 !
=======测评结果是失败========男人给的评价该歌手失败 !女人给的评价该歌手失败 !Process finished with exit code 0

拓展

上面的程序使用了双重分发,所谓双重分发是指不管类怎么变化,我们都能找到期望的方法运行。双重分发意味着得到执行的操作取决于请求的种类和两个接收者的类型。假设我们要添加一个Wait的状态类,考察Man类和Woman类的反应,由于使用了双重分发,只需增加一个Action子类即可在客户端调用即可,不需要改动任何其他类的代码

【增加类:Wait】

package com.atguigu.visitor;public class Wait extends Action {@Overridepublic void getManResult(Man man) {System.out.println(" 男人给的评价是该歌手待定 ..");}@Overridepublic void getWomanResult(Woman woman) {System.out.println(" 女人给的评价是该歌手待定 ..");}}

【客户端】

package com.atguigu.visitor;public class Client {public static void main(String[] args) {//创建ObjectStructureSystem.out.println("=======添加观众========");ObjectStructure objectStructure = new ObjectStructure();objectStructure.attach(new Man());objectStructure.attach(new Woman());System.out.println("=======测评结果是待定========");Wait wait = new Wait();objectStructure.display(wait);}}

【运行】

=======添加观众========
=======测评结果是待定========男人给的评价是该歌手待定 ..女人给的评价是该歌手待定 ..Process finished with exit code 0

案例二(个人感觉这个案例较好)

在这里插入图片描述

实现

【访问者抽象类】

package com.atguigu.visitor.Sample;/*** 访问者抽象类* 依赖要访问的数据结构,File和Directory*/
public abstract class Visitor {/*** 访问File类的方法** @param file*/public abstract void visit(File file);/*** 访问Directory类的方法** @param directory*/public abstract void visit(Directory directory);
}

【接受访问的接口】

package com.atguigu.visitor.Sample;/*** 接受访问的接口*/
public interface Element {/*** 接受访问** @param v*/public abstract void accept(Visitor v);
}

【接受访问的抽象类】

这个类不需要实现accept方法,因为不是最终被访问的类

package com.atguigu.visitor.Sample;import java.util.Iterator;public abstract class Entry implements Element {/*** 获取名字** @return*/public abstract String getName();/*** 获取大小** @return*/public abstract int getSize();/*** 增加目录条目** @param entry* @return* @throws FileTreatmentException*/public Entry add(Entry entry) throws FileTreatmentException {// 对 Directory 才有效,这里先简单报个错,让它自己重写一遍throw new FileTreatmentException();}/*** 生成Iterator** @return* @throws FileTreatmentException*/public Iterator iterator() throws FileTreatmentException {// 对 Directory 才有效,这里先简单报个错,让它自己重写一遍throw new FileTreatmentException();}/*** 显示字符串** @return*/public String toString() {return getName() + " (" + getSize() + ")";}
}

【异常类】

package com.atguigu.visitor.Sample;public class FileTreatmentException extends RuntimeException {public FileTreatmentException() {}public FileTreatmentException(String msg) {super(msg);}
}

【接受访问的具体类:File(ConcreteElement角色)】

package com.atguigu.visitor.Sample;public class File extends Entry {private String name;private int size;public File(String name, int size) {this.name = name;this.size = size;}public String getName() {return name;}public int getSize() {return size;}public void accept(Visitor v) {// 把自己交给访问者访问v.visit(this);}
}

【接受访问的具体类:Directory(ConcreteElement角色、ObjectStructure角色)】

package com.atguigu.visitor.Sample;import java.util.ArrayList;
import java.util.Iterator;public class Directory extends Entry {/*** 文件夹名字*/private String name;/*** 目录条目集合*/private ArrayList dir = new ArrayList();/*** 构造函数* @param name*/public Directory(String name) {this.name = name;}/*** 获取名字* @return*/public String getName() {return name;}/*** 获取大小* @return*/public int getSize() {int size = 0;Iterator it = dir.iterator();while (it.hasNext()) {Entry entry = (Entry) it.next();size += entry.getSize();}return size;}/*** 增加目录条目* @param entry* @return*/public Entry add(Entry entry) {dir.add(entry);return this;}/*** 生成Iterator* @return*/public Iterator iterator() {return dir.iterator();}/*** 接受访问者的访问* @param v*/public void accept(Visitor v) {v.visit(this);}
}

【具体访问者】

package com.atguigu.visitor.Sample;import java.util.Iterator;public class ListVisitor extends Visitor {/*** 当前访问的文件夹的名字*/private String currentdir = "";/*** 在访问文件时被调用* 访问的是文件,就简单输出一下,文件的信息* @param file*/public void visit(File file) {System.out.println(currentdir + "/" + file);}/*** 在访问文件夹时被调用* 访问的是文件夹,不仅输出文件夹的信息,还要递归输出子文件和子文件夹的相关信息* @param directory*/public void visit(Directory directory) {System.out.println(currentdir + "/" + directory);String savedir = currentdir;currentdir = currentdir + "/" + directory.getName();Iterator it = directory.iterator();while (it.hasNext()) {Entry entry = (Entry) it.next();// 继续让当前访问者访问子文件或者文件夹entry.accept(this);}currentdir = savedir;}
}

【主类】

package com.atguigu.visitor.Sample;public class Main {public static void main(String[] args) {try {System.out.println("Making root entries...");Directory rootdir = new Directory("root");Directory bindir = new Directory("bin");Directory tmpdir = new Directory("tmp");Directory usrdir = new Directory("usr");rootdir.add(bindir);rootdir.add(tmpdir);rootdir.add(usrdir);bindir.add(new File("vi", 10000));bindir.add(new File("latex", 20000));// 接受访问,打印整个根目录下面的所有文件信息rootdir.accept(new ListVisitor());              System.out.println("");System.out.println("Making user entries...");Directory yuki = new Directory("yuki");Directory hanako = new Directory("hanako");Directory tomura = new Directory("tomura");usrdir.add(yuki);usrdir.add(hanako);usrdir.add(tomura);yuki.add(new File("diary.html", 100));yuki.add(new File("Composite.java", 200));hanako.add(new File("memo.tex", 300));tomura.add(new File("game.doc", 400));tomura.add(new File("junk.mail", 500));// 接受访问,打印整个根目录下面的所有文件信息rootdir.accept(new ListVisitor());              } catch (FileTreatmentException e) {e.printStackTrace();}}
}

【运行】

Making root entries...
/root (30000)
/root/bin (30000)
/root/bin/vi (10000)
/root/bin/latex (20000)
/root/tmp (0)
/root/usr (0)Making user entries...
/root (31500)
/root/bin (30000)
/root/bin/vi (10000)
/root/bin/latex (20000)
/root/tmp (0)
/root/usr (1500)
/root/usr/yuki (300)
/root/usr/yuki/diary.html (100)
/root/usr/yuki/Composite.java (200)
/root/usr/hanako (300)
/root/usr/hanako/memo.tex (300)
/root/usr/tomura (900)
/root/usr/tomura/game.doc (400)
/root/usr/tomura/junk.mail (500)Process finished with exit code 0

分析

  • ConcreteVisitor 角色的开发可以独立于File类和Directory类。也就是说,Visitor模式提高了File类和Directory类作为组件的独立性。如果将进行处理的方法定义在File类和Directory类中,当每次要扩展功能,增加新的“处理”时,就不得不去修改File类和Directory类

拓展一

在示例程序中增加一个FileFindvistor类,用于将带有指定后缀名的文件手机起来,存储到集合中

【FileFindvistor】

package com.atguigu.visitor.A1;import java.util.ArrayList;
import java.util.Iterator;public class FileFindVisitor extends Visitor {private String filetype;private ArrayList found = new ArrayList();/*** 指定.后面的文件后缀名,如".txt"** @param filetype*/public FileFindVisitor(String filetype) {this.filetype = filetype;}/*** 获取已经找到的文件** @return*/public Iterator getFoundFiles() {return found.iterator();}/*** 在访问文件时被调用** @param file*/public void visit(File file) {if (file.getName().endsWith(filetype)) {// 将符合格式的文件,添加到集合中found.add(file);}}/*** 在访问文件夹时被调用** @param directory*/public void visit(Directory directory) {Iterator it = directory.iterator();while (it.hasNext()) {Entry entry = (Entry) it.next();entry.accept(this);}}
}

【主类】

package com.atguigu.visitor.A1;import java.util.Iterator;public class Main {public static void main(String[] args) {try {Directory rootdir = new Directory("root");Directory bindir = new Directory("bin");Directory tmpdir = new Directory("tmp");Directory usrdir = new Directory("usr");rootdir.add(bindir);rootdir.add(tmpdir);rootdir.add(usrdir);bindir.add(new File("vi", 10000));bindir.add(new File("latex", 20000));Directory yuki = new Directory("yuki");Directory hanako = new Directory("hanako");Directory tomura = new Directory("tomura");usrdir.add(yuki);usrdir.add(hanako);usrdir.add(tomura);yuki.add(new File("diary.html", 100));yuki.add(new File("Composite.java", 200));hanako.add(new File("memo.tex", 300));hanako.add(new File("index.html", 350));tomura.add(new File("game.doc", 400));tomura.add(new File("junk.mail", 500));// 筛选出.html结尾的文件FileFindVisitor ffv = new FileFindVisitor(".html");     rootdir.accept(ffv);// 输出.html结尾的文件System.out.println("HTML files are:");Iterator it = ffv.getFoundFiles();                      while (it.hasNext()) {                                  File file = (File)it.next();                        System.out.println(file.toString());}                                                       } catch (FileTreatmentException e) {e.printStackTrace();}}
}

【运行】

HTML files are:
diary.html (100)
index.html (350)Process finished with exit code 0

拓展二

Directory类的getSize方法的作用是获取文件夹大小,请编写一个获取大小的SizeVisitor类,用它替换掉 Directory类的getSize方法

【SizeVisitor】

import java.util.Iterator;public class SizeVisitor extends Visitor {private int size = 0;public int getSize() {return size;}public void visit(File file) {size += file.getSize();}public void visit(Directory directory) {Iterator it = directory.iterator();while (it.hasNext()) {Entry entry = (Entry) it.next();entry.accept(this);}}
}

【修改Directory的方法】

package com.atguigu.visitor.A2;import java.util.ArrayList;
import java.util.Iterator;public class Directory extends Entry {private String name;private ArrayList dir = new ArrayList();public Directory(String name) {         // 构造函数this.name = name;}public String getName() {               // 获取名字return name;}public int getSize() {// 使用visitor来替换原来的方式SizeVisitor v = new SizeVisitor();accept(v);return v.getSize();}public Entry add(Entry entry) {dir.add(entry);return this;}public Iterator iterator() {return dir.iterator();}public void accept(Visitor v) {v.visit(this);}
}

拓展三

基于java.util.ArrayList类编写一个具有Element接口的ElementArrayList类,使得Directory类和File类可以被add至ElementArrayList 中,而且它还可以接受(accept) ListVisitor 的实例访问它

【ElementArrayList】

package com.atguigu.visitor.A3;import java.util.ArrayList;
import java.util.Iterator;/*** 继承ArrayList,这样就不用定义集合的add remove等操作*/
class ElementArrayList extends ArrayList implements Element {public void accept(Visitor v) {// 使用迭代器遍历Iterator it = iterator();while (it.hasNext()) {Element e = (Element)it.next();e.accept(v);}}
}

由于visit方法不用传入ElementArrayList类作为参数,因此不用修改Visitor

【主类】

package com.atguigu.visitor.A3;public class Main {public static void main(String[] args) {try {Directory root1 = new Directory("root1");root1.add(new File("diary.html", 10));root1.add(new File("index.html", 20));Directory root2 = new Directory("root2");root2.add(new File("diary.html", 1000));root2.add(new File("index.html", 2000));ElementArrayList list = new ElementArrayList();list.add(root1);list.add(root2);list.add(new File("etc.html", 1234));list.accept(new ListVisitor());} catch (FileTreatmentException e) {e.printStackTrace();}}
}

【运行】

/root1 (30)
/root1/diary.html (10)
/root1/index.html (20)
/root2 (3000)
/root2/diary.html (1000)
/root2/index.html (2000)
/etc.html (1234)Process finished with exit code 0

总结

【优点】

  • 访问者模式符合单一职责原则、让程序具有优秀的扩展性、灵活性非常高
  • 访问者模式可以对功能进行统一,可以做报表、UI、拦截器与过滤器,适用于数据结构相对稳定的系统
  • 如果一个系统有比较稳定的数据结构,又有经常变化的功能需求,那么访问者模式就是比较合适的
  • 易于增加 ConcreteVisitor 角色

【缺点】

  • 具体元素对访问者公布细节,也就是访问者关注其他类的内部细节(如Success里面传入了Man,且调用其accept方法),这是迪米特法则不建议的,这样造成了具体元素变更比较困难
  • 违背了依赖倒转原则。访问者依赖的是具体元素,而不是抽象元素(Action里面依赖的是Man和Woman,而不是Person)
  • 难以增加ConcreteElement 角色。一旦增加了ConcreteElement 角色,需要在Visitor类中声明新的visit方法,而且所有的ConcreteVisitor都需要实现这个方法

额外知识

双重分发

// accept (接受)方法的调用方式
element.accept(visitor);// visit(访问)方法的调用方式
visitor.visit(element);

ConcreteElementConcreteVisitor这两个角色互相调用共同决定了实际进行的处理

文章说明

  • 本文章为本人学习尚硅谷的学习笔记,文章中大部分内容来源于尚硅谷视频(点击学习尚硅谷相关课程),也有部分内容来自于自己的思考,发布文章是想帮助其他学习的人更方便地整理自己的笔记或者直接通过文章学习相关知识,如有侵权请联系删除,最后对尚硅谷的优质课程表示感谢。
  • 本人还同步阅读《图解设计模式》书籍(图解设计模式/(日)结城浩著;杨文轩译–北京:人民邮电出版社,2017.1),进而综合两者的内容,让知识点更加全面

相关文章:

【设计模式——学习笔记】23种设计模式——访问者模式Visitor(原理讲解+应用场景介绍+案例介绍+Java代码实现)

文章目录 案例引入要求传统方案 介绍基本介绍应用场景登场角色尚硅谷版本《图解设计模式》版本 案例实现案例一实现拓展 案例二(个人感觉这个案例较好)实现分析拓展一拓展二拓展三 总结额外知识双重分发 文章说明 案例引入 要求 测评系统需求&#xff1a;将观众分为男人和女人…...

Ubuntu安装MySQL 8.0与Navicat

目录 Ubuntu安装MySQL 8.0 1、更新软件包列表 2、安装 MySQL 8.0 3、启动 MySQL 服务 5、确保MySQL服务器正在运行 5、root 用户的密码 6、登录MySQL&#xff0c;输入mysql密码 7、MySQL默认位置 Ubuntu安装Navicat 1、下载 Navicat 2、额外的软件包 3、执行命令 U…...

GB28181智慧可视化指挥控制系统之执法记录仪设计探讨

什么是智慧可视化指挥控制系统&#xff1f; 智慧可视化指挥控制平台通过4G/5G网络、WIFI实时传输视音频数据至指挥中心&#xff0c;特别是在有突发情况时&#xff0c;可以指定一台执法仪为现场视频监控器&#xff0c;实时传输当前画面到指挥中心&#xff0c;指挥中心工作人员可…...

【SpringBoot】自动配置自动加载controller的原理

SpringBoot自动配置&&自动加载controller的原理.md 好久没有更新自己的博客了,自己最近的正好有点空闲的时间进行,自己在写着写着,突然想起来, 为什么我们点击application就能自动加载Controller呢?(好家伙,我顿时鱼鳃,哈哈) 1.首先我们来到启动现场>启动类 Sprin…...

Docker Enable live

ubuntu - Enabling live restore on docker isnt keeping the containers alive - Stack Overflow容器安全之启用实时恢复 - 简书 (jianshu.com)...

9.物联网操作系统之软件定时器

一。软件定时器概念及应用 1.软件定时器定义 就是软件实现定时器。 2.FreeRTOS软件定时器介绍 如上图所示&#xff0c;Times的左边为设置定时器时间&#xff0c;设置方式可以为任务设置或者中断设置&#xff1b;Times的右边为定时器的定时相应&#xff0c;使用CalBack相应。 …...

Windows Server 2012 R2 安装 Oracle RAC 11g R2

Windows Server 2012 R2 安装 Oracle RAC 11g R2 环境准备安装系统设置虚拟网络配置虚拟机网卡开机进行系统配置关闭防火墙设置网络系统高级设置修改注册表修改计算机名称设置账户控制RAC1 和 RAC2 的磁盘共享修改 hosts同步时间在 RAC1 RAC2 DATA 中安装 .net3.5在 DATA 中搭建…...

本地shell无法连接ubuntu,解决办法?

1.启动ssh服务&#xff1b; sudo /etc/init.d/ssh start若重启ssh服务后&#xff0c;还是连接不上&#xff0c;继续第2步&#xff1b; 2.修改SSH配置文件 /etc/ssh/sshd_config 默认是不允许root远程登录的&#xff0c;可以再配置文件开启。 sudo vi /etc/ssh/sshd_config找…...

关于openwrt的802.11w 管理帧保护使用

目录 关于openwrt的802.11w 管理帧保护使用先来看看界面上的提示 实际遇到的问题总结 关于openwrt的802.11w 管理帧保护使用 先来看看界面上的提示 注意&#xff1a;有些无线驱动程序不完全支持 802.11w。例如&#xff1a;mwlwifi 可能会有一些问题 实际遇到的问题 802.11w 管…...

centos手动离线安装php,nginx

1、CentOS 7上安装并配置Nginx https://www.cnblogs.com/xujiecnblogs/p/16843984.html /usr/local/nginx/sbin/nginx #启动 /usr/local/nginx/sbin/nginx -s stop #关闭 /usr/local/nginx/sbin/nginx -s reload #重启 增加h…...

Java基础六 - Collection集合List、Set、Queue,Map

1. List - ArrayList、LinkedList、Vector ArrayList 1. 可以有重复元素 2. 超出后自动增加大小&#xff0c;增加一半。会自动重新分配更大的数组&#xff0c;并将元素复制到新数组中 3. 通过索引保存值&#xff0c;访问可以通过索引访问&#xff0c;更加高效。但是添加/删除…...

k8s nginx+ingress 配置

1 nginx> ingress 配置&#xff1a; 2 nginx >service 配置 3 nginx pod配置&#xff1a; 4 nginx.conf 配置文件&#xff1a; # web端v1server{listen 30006;add_header Strict-Transport-Security "max-age31536000; includeSubDomains";#add_header Content…...

探索Streamlit中强大而灵活的 st.write() 函数(五):构建丰富多样的应用界面

文章目录 1 前言2 显示HTML的内容3 显示Markdown内容4 显示代码块5 显示DataFrame的交互式表格6 显示音频和视频7 显示图表8 显示图片9 显示地图10 显示PDF文件11 显示文件下载链接12 结语 1 前言 在这篇博文中&#xff0c;我们将着重介绍Streamlit中一个核心而重要的函数&…...

LinAlgError: Singular matrix 问题解决

错误log&#xff1a; c:\Program Files\Python39\lib\site-packages\numpy\linalg\linalg.py in inv(a)543 signature D->D if isComplexType(t) else d->d544 extobj get_linalg_error_extobj(_raise_linalgerror_singular) --> 545 ainv _umath_lin…...

【ASP.NET MVC】使用动软(五)(13)

一、问题 前文完成的用户登录后的首页如下&#xff1a; 后续账单管理、人员管理等功能页面都有相同的头部&#xff0c;左边和下边&#xff0c;唯一不同的右边内容部分&#xff0c;所以要解决重复设计的问题。 二、解决方法——使用布局页 在Views上右键添加新建项&#xff…...

MongoDB面试题

1. NoSQL 数据库是什么意思?NoSQL 与 RDBMS 直接有什么区别?为什么要使用和不使用 NoSQL 数据库?说一说 NoSQL 数据库的几个优点? NoSQL 是非关系型数据库&#xff0c;NoSQL Not Only SQL。 关系型数据库采用的结构化的数据&#xff0c;NoSQL 采用的是键值对的方式存储数…...

Python Web 开发 Flask 介绍

WEB开发是现在程序必会的技能&#xff0c;因为大部分软件都以Web形式提供&#xff0c;及时制作后台开发&#xff0c;或者只做前台开发&#xff0c;也需要了解Web开发的概念和特点。由于Python是解释性脚本语言&#xff0c;用来做Web开发非常适合&#xff0c;而且Python有上百种…...

本地mvn仓库清理无用jar包

背景 开发java时间久了&#xff0c;本地的m2仓库就会产生很多过期的jar包&#xff0c;不清理的话比较占空间。 原理 是通过比较同一目录下&#xff0c;对应jar包的版本号的大小&#xff0c;保留最大版本号那个&#xff0c;删除其他的。 脚本 执行脚本见文章顶部 执行方式 …...

MySQL的常用函数大全

一、字符串函数 常用函数&#xff1a; 函数功能CONCAT(s1, s2, …, sn)字符串拼接&#xff0c;将s1, s2, …, sn拼接成一个字符串LOWER(str)将字符串全部转为小写UPPER(str)将字符串全部转为大写LPAD(str, n, pad)左填充&#xff0c;用字符串pad对str的左边进行填充&#xff0…...

一百四十三、Linux——Linux的CentOS 7系统语言由中文改成英文

一、目的 之前安装CentOS 7系统的时候把语言设置成中文&#xff0c;结果Linux文件夹命名出现中文乱码的问题&#xff0c;于是决定把Linux系统语言由中文改成英文 二、实施步骤 &#xff08;一&#xff09;到etc目录下&#xff0c;找到配置文件locale.conf # cd /etc/ # ls…...

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站&#xff0c;会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后&#xff0c;网站没有变化的情况。 不熟悉siteground主机的新手&#xff0c;遇到这个问题&#xff0c;就很抓狂&#xff0c;明明是哪都没操作错误&#x…...

云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地

借阿里云中企出海大会的东风&#xff0c;以**「云启出海&#xff0c;智联未来&#xff5c;打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办&#xff0c;现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...

安宝特方案丨XRSOP人员作业标准化管理平台:AR智慧点检验收套件

在选煤厂、化工厂、钢铁厂等过程生产型企业&#xff0c;其生产设备的运行效率和非计划停机对工业制造效益有较大影响。 随着企业自动化和智能化建设的推进&#xff0c;需提前预防假检、错检、漏检&#xff0c;推动智慧生产运维系统数据的流动和现场赋能应用。同时&#xff0c;…...

visual studio 2022更改主题为深色

visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中&#xff0c;选择 环境 -> 常规 &#xff0c;将其中的颜色主题改成深色 点击确定&#xff0c;更改完成...

华为OD机试-食堂供餐-二分法

import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...

DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI

前一阵子在百度 AI 开发者大会上&#xff0c;看到基于小智 AI DIY 玩具的演示&#xff0c;感觉有点意思&#xff0c;想着自己也来试试。 如果只是想烧录现成的固件&#xff0c;乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外&#xff0c;还提供了基于网页版的 ESP LA…...

C# 类和继承(抽象类)

抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...

Linux-07 ubuntu 的 chrome 启动不了

文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了&#xff0c;报错如下四、启动不了&#xff0c;解决如下 总结 问题原因 在应用中可以看到chrome&#xff0c;但是打不开(说明&#xff1a;原来的ubuntu系统出问题了&#xff0c;这个是备用的硬盘&a…...

拉力测试cuda pytorch 把 4070显卡拉满

import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试&#xff0c;通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小&#xff0c;增大可提高计算复杂度duration: 测试持续时间&#xff08;秒&…...

[Java恶补day16] 238.除自身以外数组的乘积

给你一个整数数组 nums&#xff0c;返回 数组 answer &#xff0c;其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法&#xff0c;且在 O(n) 时间复杂度…...