java设计模式之:访问者模式

news2024/11/16 15:37:04

前言

关于设计模式,我们得结合生活中的案例来学习;最近我在网上也看了不少文章,今天想跟大家分享一下关于访问者模式的一些知识,先来看一个简单的案例吧。

相信大家都去过医院,看完病,医生都会给我们开一个处方单,很多医院都存在如下处理流程:划价人员拿到处方单之后根据药品名称和数量计算总价,药房工作人员根据药品名称和数量准备药品。

我们可以将处方单看成一个药品信息的集合,里面包含了一种或多种不同类型的药品信息,不同类型的工作人员(如划价人员和药房工作人员)在操作同一个药品信息集合时将提供不同的处理方式,而且可能还会增加新类型的工作人员来操作处方单。

在软件开发中,有时候我们也需要处理像处方单这样的集合对象结构,在该对象结构中存储了多个不同类型的对象信息,而且对同一对象结构中的元素的操作方式并不唯一,可能需要提供多种不同的处理方式,还有可能增加新的处理方式。在设计模式中,有一种模式可以满足上述要求,其模式动机就是以不同的方式操作复杂对象结构,该模式就是我们本章将要介绍的访问者模式。

访问者模式概述

访问者模式(Visitor Pattern):提供一个作用于某对象结构中的各元素的操作表示,它使我们可以在不改变各元素的类的前提下定义作用于这些元素的新操作。访问者模式是一种对象行为型模式。

访问者模式是一种较为复杂的行为型设计模式,它包含访问者和被访问元素两个主要组成部分,这些被访问的元素通常具有不同的类型,且不同的访问者可以对它们进行不同的访问操作。例如处方单中的各种药品信息就是被访问的元素,而划价人员和药房工作人员就是访问者。访问者模式使得用户可以在不修改现有系统的情况下扩展系统的功能,为这些不同类型的元素增加新的操作。

在使用访问者模式时,被访问元素通常不是单独存在的,它们存储在一个集合中,这个集合被称为“对象结构”,访问者通过遍历对象结构实现对其中存储的元素的逐个操作。

其结构如下图所示:

image-20230408221733799

  • Vistor(抽象访问者):抽象访问者为对象结构中每一个具体元素类ConcreteElement声明一个访问操作,从这个操作的名称或参数类型可以清楚知道需要访问的具体元素的类型,具体访问者需要实现这些操作方法,定义对这些元素的访问操作。
  • ConcreteVisitor(具体访问者):具体访问者实现了每个由抽象访问者声明的操作,每一个操作用于访问对象结构中一种类型的元素。
  • Element(抽象元素):抽象元素一般是抽象类或者接口,它定义一个accept()方法,该方法通常以一个抽象访问者作为参数。
  • ConcreteElement(具体元素):具体元素实现了accept()方法,在accept()方法中调用访问者的访问方法以便完成对一个元素的操作。
  • ObjectStructure(对象结构):对象结构是一个元素的集合,它用于存放元素对象,并且提供了遍历其内部元素的方法。它可以结合组合模式来实现,也可以是一个简单的集合对象,如一个List对象或一个Set对象。

适用场景

(1)一个对象结构包含多个类型的对象,希望对这些对象实施一些依赖其具体类型的操作。在访问者中针对每一种具体的类型都提供了一个访问操作,不同类型的对象可以有不同的访问操作。

(2)需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作“污染”这些对象的类,也不希望在增加新操作时修改这些类。访问者模式使得我们可以将相关的访问操作集中起来定义在访问者类中,对象结构可以被多个不同的访问者类所使用,将对象本身与对象的访问操作分离。

(3)对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。

案例场景一

某银行开发了一套OA系统,在该OA系统中包含一个员工信息管理子系统,该银行员工包括正式员工和临时工,每周人力资源部和财务部等部门需要对员工数据进行汇总,汇总数据包括员工工作时间、员工工资等。该公司基本制度如下:

(1)正式员工(Full time Employee)每周工作时间为40小时,不同级别、不同部门的员工每周基本工资不同;如果超过40小时,超出部分按照100元/小时作为加班费;如果少于40小时,所缺时间按照请假处理,请假所扣工资以80元/小时计算,直到基本工资扣除到零为止。除了记录实际工作时间外,人力资源部需记录加班时长或请假时长,作为员工平时表现的一项依据。

(2)临时工(Part time Employee)每周工作时间不固定,基本工资按小时计算,不同岗位的临时工小时工资不同。人力资源部只需记录实际工作时间。

人力资源部和财务部工作人员可以根据各自的需要对员工数据进行汇总处理,人力资源部负责汇总每周员工工作时间,而财务部负责计算每周员工工资。

一坨坨代码实现

class EmployeeList {

    private ArrayList<Employee> list = new ArrayList<Employee>(); //员工集合  

    //处理员工数据  
    public void handle(String departmentName) {
        //财务部处理员工数据  
        if (departmentName.equalsIgnoreCase("财务部")) {
            for (Object obj : list) {
                if (obj.getClass().getName().equalsIgnoreCase("FulltimeEmployee")) {
                    System.out.println("财务部处理全职员工数据!");
                } else {
                    System.out.println("财务部处理兼职员工数据!");
                }
            }
            //人力资源部处理员工数据  
        } else if (departmentName.equalsIgnoreCase("人力资源部")) {
            for (Object obj : list) {
                if (obj.getClass().getName().equalsIgnoreCase("FulltimeEmployee")) {
                    System.out.println("人力资源部处理全职员工数据!");
                } else {
                    System.out.println("人力资源部处理兼职员工数据!");
                }
            }
        }
    }
}  

handle()方法中,通过对部门名称和员工类型进行判断,不同部门对不同类型的员工进行了不同的处理,满足了员工数据汇总的要求。但是该解决方案存在如下几个问题:

(1)EmployeeList类承担了过多的职责,既不方便代码的复用,也不利于系统的扩展,违背了“单一职责原则”。

(2)在代码中包含大量的“if…else…”条件判断语句,既需要对不同部门进行判断,又需要对不同类型的员工进行判断,还将出现嵌套的条件判断语句,导致测试和维护难度增大。

(3)如果要增加一个新的部门来操作员工集合,不得不修改EmployeeList类的源代码,在handle()方法中增加一个新的条件判断语句和一些业务处理代码来实现新部门的访问操作。这违背了“开闭原则”,系统的灵活性和可扩展性有待提高。

(4)如果要增加一种新类型的员工,同样需要修改EmployeeList类的源代码,在不同部门的处理代码中增加对新类型员工的处理逻辑,这也违背了“开闭原则”。

重构代码

定义抽象元素

//员工类:抽象元素类
public interface Employee {
    void accept(Department handler); //接受一个抽象访问者访问
} 

具体元素类

//全职员工类:具体元素类
public class FullTimeEmployee implements Employee {

    private String name;
    private double weeklyWage;
    private int workTime;

    public FullTimeEmployee(String name, double weeklyWage, int workTime) {
        this.name = name;
        this.weeklyWage = weeklyWage;
        this.workTime = workTime;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setWeeklyWage(double weeklyWage) {
        this.weeklyWage = weeklyWage;
    }

    public void setWorkTime(int workTime) {
        this.workTime = workTime;
    }

    public String getName() {
        return (this.name);
    }

    public double getWeeklyWage() {
        return (this.weeklyWage);
    }

    public int getWorkTime() {
        return (this.workTime);
    }

    public void accept(Department handler) {
        handler.visit(this); //调用访问者的访问方法  
    }
}
//兼职员工类:具体元素类
public class PartTimeEmployee implements Employee {

    private String name;
    private double hourWage;
    private int workTime;

    public PartTimeEmployee(String name, double hourWage, int workTime) {
        this.name = name;
        this.hourWage = hourWage;
        this.workTime = workTime;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setHourWage(double hourWage) {
        this.hourWage = hourWage;
    }

    public void setWorkTime(int workTime) {
        this.workTime = workTime;
    }

    public String getName() {
        return (this.name);
    }

    public double getHourWage() {
        return (this.hourWage);
    }

    public int getWorkTime() {
        return (this.workTime);
    }

    public void accept(Department handler) {
        handler.visit(this); //调用访问者的访问方法  
    }
} 

定义抽象访问者类

//部门类:抽象访问者类
public abstract class Department {
    //声明一组重载的访问方法,用于访问不同类型的具体元素  
    public abstract void visit(FullTimeEmployee employee);
    public abstract void visit(PartTimeEmployee employee);
}

具体访问者类

//财务部类:具体访问者类
public class FADepartment extends Department {

    //实现财务部对全职员工的访问  
    public void visit(FullTimeEmployee employee) {
        int workTime = employee.getWorkTime();
        double weekWage = employee.getWeeklyWage();
        if (workTime > 40) {
            weekWage = weekWage + (workTime - 40) * 100;
        } else if (workTime < 40) {
            weekWage = weekWage - (40 - workTime) * 80;
            if (weekWage < 0) {
                weekWage = 0;
            }
        }
        System.out.println("正式员工" + employee.getName() + "实际工资为:" + weekWage + "元。");
    }

    //实现财务部对兼职员工的访问  
    public void visit(PartTimeEmployee employee) {
        int workTime = employee.getWorkTime();
        double hourWage = employee.getHourWage();
        System.out.println("临时工" + employee.getName() + "实际工资为:" + workTime * hourWage + "元。");
    }
} 
//人力资源部类:具体访问者类
public class HRDepartment extends Department {

    //实现人力资源部对全职员工的访问  
    public void visit(FullTimeEmployee employee) {
        int workTime = employee.getWorkTime();
        System.out.println("正式员工" + employee.getName() + "实际工作时间为:" + workTime + "小时。");
        if (workTime > 40) {
            System.out.println("正式员工" + employee.getName() + "加班时间为:" + (workTime - 40) + "小时。");
        } else if (workTime < 40) {
            System.out.println("正式员工" + employee.getName() + "请假时间为:" + (40 - workTime) + "小时。");
        }
    }

    //实现人力资源部对兼职员工的访问  
    public void visit(PartTimeEmployee employee) {
        int workTime = employee.getWorkTime();
        System.out.println("临时工" + employee.getName() + "实际工作时间为:" + workTime + "小时。");
    }
}

定义数据结构以及测试

//员工列表类:对象结构
class EmployeeList {

    //定义一个集合用于存储员工对象  
    private ArrayList<Employee> list = new ArrayList<>();

    public EmployeeList(){
        list.add(new FullTimeEmployee("张三",3200.00,45));
        list.add(new FullTimeEmployee("李四",2500.00,40));
        list.add(new PartTimeEmployee("王二",80.00,20));
        list.add(new PartTimeEmployee("李强",100.00,30));
    }

    //遍历访问员工集合中的每一个员工对象  
    public void accept(Department handler) {
        for (Object obj : list) {
            ((Employee) obj).accept(handler);
        }
    }

    public static void main(String[] args) {
        EmployeeList employeeList = new EmployeeList();
        System.out.println("\r\n财务部:");
        employeeList.accept(new FADepartment());
        System.out.println("\r\n人力资源部:");
        employeeList.accept(new HRDepartment());
    }
} 

测试结果如下:

财务部:
正式员工张三实际工资为:3700.0元。
正式员工李四实际工资为:2500.0元。
临时工王二实际工资为:1600.0元。
临时工李强实际工资为:3000.0元。

人力资源部:
正式员工张三实际工作时间为:45小时。
正式员工张三加班时间为:5小时。
正式员工李四实际工作时间为:40小时。
临时工王二实际工作时间为:20小时。
临时工李强实际工作时间为:30小时。

从以上的业务场景中可以看到,在嵌⼊访问者模式后,可以让整个⼯程结构变得容易添加和修改。

如果要在系统中增加一种新的访问者,无须修改源代码,只要增加一个新的具体访问者类即可,在该具体访问者中封装了新的操作元素对象的方法。从增加新的访问者的角度来看,访问者模式符合“开闭原则”。

如果要在系统中增加一种新的具体元素,例如增加一种新的员工类型为“退休人员”,由于原有系统并未提供相应的访问接口(在抽象访问者中没有声明任何访问“退休人员”的方法),因此必须对原有系统进行修改,在原有的抽象访问者类和具体访问者类中增加相应的访问方法。从增加新的元素的角度来看,访问者模式违背了“开闭原则”。

综上所述,访问者模式与抽象工厂模式类似,对“开闭原则”的支持具有倾斜性,可以很方便地添加新的访问者,但是添加新的元素较为麻烦。

案例场景二

校园中有学生和老师两种身份的用户,那么对于家长和校长关心的角度来看,他们的视角是不同的。家长更关心孩子的成绩和老师的能力,校长更关心老师所在班级学生的人数和升学率。

那么这样学生老师就是一个固定信息的内容,而想让不同视角的用户获取关心的信息,就比较适合使用访问者模式来实现,从而让实体与业务解耦,增强扩展性。

代码实现

定义用户抽象类

// 基础用户信息
public abstract class User {

    public String name;      // 姓名
    public String identity;  // 身份;重点班、普通班 | 特级教师、普通教师、实习教师
    public String clazz;     // 班级

    public User(String name, String identity, String clazz) {
        this.name = name;
        this.identity = identity;
        this.clazz = clazz;
    }

    // 核心访问方法
    public abstract void accept(Visitor visitor);

}   

实现用户信息(老师和学生)

老师类

public class Teacher extends User {

    public Teacher(String name, String identity, String clazz) {
        super(name, identity, clazz);
    }

    public void accept(Visitor visitor) {
        visitor.visit(this);
    }

    // 升本率
    public double entranceRatio() {
        return BigDecimal.valueOf(Math.random() * 100).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
    }

}

学生类

public class Student extends User {

    public Student(String name, String identity, String clazz) {
        super(name, identity, clazz);
    }

    public void accept(Visitor visitor) {
        visitor.visit(this);
    }

    //排名
    public int ranking() {
        return (int) (Math.random() * 100);
    }
}  

定义访问数据接口

public interface Visitor {

    // 访问学生信息
    void visit(Student student);

    // 访问老师信息
    void visit(Teacher teacher);
}   

实现访问类型(校长和家长)

访问者:校长

public class Principal implements Visitor {

    private Logger logger = LoggerFactory.getLogger(Principal.class);

    public void visit(Student student) {
        logger.info("学生信息 姓名:{} 班级:{}", student.name, student.clazz);
    }

    public void visit(Teacher teacher) {
        logger.info("学生信息 姓名:{} 班级:{} 升学率:{}", teacher.name, teacher.clazz, teacher.entranceRatio());
    }

}  

访问者:家长

public class Parent implements Visitor {

    private Logger logger = LoggerFactory.getLogger(Parent.class);

    public void visit(Student student) {
        logger.info("学生信息 姓名:{} 班级:{} 排名:{}", student.name, student.clazz, student.ranking());
    }

    public void visit(Teacher teacher) {
        logger.info("老师信息 姓名:{} 班级:{} 级别:{}", teacher.name, teacher.clazz, teacher.identity);
    }

}   

数据结构

public class DataView {

    List<User> userList = new ArrayList<User>();

 
    public DataView() {
        userList.add(new Student("小马", "重点班", "一年一班"));
        userList.add(new Student("小张", "重点班", "一年一班"));
        userList.add(new Student("小刘", "普通班", "二年三班"));
        userList.add(new Student("小董", "普通班", "三年四班"));
        userList.add(new Teacher("Tom", "特级教师", "一年一班"));
        userList.add(new Teacher("Jack", "特级教师", "一年一班"));
        userList.add(new Teacher("Rose", "普通教师", "二年三班"));
        userList.add(new Teacher("Bob", "实习教师", "三年四班"));
    }

    // 展示
    public void show(Visitor visitor) {
        for (User user : userList) {
            user.accept(visitor);
        }
    }
}

编写测试类

@Test
public void test(){
    DataView dataView = new DataView();      

    logger.info("\r\n家长视角访问:");
    dataView.show(new Parent());     // 家长

    logger.info("\r\n校长视角访问:");
    dataView.show(new Principal());  // 校长
}

测试结果

家长视角访问:
12:33:21.336 [main] INFO  o.i.demo.design.visitor.impl.Parent - 学生信息 姓名:小马 班级:一年一班 排名:58
12:33:21.336 [main] INFO  o.i.demo.design.visitor.impl.Parent - 学生信息 姓名:小张 班级:一年一班 排名:17
12:33:21.336 [main] INFO  o.i.demo.design.visitor.impl.Parent - 学生信息 姓名:小刘 班级:二年三班 排名:29
12:33:21.336 [main] INFO  o.i.demo.design.visitor.impl.Parent - 学生信息 姓名:小董 班级:三年四班 排名:67
12:33:21.336 [main] INFO  o.i.demo.design.visitor.impl.Parent - 老师信息 姓名:Tom 班级:一年一班 级别:特级教师
12:33:21.336 [main] INFO  o.i.demo.design.visitor.impl.Parent - 老师信息 姓名:Jack 班级:一年一班 级别:特级教师
12:33:21.337 [main] INFO  o.i.demo.design.visitor.impl.Parent - 老师信息 姓名:Rose 班级:二年三班 级别:普通教师
12:33:21.337 [main] INFO  o.i.demo.design.visitor.impl.Parent - 老师信息 姓名:Bob 班级:三年四班 级别:实习教师

校长视角访问:
12:33:21.338 [main] INFO  o.i.d.design.visitor.impl.Principal - 学生信息 姓名:小马 班级:一年一班
12:33:21.339 [main] INFO  o.i.d.design.visitor.impl.Principal - 学生信息 姓名:小张 班级:一年一班
12:33:21.339 [main] INFO  o.i.d.design.visitor.impl.Principal - 学生信息 姓名:小刘 班级:二年三班
12:33:21.339 [main] INFO  o.i.d.design.visitor.impl.Principal - 学生信息 姓名:小董 班级:三年四班
12:33:21.342 [main] INFO  o.i.d.design.visitor.impl.Principal - 老师信息 姓名:Tom 班级:一年一班 升学率:61.33
12:33:21.343 [main] INFO  o.i.d.design.visitor.impl.Principal - 老师信息 姓名:Jack 班级:一年一班 升学率:83.64
12:33:21.343 [main] INFO  o.i.d.design.visitor.impl.Principal - 老师信息 姓名:Rose 班级:二年三班 升学率:87.57
12:33:21.343 [main] INFO  o.i.d.design.visitor.impl.Principal - 老师信息 姓名:Bob 班级:三年四班 升学率:30.34
  • 通过测试结果可以看到,家长和校长的访问视角同步,数据也是差异化的。
  • 通过这样的测试结果,可以看到访问者模式的初心和结果,在适合的场景运用合适的模式,非常有利于程序开发。

总结

由于访问者模式的使用条件较为苛刻,本身结构也较为复杂,因此在实际应用中使用频率不是特别高。当系统中存在一个较为复杂的对象结构,且不同访问者对其所采取的操作也不相同时,可以考虑使用访问者模式进行设计。在XML文档解析、编译器的设计、复杂集合对象的处理等领域访问者模式得到了一定的应用。

好的学习⽅式才好更容易接受知识,学习编程的更需要的不单单是看,⽽是操作。⼆⼗多种设计模式每⼀种都有⾃⼰的设计技巧,也可以说是巧妙之处,这些巧妙的地⽅往往是解决复杂难题的最佳视⻆。亲⼒亲为,才能为所欲为,为了⾃⼰的欲望⽽努⼒!

参考书籍

  • 《图解设计模式》

  • 《重学 Java 设计模式》

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

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

相关文章

Linux重定向和缓冲区理解

本文已收录至《Linux知识与编程》专栏&#xff01; 作者&#xff1a;ARMCSKGT 演示环境&#xff1a;CentOS 7 重定向和缓冲区理解 前言正文文件描述符重定向重定向原理重定向命令重定向函数 缓冲区缓冲区是什么&#xff1f;缓冲区刷新策略内核缓冲区与普通缓冲区 最后 前言 前…

5款超级好用的开发效率工具,建议收藏!

大家好&#xff01;高温天气切莫太累&#xff0c;注意防暑休闲开胃(&#xff5e; o &#xff5e;)~zZ 人口过剩的时代&#xff0c;劳动力也追求高性价比。好的工具&#xff0c;能够帮助我们更高效地完成工作&#xff0c;节省时间&#xff08;摸鱼时间&#xff09;和精力&#…

吐血整理,性能测试方法与步骤详细,进阶测试之路...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 总体方向&#xf…

HTML5 sessionStorage会话存储

sessionStorage 是HTML5新增的一个会话存储对象&#xff0c;用于临时保存同一窗口(或标签页)的数据&#xff0c;在关闭窗口或标签页之后将会删除这些数据。本篇主要介绍 sessionStorage(会话存储)的使用方式。包括添加、修改、删除等操作。 目录 1. 介绍 1.1 说明 1.2 特点 1…

微信小程序实现一个文字展开收起功能

1.0 需求背景 需求很常见&#xff0c;就是当一行文字过多时&#xff0c;显示省略号&#xff0c;然后显示展开两个字&#xff0c;点击&#xff0c;文字完全展示开&#xff0c;点击收起&#xff0c;回到省略形式&#xff0c;如下图 2.0 需求分析 有了上图&#xff0c;应该能更好…

2023亚马逊云科技中国峰会之Serverless

序言 Amazon Web Services&#xff0c;是Amazon.com推出的一系列云计算服务。 它提供了一系列的基础设施服务、平台服务和软件服务&#xff0c;希望可以帮助我们更轻松地构建和管理基于云的应用程序。 今天来学习一下 Serverless 本文会介绍以下六个模块&#xff1a; 为什么会…

RocketMq 同组消费者 自动设置InstanceName

RocketMq 同组消费者 自动设置InstanceName 一、背景二、处理方法三、源码分析四、总结 一、背景 同组多于1个消费者&#xff0c;如果没单独设置instanceName,默认为DEFAULT。启动时会报如下错误&#xff1a; org.apache.rocketmq.client.exception.MQClientException: The co…

物联网工业触摸屏与防火墙的安全协作

1 前言 随着物联网技术的快速发展&#xff0c;物联网HMI不仅需要提供SCADA级功能库和控件库&#xff08;点击查看物联网HMI功能库和控件库的详细介绍&#xff09;&#xff0c;还需要具备强大的安全性能。虹科物联网HMI内置防火墙功能&#xff0c;识别和阻止未经授权的访问&…

PCI Express --- LTSSM

目录 1. 链路训练和状态机 1.1 Detect 状态 1.1.1 Detect.Quiet 子状态 1.1.2 Detect.Active 子状态 1.2 Polling 状态 1.2.1 Polling.Active 子状态 1.2.2 Polling.Compliance 子状态 1.2.2 Polling.Configuration 子状态 1.2.3 Polling.Speed 子状态 1.3 Configuration 状…

性能测试超细总结,如何才能做到有效压测?性能压测看这篇就够了...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 目标制定以及业务…

java SSM 游戏资讯系统myeclipse开发mysql数据库springMVC模式java编程计算机网页设计

一、源码特点 java SSM 游戏资讯系统是一套完善的web设计系统&#xff08;系统采用SSM框架进行设计开发&#xff0c;springspringMVCmybatis&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和 数据库&#xff0c;系统主要采用B…

3ds Max - Pivot Painter Tool

很久之前的笔记&#xff0c;整理归档&#xff1b; Pivot Painter Tool是3dsMax中的插件&#xff0c;主要是辅助将Mesh中每个Element生成自己的Pivot Position&#xff0c;方便如使用World Position Offset对每个Element进行精确控制&#xff0c;导入使用Pivot Painter Tool工具…

深入理解Linux虚拟内存管理(七)

系列文章目录 Linux 内核设计与实现 深入理解 Linux 内核 Linux 设备驱动程序 Linux设备驱动开发详解 深入理解Linux虚拟内存管理&#xff08;一&#xff09; 深入理解Linux虚拟内存管理&#xff08;二&#xff09; 深入理解Linux虚拟内存管理&#xff08;三&#xff09; 深入理…

Linux系统和Windows系统下Python2代码转换为Python3代码工具使用指南

简介 本文主要介绍Linux系统和Windows系统下Python2代码转换为Python3代码工具2to3.py或2to3指令使用指南。 项目场景及问题描述 Python2的最后一个版本是2.7&#xff0c;在2020年彻底停止支持。有些环境不方便同时安装Python2和Python3&#xff0c;或者在使用Python3的环境…

【JVM】JVM 垃圾回收算法

文章目录 前言标记清除&#xff08;Mark-Sweep&#xff09;介绍优缺点 复制&#xff08;拷贝 Copying&#xff09;介绍优缺点 标记整理&#xff08;Mark-Compact&#xff09;介绍优缺点 前言 目前JVM中有三种常见的垃圾回收算法&#xff0c;分别是&#xff1a;标记清除、标记整…

Matter实战系列-----1.软硬件开发环境搭建

一、硬件方面 我使用的是一套xG21 BRD4180B和两块xG24 BRD4187C,如下图&#xff1a; 1.1 RCP&#xff1a; 芯片型号EFR32MG21A020F1024IM32 1.2 Matter Light/Switch over Thread&#xff1a; 芯片型号EFR32MG24B220F1536IM48 1.3 蓝牙5.0 USB dongle 注意由于Linux对蓝牙…

阿里、字节、网易面试必考,黑马【爆火】微服务项目发布

最近&#xff0c;收到一位粉丝投稿&#xff0c;他说&#xff1a;“阿里三面凉凉了&#xff0c;输在了微服务上。” 在看到微服务的面试题后&#xff0c;整个人都是懵的&#xff0c;发现没有经验的自己&#xff0c;一窍不通。 如今&#xff0c;微服务已经成为Java开发者必备的…

深入篇【C++】string类的常用接口介绍:标准库中的string类 【万字总结】

深入篇【C】string类的常用接口介绍&#xff1a;标准库中的string类 Ⅰ.string类介绍Ⅱ.string类的常用接口①.string类对象的常用构造1.string()2.string(const char*ch)3.string(const string& str)4.string(size_t n,char c)5.string(const string& str,size_t pos,…

想开发测试工具,应该如何入手?

何为测试工具&#xff1f;就是能辅助测试同学来完成特定的操作的工具&#xff0c;比如常见的如postman、Fiddler、Charles、jira&#xff0c;包括jmeter等&#xff0c;当然还包括公司自己开发的用例转换工具&#xff0c;造数工具&#xff0c;Mock工具或是平台等等。一般以应用程…

测试在“鸡头”和“凤尾”间如何选择?

经常在知乎上碰到这样的问题&#xff1a;同时拿到多个offer&#xff0c;公司有大有小&#xff0c;有创业型有成熟性&#xff0c;怎么在“鸡头”和“凤尾”间做选择&#xff1f; 为什么会纠结呢&#xff1f;通常创业型公司&#xff0c;给优秀的测试员的薪酬远高于市场平均值&…