设计模式补

适配器模式(Adapter Pattern)

适配器类继承A目标接口,把B非适配器类当适配器类的参数,用适配器类使用A目标接口的方法

是一种结构型设计模式,它的主要目的是使原本接口不兼容的类可以一起工作。适配器模式通过创建一个适配器类来包装现有类的接口,使其看起来像另一个接口,从而使得原本不兼容的类可以协同工作。

适配器模式的目的

  1. 接口转换:使一个类的接口匹配另一个类的接口,即使它们之间没有继承关系。
  2. 增加灵活性:允许系统使用不兼容接口的对象,而无需修改现有代码。
  3. 复用现有类:通过适配器模式,可以轻松地复用已有的类而不必修改它们的源代码。
  4. 隔离变化:通过引入适配器,可以在不改变现有代码的情况下添加新的功能。

适配器模式的使用场景

  1. 现有类库的复用:当你有一个已经存在的类库,但是它的接口与你的应用不兼容时,可以通过适配器模式来桥接这两个类库之间的接口差异。
  2. 第三方组件集成:在集成第三方组件时,第三方组件的API可能与你的系统不兼容,这时可以使用适配器模式来解决接口不匹配的问题。
  3. 遗留系统的改造:当你需要更新遗留系统的一部分时,可以通过适配器模式将新旧接口进行转换,以维持系统的连续性。
  4. 简化复杂的接口:有时你需要使用的类具有非常复杂的接口,而你只需要其中的一部分功能。这时可以使用适配器模式来创建一个更简洁的接口。
  5. 统一多个相似接口:如果有多个类具有相似的功能但接口不同,可以通过适配器模式为这些类提供一个统一的接口。
  6. // 目标接口
    class Target {request() {return "Target: The default target's behavior.";}
    }// 不兼容的适配者类
    class Adaptee {specificRequest() {return "Adaptee: The adaptee's behavior.";}
    }// 适配器类
    class Adapter extends Target {constructor(adaptee) {super();this.adaptee = adaptee;}request() {const result = super.request();const additionalBehavior = this.adaptee.specificRequest();return `${result} Adapter: Also adapts the adaptee's behavior ${additionalBehavior}`;}
    }// 使用
    const adaptee = new Adaptee();
    const adapter = new Adapter(adaptee);console.log(adapter.request());
    // Output: Target: The default target's behavior. Adapter: Also adapts the adaptee's behavior Adaptee: The adaptee's behavior.

    单例模式

  7. 是一种常用的软件设计模式,它的目的是确保一个类只有一个实例,并提供一个全局访问点来访问这个实例。这种模式在很多情况下都非常有用,特别是在需要频繁地访问同一个对象的情况下,而且这个对象的创建成本较高或其状态需要在整个系统中保持一致时。

饿汉式单例

目的
  • 确保线程安全:饿汉式单例在类加载时就完成了实例化,因此不需要担心多线程环境下的同步问题。
  • 简单直接:由于在类加载时就已经创建好了实例,所以无需额外的同步机制来保证实例的唯一性。
用途
  • 当单例对象的创建成本较低,且需要确保在应用程序启动时就准备好该对象时。
  • 当应用程序不需要考虑延迟加载单例对象的情况。

汉式单例

目的
  • 延迟加载:懒汉式单例只有在第一次被请求时才创建实例,这样可以节省资源,特别是在单例对象创建成本较高时。
  • 按需初始化:只有在真正需要时才会创建单例对象,适用于资源密集型对象。
用途
  • 当单例对象的创建成本较高,且可能并不总是需要使用它时。
  • 当希望最小化应用程序启动时的资源占用时。

原型模式(Prototype Pattern)

是一种创建型设计模式,它使用一个现有的对象作为原型,并通过复制该原型对象来创建新的对象。这种模式的主要目的是避免创建过程中的重复计算或初始化工作,提高性能和效率。

原型模式的目的

  1. 避免创建过程中的开销:当创建一个新对象的成本很高时(例如,需要复杂的初始化或从数据库获取数据),使用原型模式可以避免这些开销。
  2. 简化对象创建:通过克隆一个已有实例,可以简化对象的创建过程,特别是当对象的创建过程涉及大量的配置或设置时。
  3. 增强灵活性:原型模式允许对象的创建在运行时进行定制,这增加了系统的灵活性。

使用场景

  1. 创建复杂的对象:当对象的创建过程很复杂时,使用原型模式可以避免每次创建新对象时都需要重新初始化的过程。
  2. 性能敏感的应用:在性能敏感的应用中,避免重复的创建和初始化过程可以显著提高性能。
  3. 对象池:在需要大量相似对象的情况下,使用原型模式可以有效地管理对象池,减少资源消耗。
  4. 配置对象:当需要根据不同的配置创建不同的对象实例时,使用原型模式可以简化配置过程。

import java.util.Date;// 定义一个原型接口
interface Prototype<T> extends Cloneable {T clone();
}// 具体的实现类
class Person implements Prototype<Person> {private String name;private Date birthday;public Person(String name, Date birthday) {this.name = name;this.birthday = birthday;}public String getName() {return name;}public Date getBirthday() {return birthday;}@Overridepublic Person clone() {try {// 浅拷贝Person person = (Person) super.clone();// 深拷贝person.birthday = (Date) this.birthday.clone();return person;} catch (CloneNotSupportedException e) {throw new AssertionError();}}
}// 使用示例
public class PrototypeExample {public static void main(String[] args) {Date date = new Date();Person original = new Person("Alice", date);Person cloned = original.clone();System.out.println("Original: " + original.getName() + ", " + original.getBirthday());System.out.println("Cloned: " + cloned.getName() + ", " + cloned.getBirthday());}
}

观察者模式(Observer Pattern)

是一种行为型设计模式,它定义了对象之间的一种一对多依赖关系,使得每当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。这种模式在Java中有着广泛的应用,尤其是在事件驱动的系统和GUI应用程序中。

观察者模式的目的

  1. 解耦:观察者模式有助于解耦主体(Subject)和观察者(Observer)。主体不知道观察者的存在,观察者也不知道其他观察者的存在。
  2. 动态订阅/取消订阅:观察者可以动态地订阅或取消订阅主题的变化通知。
  3. 扩展性:当需要添加新的观察者时,不需要修改主体的代码,符合开闭原则。

使用场景

  1. 事件处理:在事件驱动的系统中,观察者模式可以用来处理各种事件,例如按钮点击事件、窗口关闭事件等。
  2. GUI应用程序:在图形用户界面中,当用户与界面交互时,观察者模式可以用来更新界面元素的状态。
  3. 数据绑定:在MVC架构中,观察者模式可以用来实现视图对模型的自动更新。
  4. 多线程应用:在多线程环境中,观察者模式可以用来处理异步事件的通知。
  5. 日志记录:当系统中的日志级别发生变化时,可以使用观察者模式来通知所有相关组件进行相应的更新。
// 观察者接口
interface Observer {void update(String message);
}// 主题接口
interface Subject {void registerObserver(Observer observer);void removeObserver(Observer observer);void notifyObservers(String message);
}// 具体的主题实现
class NewsAgency implements Subject {// 存储观察者的列表private List<Observer> observers = new ArrayList<>();private String news;// 设置新闻,并通知所有观察者public void setNews(String news) {this.news = news;notifyObservers(news);}// 注册观察者@Overridepublic void registerObserver(Observer observer) {observers.add(observer);}// 移除观察者@Overridepublic void removeObserver(Observer observer) {observers.remove(observer);}// 通知所有观察者@Overridepublic void notifyObservers(String message) {for (Observer observer : observers) {observer.update(message);}}
}// 具体的观察者实现
class NewsPaper implements Observer {private String name;// 构造函数public NewsPaper(String name) {this.name = name;}// 当有新闻更新时,更新方法会被调用@Overridepublic void update(String message) {System.out.println(name + " received: " + message);}
}// 使用示例
public class ObserverExample {public static void main(String[] args) {// 创建新闻机构NewsAgency agency = new NewsAgency();// 创建报纸观察者NewsPaper newspaper1 = new NewsPaper("Daily News");NewsPaper newspaper2 = new NewsPaper("Evening Post");// 注册报纸观察者agency.registerObserver(newspaper1);agency.registerObserver(newspaper2);// 发布新闻agency.setNews("New event occurred.");}
}

策略模式(Strategy Pattern)

建一个对象,可以用同一个方法,调用功能相同类不同的方法.

是一种行为型设计模式,它定义了一系列算法,并将每一个算法封装起来,使它们可以互相替换。策略模式让算法的变化独立于使用算法的客户。

策略模式的目的

  1. 封装算法:将算法封装成独立的类,使得算法的实现可以独立于使用它的客户。
  2. 算法互换:可以在运行时根据需要选择不同的算法来解决问题。
  3. 易于扩展:当需要添加新的算法时,只需要添加新的策略类即可,不需要修改原有的代码。
  4. 解耦:策略模式通过定义一系列的策略类来解耦算法与使用算法的客户。

使用场景

  1. 算法的多种实现:当一个系统中存在多种算法实现,且这些算法可以互相替换时,可以使用策略模式。
  2. 条件判断的替代:如果在一个系统中频繁地使用条件语句(如 if-else 或 switch-case)来选择不同的行为,则可以使用策略模式来简化这些条件判断。
  3. 行为参数化:当需要将行为参数化时,可以使用策略模式来实现。
// 策略接口
interface SortingStrategy {void sort(int[] numbers);
}// 具体策略实现 - 冒泡排序
class BubbleSortStrategy implements SortingStrategy {@Overridepublic void sort(int[] numbers) {bubbleSort(numbers);}// 冒泡排序算法private void bubbleSort(int[] numbers) {int n = numbers.length;boolean swapped;for (int i = 0; i < n - 1; i++) {swapped = false;for (int j = 0; j < n - 1 - i; j++) {if (numbers[j] > numbers[j + 1]) {int temp = numbers[j];numbers[j] = numbers[j + 1];numbers[j + 1] = temp;swapped = true;}}if (!swapped) break;}}
}// 具体策略实现 - 快速排序
class QuickSortStrategy implements SortingStrategy {@Overridepublic void sort(int[] numbers) {quickSort(numbers, 0, numbers.length - 1);}// 快速排序算法private void quickSort(int[] arr, int low, int high) {if (low < high) {int pi = partition(arr, low, high);quickSort(arr, low, pi - 1);quickSort(arr, pi + 1, high);}}// 分区算法private int partition(int[] arr, int low, int high) {int pivot = arr[high];int i = (low - 1);for (int j = low; j < high; j++) {if (arr[j] < pivot) {i++;int temp = arr[i];arr[i] = arr[j];arr[j] = temp;}}int temp = arr[i + 1];arr[i + 1] = arr[high];arr[high] = temp;return i + 1;}
}// 上下文类
class SortContext {private SortingStrategy strategy;// 构造函数public SortContext(SortingStrategy strategy) {this.strategy = strategy;}// 设置策略public void setStrategy(SortingStrategy strategy) {this.strategy = strategy;}// 排序方法public void sort(int[] numbers) {strategy.sort(numbers);}
}// 使用示例
public class StrategyExample {public static void main(String[] args) {int[] numbers = {5, 3, 2, 4, 1};// 创建上下文对象并设置初始策略为冒泡排序SortContext context = new SortContext(new BubbleSortStrategy());// 排序并打印结果context.sort(numbers);printArray(numbers);// 更改策略为快速排序context.setStrategy(new QuickSortStrategy());// 再次排序并打印结果context.sort(numbers);printArray(numbers);}// 打印数组private static void printArray(int[] numbers) {for (int number : numbers) {System.out.print(number + " ");}System.out.println();}
}

 

责任链模式(Chain of Responsibility Pattern)

是一种行为型设计模式,它允许请求沿着处理者链传递,直到有一个处理者处理它为止。每个处理者都包含对下一个处理者的引用,因此可以形成一条链。

责任链模式的目的

  1. 解耦:通过将请求的发送者和接收者解耦,使得发送者不需要知道请求最终由谁处理。
  2. 动态分配责任:可以在运行时动态地添加、删除或重新排列处理者,从而改变请求的处理顺序。
  3. 简化对象:每个处理者对象只负责自己的职责,不关心后续处理者的行为,这使得对象更加单一职责化。

使用场景

  1. 异常处理:在异常处理中,可以使用责任链模式来处理不同类型的异常,每个处理者负责处理特定类型的异常。
  2. 权限验证:在权限管理系统中,可以使用责任链模式来处理不同级别的权限验证。
  3. 日志记录:在日志记录系统中,可以使用责任链模式来处理不同级别的日志信息。
  4. HTTP请求处理:在Web框架中,可以使用责任链模式来处理HTTP请求,例如过滤器(Filter)模式就是责任链模式的一个应用场景。
  5. GUI事件处理:在GUI应用程序中,可以使用责任链模式来处理鼠标点击、键盘输入等事件。

 通过这个示例,我们可以清楚地看到责任链模式如何应用于Web框架中的过滤器模式。每个过滤器都有机会处理请求,并可以决定是否继续传递给下一个过滤器。这种模式使得我们可以轻松地添加新的过滤器,而不需要修改现有的代码结构。

 

// 处理者接口
interface Filter {void doFilter(Request request, Response response, FilterChain filterChain);
}// 请求对象
class Request {private String url;public Request(String url) {this.url = url;}public String getUrl() {return url;}
}// 响应对象
class Response {// 可以添加响应相关的属性和方法
}// 过滤器链
class FilterChain {private List<Filter> filters = new ArrayList<>();public void addFilter(Filter filter) {filters.add(filter);}public void doFilter(Request request, Response response) {for (Filter filter : filters) {filter.doFilter(request, response, this);}}
}// 具体过滤器实现
class AuthenticationFilter implements Filter {@Overridepublic void doFilter(Request request, Response response, FilterChain filterChain) {System.out.println("AuthenticationFilter: Checking authentication for request to " + request.getUrl());filterChain.doFilter(request, response); // 传递给下一个过滤器}
}class LoggingFilter implements Filter {@Overridepublic void doFilter(Request request, Response response, FilterChain filterChain) {System.out.println("LoggingFilter: Logging request to " + request.getUrl());filterChain.doFilter(request, response); // 传递给下一个过滤器}
}class PerformanceFilter implements Filter {@Overridepublic void doFilter(Request request, Response response, FilterChain filterChain) {long startTime = System.currentTimeMillis();filterChain.doFilter(request, response); // 传递给下一个过滤器long endTime = System.currentTimeMillis();System.out.println("PerformanceFilter: Request took " + (endTime - startTime) + " ms");}
}// 客户端类
public class WebFrameworkExample {public static void main(String[] args) {// 创建过滤器链FilterChain filterChain = new FilterChain();// 添加过滤器filterChain.addFilter(new AuthenticationFilter());filterChain.addFilter(new LoggingFilter());filterChain.addFilter(new PerformanceFilter());// 创建请求Request request = new Request("/some-resource");// 创建响应Response response = new Response();// 发送请求filterChain.doFilter(request, response);}
}

 下面是关于这段代码的调用流程和内部关系的详细说明,包括各个组件之间的关系是包含还是并列。

### 处理者接口 `Filter`
- **定义**:定义了一个 `doFilter` 方法,用于处理请求,并传递给下一个过滤器。
- **关系**:这是一个接口,被具体的过滤器实现类继承。

### 请求对象 `Request`
- **定义**:代表了一个HTTP请求,包含了URL等信息。
- **关系**:请求对象是过滤器链中的传递对象,每个过滤器都需要访问它。

### 响应对象 `Response`
- **定义**:代表了一个HTTP响应,可以包含响应相关的属性和方法。
- **关系**:响应对象是过滤器链中的传递对象,每个过滤器都可以修改它。

### 过滤器链 `FilterChain`
- **定义**:包含了过滤器列表,并提供了方法来添加过滤器和遍历过滤器列表来处理请求。
- **关系**:过滤器链包含多个过滤器对象,这些过滤器对象按顺序排列,形成了一个链表结构。

### 具体过滤器实现
- **AuthenticationFilter**:检查用户的认证状态。
- **LoggingFilter**:记录请求的日志信息。
- **PerformanceFilter**:记录请求的处理时间。
- **关系**:这些过滤器实现类实现了 `Filter` 接口,并在 `doFilter` 方法中处理请求,并可以决定是否继续传递给下一个过滤器。

### 客户端类 `WebFrameworkExample`
- **定义**:创建过滤器链、添加过滤器、创建请求和响应,并发送请求。
- **关系**:客户端类是外部调用者,它创建过滤器链和请求/响应对象,并触发过滤器链的处理过程。

### 调用流程
1. **创建过滤器链**:在 `WebFrameworkExample` 中,创建了一个 `FilterChain` 对象。
2. **添加过滤器**:通过调用 `addFilter` 方法,向过滤器链中添加了三个过滤器:`AuthenticationFilter`、`LoggingFilter` 和 `PerformanceFilter`。
3. **创建请求和响应**:创建了一个 `Request` 对象和一个 `Response` 对象。
4. **发送请求**:通过调用 `filterChain.doFilter(request, response)` 来发送请求。
5. **过滤器处理**:
   - 首先,`AuthenticationFilter` 被调用,它会输出认证信息,并调用 `filterChain.doFilter(request, response)` 来传递给下一个过滤器。
   - 接下来,`LoggingFilter` 被调用,它会输出日志信息,并同样调用 `filterChain.doFilter(request, response)` 来传递给下一个过滤器。
   - 最后,`PerformanceFilter` 被调用,它会记录处理时间,并结束整个过滤器链的处理过程。

### 内部关系
- **包含关系**:`FilterChain` 包含了多个过滤器对象。
- **并列关系**:各个过滤器实现类之间是并列关系,它们都实现了 `Filter` 接口,但各自负责不同的任务。

### 总结
在这个示例中,`FilterChain` 是一个中心组件,它包含了多个过滤器对象,并负责将请求传递给这些过滤器。每个过滤器都实现了相同的接口,并可以决定是否继续传递请求给下一个过滤器。这种结构使得我们可以轻松地添加新的过滤器,而不影响现有的过滤器链。

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

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

相关文章

查看路由表 netstat -r

“Kernel IP routing table” 是Linux系统中用于展示和配置IP路由的表。它告诉操作系统如何将数据包从一个网络接口发送到另一个网络或主机。下面是对您给出的路由表条目的解释&#xff1a; Destination&#xff1a;目的地地址&#xff0c;可以是具体的IP地址&#xff0c;也可…

C#:通用方法总结—第8集

大家好&#xff0c;今天继续讲解我们的通用方法系列。 下面是今天分享的通用方法&#xff1a; &#xff08;1&#xff09;这个通用方法为Ug删除参数构建器方法&#xff1a; public static int RemoveParameters(int id)//删除参数构建器 { UFSession.GetUFSession().Undo.Se…

Android 一个简单线程更新UI

new Thread(new Runnable() {Overridepublic void run() {//执行的业务runOnUiThread(new Runnable() {Overridepublic void run() {//更新UI//Loading("信息处理中");}});} }).start();

Vue3学习总结-v-if与v-show的使用案例和区别

&#x1f4ca; Vue 3 实战&#xff1a;v-if 与 v-show 在用户界面切换中的应用 在 Vue.js 中&#xff0c;v-if 和 v-show 是两种常用的条件渲染指令&#xff0c;它们允许我们根据表达式的值来动态地显示或隐藏 DOM 元素。这两种指令虽然看起来相似&#xff0c;但在内部实现和性…

ctfshow解题方法

171 172 爆库名->爆表名->爆字段名->爆字段值 -1 union select 1,database() ,3 -- //返回数据库名 -1 union select 1,2,group_concat(table_name) from information_schema.tables where table_schema库名 -- //获取数据库里的表名 -1 union select 1,group_concat(…

WebGIS学习——Cesium|Javascript

1.Cesium学习什么&#xff1a;Cesium实战项目说明-CSDN博客 2.Cesium绘制图形(箭头等):Cesium 态势标绘 _cesium态势标绘-CSDN博客 3.CesiumThree集成 4.Cesium深度图相关&#xff1a;Cesium离屏渲染深度图实验_cesium 离屏渲染-CSDN博客 5.洪涝&#xff1a;cesium淹没分析…

Github 2024-07-25 Go开源项目日报 Top10

根据Github Trendings的统计,今日(2024-07-25统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Go项目10Testify - Go代码测试工具包 创建周期:4237 天开发语言:Go协议类型:MIT LicenseStar数量:22206 个Fork数量:1550 次关注人数:222…

TCP连接中的过程状态解析

TCP建立连接三次握手的过程 TCP是一个面向连接的协议&#xff0c;所以在连接双方发送数据之前&#xff0c;都需要首先建立一条连接。 Client连接Server三次握手过程&#xff1a; 当Client端调用socket函数调用时&#xff0c;相当于Client端产生了一个处于Closed状态的套接字…

Python爬虫入门02:Fiddler下载使用教程

文章目录 手机抓包全攻略&#xff1a;Fiddler 工具深度解析引言Fiddler 工具简介为什么选择 Fiddler&#xff1f; 安装与配置 Fiddler步骤一&#xff1a;下载与安装步骤二&#xff1a;配置浏览器代理步骤三&#xff1a;安装 HTTPS 证书 配置手机以使用 Fiddler步骤一&#xff1…

操作系统面试知识点总结5

#来自ウルトラマンメビウス&#xff08;梦比优斯&#xff09; 1 IO管理概述 1.1 I/O 设备 I/O 设备的类型分类。 1.1.1 按使用特性 人机交互类外部设备&#xff0c;例如打印机、显示器等。存储设备&#xff0c;例如磁盘、光盘等。网络通信设备&#xff0c;例如网络接口等。 1…

PHP While循环

PHP 中的 while 循环是一种基本的循环控制结构&#xff0c;它允许你重复执行一段代码块&#xff0c;直到指定的条件不再满足为止。while 循环在需要执行未知次数的代码块时非常有用&#xff0c;特别是当你需要在满足特定条件时继续执行循环时。 基本语法 while (条件表达式) …

21 B端产品经理之技术常识(1)

产品经理需要掌握一些基本的技术知识。 了解公司前端与后端 前端 前端开发&#xff1a;创建WEB页面或APP等前端界面呈现给用户的过程&#xff0c;即前端负责用户界面交互。 前端技能&#xff1a; HTML&#xff1a;一种标记语言&#xff0c;能够实现Web页面并在浏览器中显示。…

【计算机网络】IP地址和子网掩码(IP地址篇)

个人主页&#xff1a;【&#x1f60a;个人主页】 系列专栏&#xff1a;【❤️计算机网络】 文章目录 前言IP地址网络地址网络地址的定义与组成作用分类网络地址的分配与管理 广播地址&#xff08;Broadcast Address&#xff09;定义构成类型作用注意事项 广播地址功能 组播地址…

HiveSQL题——炸裂+开窗

一、每个学科的成绩第一名是谁&#xff1f; 0 问题描述 基于学生成绩表输出每个科目的第一名是谁呢&#xff1f; 1 数据准备 with t1 as(selectzs as name,[{"Chinese":80},{"Math":70}],{"English"…

Windows NVM(Node Version Manager)使用指南

Windows NVM&#xff08;Node Version Manager&#xff09;使用指南 引言 Node Version Manager (NVM) 是一个用于管理多个 Node.js 版本的工具&#xff0c;它允许开发者在同一台机器上安装和使用不同版本的 Node.js。虽然 NVM 最初是为 Unix-like 系统设计的&#xff0c;但 …

单链表的基本操作

定义一个结点 typedef struct ElemType{char name[20];int sex;int age;char number[11]; };//定义一个结点 typedef struct Lnode{ ElemType data;struct Lnode* next; }LNode,*LinkList;链表的初始化 一般包含两步&#xff1a;给结点分配空间&#xff0c;头指针指针域…

CompletableFuture使用详解

简单的任务&#xff0c;用Future获取结果还好&#xff0c;但我们并行提交的多个异步任务&#xff0c;往往并不是独立的&#xff0c;很多 时候业务逻辑处理存在串行[依赖]、并行、聚合的关系。如果要我们手动用 Future 实现&#xff0c;是非常麻烦的。 CompletableFutur…

JMeter基本使用

一、JMeter线程组相关 进程&#xff1a;正在运行的程序。线程&#xff1a;是进程中的执行线索。线程组&#xff1a;进程中有许多线程&#xff0c;为了方便管理&#xff0c;可以对线程按照性质分组&#xff0c;分组的结果就是线程组。PS&#xff1a;三者关系&#xff0c;一个进…

Pytest设置日志

在 pytest 中,您可以使用内置的日志功能来记录测试过程中的信息。pytest 默认使用了 logging 模块来处理日志记录。此外,您还可以使用 pytest 的插件来增强日志功能,比如 pytest-loguru 插件,它结合了 loguru 库来提供更强大的日志管理能力。 1. 使用内置的日志功能 设置…

【设计模式】(万字总结)深入理解Java中的创建型设计模式

1. 前言 在软件开发的世界里&#xff0c;设计模式是一种被广泛接受并应用的解决方案。它们不仅仅是代码的设计&#xff0c;更是对问题的思考和解决的方法论。在Java开发中&#xff0c;特别是在面向对象的编程中&#xff0c;设计模式尤为重要。创建型设计模式&#xff0c;作为设…