万字解析设计模式之 装饰者模式

一·、装饰者模式

1.1概述

装饰者模式是一种结构型设计模式,它允许在运行时动态地为一个对象添加额外的职责。它以一种透明的方式来扩展对象的功能,而不需要通过子类来实现。在装饰者模式中,有一个基本对象,也称为组件,它可以被一个或多个装饰器包装。装饰器不改变基本对象本身的行为,而是在基本对象的行为之前或之后添加一些额外的行为。这样可以轻松地构建出复杂的对象,而不需要使用大量的子类。

动态地给一个对象增加一些额外的职责,就增加对象功能来说,装饰模式比生成子类实现更为灵活

我们先来看一个快餐店的例子。

快餐店有炒面、炒饭这些快餐,可以额外附加鸡蛋、火腿、培根这些配菜,当然加配菜需要额外加钱,每个配菜的价钱通常不太一样,那么计算总价就会显得比较麻烦。

使用继承的方式存在的问题:

  • 扩展性不好

    如果要再加一种配料(火腿肠),我们就会发现需要给FriedRice和FriedNoodles分别定义一个子类。如果要新增一个快餐品类(炒河粉)的话,就需要定义更多的子类。

  • 产生过多的子类

1.2结构

装饰(Decorator)模式中的角色:

  • 抽象构件(Component)角色 :定义一个抽象接口以规范准备接收附加责任的对象。
  • 具体构件(Concrete Component)角色 :实现抽象构件,通过装饰角色为其添加一些职责。
  • 抽象装饰(Decorator)角色 : 继承或实现抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
  • 具体装饰(ConcreteDecorator)角色 :实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。

 1.3实现

我们使用装饰者模式对快餐店案例进行改进,体会装饰者模式的精髓。

类图如下:

抽象组件

package com.yanyu.Decorator;// 定义一个抽象组件类,即快餐接口,包括价格和描述
public abstract class FastFood {private float price;  // 定义价格private String desc;  // 定义描述public FastFood() {}public FastFood(float price, String desc) {  // 构造函数,传入价格和描述this.price = price;this.desc = desc;}public void setPrice(float price) {  // 设置价格this.price = price;}public float getPrice() {  // 获取价格return price;}public String getDesc() {  // 获取描述return desc;}public void setDesc(String desc) {  // 设置描述this.desc = desc;}public abstract float cost();  // 获取价格的抽象方法
}

具体构件;

package com.yanyu.Decorator;//炒饭
public class FriedRice extends FastFood {public FriedRice() {super(10, "炒饭");}public float cost() {return getPrice();}
}
package com.yanyu.Decorator;//炒面
public class FriedNoodles extends FastFood {public FriedNoodles() {super(12, "炒面");}public float cost() {return getPrice();}
}

抽象装饰者

package com.yanyu.Decorator;// 定义一个抽象装饰者类,即配料类,继承自快餐接口
public abstract class Garnish extends FastFood {private FastFood fastFood;  // 定义一个快餐接口类型的对象,即被装饰的组件public FastFood getFastFood() {  // 获取被装饰的组件return fastFood;}public void setFastFood(FastFood fastFood) {  // 设置被装饰的组件this.fastFood = fastFood;}public Garnish(FastFood fastFood, float price, String desc) {  // 构造函数,传入被装饰的组件、价格和描述super(price, desc);  // 调用父类的构造函数,初始化价格和描述this.fastFood = fastFood;  // 初始化被装饰的组件}
}

具体装饰者

package com.yanyu.Decorator;//鸡蛋配料
public class Egg extends Garnish {public Egg(FastFood fastFood) {super(fastFood,1,"鸡蛋");}public float cost() {return getPrice() + getFastFood().getPrice();}@Overridepublic String getDesc() {return super.getDesc() + getFastFood().getDesc();}
}
package com.yanyu.Decorator;//培根配料
public class Bacon extends Garnish {public Bacon(FastFood fastFood) {super(fastFood,2,"培根");}@Overridepublic float cost() {return getPrice() + getFastFood().getPrice();}@Overridepublic String getDesc() {return super.getDesc() + getFastFood().getDesc();}
}

客户端类

// 测试类
public class Client {public static void main(String[] args) {// 点一份炒饭FastFood food = new FriedRice();// 花费的价格System.out.println(food.getDesc() + " " + food.cost() + "元");System.out.println("========");// 点一份加鸡蛋的炒饭FastFood food1 = new FriedRice();food1 = new Egg(food1);  // 使用装饰者模式给炒饭加上鸡蛋// 花费的价格System.out.println(food1.getDesc() + " " + food1.cost() + "元");System.out.println("========");// 点一份加培根的炒面FastFood food2 = new FriedNoodles();food2 = new Bacon(food2);  // 使用装饰者模式给炒面加上培根// 花费的价格System.out.println(food2.getDesc() + " " + food2.cost() + "元");}
}

好处:

  • 饰者模式可以带来比继承更加灵活性的扩展功能,使用更加方便,可以通过组合不同的装饰者对象来获取具有不同行为状态的多样化的结果。装饰者模式比继承更具良好的扩展性,完美的遵循开闭原则,继承是静态的附加责任,装饰者则是动态的附加责任。
  • 装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。

1.4使用场景

  • 如果你希望在无需修改代码的情况下即可使用对象, 且希望在运行时为对象新增额外的行为, 可以使用装饰模式。

装饰能将业务逻辑组织为层次结构, 你可为各层创建一个装饰, 在运行时将各种不同逻辑组合成对象。 由于这些对象都遵循通用接口, 客户端代码能以相同的方式使用这些对象。

  • 如果用继承来扩展对象行为的方案难以实现或者根本不可行, 你可以使用该模式。

许多编程语言使用 final最终关键字来限制对某个类的进一步扩展。 复用最终类已有行为的唯一方法是使用装饰模式: 用封装器对其进行封装。

1.5JDK源码解析

装饰者模式(Decorator Pattern)是一种结构型设计模式,它允许我们动态地给一个对象添加额外的行为,是继承关系的一种替代方案。

在 JDK 中,装饰者模式被广泛地使用,比如 `java.io` 包中的输入输出流就是一个很好的例子。在这个包中,`InputStream` 和 `OutputStream` 是抽象基类,它们定义了基本的输入输出操作。`FilterInputStream` 和 `FilterOutputStream` 是装饰者类,它们继承了基类并添加了额外的行为,比如缓冲读写、数据压缩等。下面以 `BufferedInputStream` 为例进行介绍。

public class BufferedInputStream extends FilterInputStream {protected volatile byte buf[];protected int count;protected int pos;protected int markpos = -1;protected int marklimit;// ...public BufferedInputStream(InputStream in) {this(in, DEFAULT_BUFFER_SIZE);}public BufferedInputStream(InputStream in, int size) {super(in);if (size <= 0) {throw new IllegalArgumentException("Buffer size <= 0");}buf = new byte[size];}// ...}

可以看到,`BufferedInputStream` 继承了 `FilterInputStream`,并重载了 `InputStream` 的部分方法。它包含一个字节数组 `buf`,用来缓存输入数据,从而减少底层输入流的读取次数,提高效率。

public class FilterInputStream extends InputStream {protected volatile InputStream in;protected FilterInputStream(InputStream in) {this.in = Objects.requireNonNull(in);}// ...}

`FilterInputStream` 和 `FilterOutputStream` 都是抽象类,它们继承了基类并添加了构造函数和 `in` 或 `out` 属性,用于保存被装饰的基础流。

在这个例子中,`BufferedInputStream` 充当了装饰者的角色,它在 `InputStream` 的基础上添加了缓存的功能,从而满足了更加复杂的输入需求。这个模式让我们能够在运行时动态地添加或修改对象的行为,而不需要修改原始对象的代码。

1.6代理和装饰者的区别

静态代理和装饰者模式的区别:

  • 相同点:

    • 都要实现与目标类相同的业务接口
    • 在两个类中都要声明目标对象
    • 都可以在不修改目标类的前提下增强目标方法
  • 不同点:

    • 目的不同 装饰者是为了增强目标对象 静态代理是为了保护和隐藏目标对象
    • 获取目标对象构建的地方不同 装饰者是由外界传递进来,可以通过构造方法传递 静态代理是在代理类内部创建,以此来隐藏目标对象

 

 二、装饰者模式实验

任务描述

某系统的读写文件模块,最初的业务逻辑类仅能读取和写入纯文本的数据。 现状系统需要进行安全升级,写数据需要先压缩再加密,读数据需要先解压缩再解密还原。

本关任务:用装饰模式加强原来的文件读写模块。第一个封装器负责加密和解密数据, 而第二个则负责压缩和解压数据。

实现方式

  1. 确保业务逻辑可用一个基本组件及多个额外可选层次表示;

  2. 找出基本组件和可选层次的通用方法。 创建一个组件接口并在其中声明这些方法;

  3. 创建一个具体组件类, 并定义其基础行为;

  4. 创建装饰基类, 使用一个成员变量存储指向被封装对象的引用。 该成员变量必须被声明为组件接口类型, 从而能在运行时连接具体组件和装饰。 装饰基类必须将所有工作委派给被封装的对象;

  5. 确保所有类实现组件接口;

  6. 将装饰基类扩展为具体装饰。 具体装饰必须在调用父类方法 (总是委派给被封装对象) 之前或之后执行自身的行为;

  7. 客户端代码负责创建装饰并将其组合成客户端所需的形式。

编程提示

//压缩函数String compress(String stringData) {byte[] data = stringData.getBytes();try {ByteArrayOutputStream bout = new ByteArrayOutputStream(512);DeflaterOutputStream dos = new DeflaterOutputStream(bout, new Deflater(compLevel));dos.write(data);dos.close();bout.close();return Base64.getEncoder().encodeToString(bout.toByteArray());} catch (IOException ex) {return null;}}
//解压缩函数String decompress(String stringData) {byte[] data = Base64.getDecoder().decode(stringData);try {InputStream in = new ByteArrayInputStream(data);InflaterInputStream iin = new InflaterInputStream(in);ByteArrayOutputStream bout = new ByteArrayOutputStream(512);int b;while ((b = iin.read()) != -1) {bout.write(b);}in.close();iin.close();bout.close();return new String(bout.toByteArray());} catch (IOException ex) {return null;}}
 //加密函数String encode(String data) {byte[] result = data.getBytes();for (int i = 0; i < result.length; i++) {result[i] += (byte) 1;}return Base64.getEncoder().encodeToString(result);}
//解密函数String decode(String data) {byte[] result = Base64.getDecoder().decode(data);for (int i = 0; i < result.length; i++) {result[i] -= (byte) 1;}return new String(result);}

编程要求

根据提示,在右侧编辑器 Begin-End 内补充代码: DataSource:抽象读写构件类; FileDataSource:具体文件读写构件类; DataSourceDecorator:抽象装饰类; CompressionDecorator:具体的字符串压缩/解压缩装饰类; EncryptionDecorator:具体的字符串加密/解密装饰类。

"DataSourceDecorator.java"、"CompressionDecorator.java" 和 "EncryptionDecorator.java",请补全文件中的代码,其它文件的代码不需要修改。

测试说明

平台会自动从文件中读取数据,然后对你编写的代码进行测试:

预期输出: Input ---------------- user,password John100000 Steven912000 Encoded -------------- Zkt4c01WNXUxam1KTUQ1dnt6Okw1Z01Mezloe09CQkNzdkRUMk1NVlFGdUVKekJJQlFjbEQ5Uj4= Decoded -------------- user,password John100000 Steven912000

抽象构件

package step1;// 定义数据源接口,包含写入数据和读取数据的方法
public interface DataSource {void writeData(String data);String readData();
}

具体构件

package step1;import java.io.*;// 具体组件,实现了数据源接口
public class FileDataSource implements DataSource {private String name;public FileDataSource(String name) {this.name = name;}@Overridepublic void writeData(String data) {// 将数据写入文件File file = new File(name);try (OutputStream fos = new FileOutputStream(file)) {fos.write(data.getBytes(), 0, data.length());} catch (IOException ex) {System.out.println(ex.getMessage());}}@Overridepublic String readData() {// 从文件中读取数据char[] buffer = null;File file = new File(name);try (FileReader reader = new FileReader(file)) {buffer = new char[(int) file.length()];reader.read(buffer);} catch (IOException ex) {System.out.println(ex.getMessage());}return new String(buffer);}
}

抽象装饰

package step1;// 定义一个抽象装饰者类
public abstract class DataSourceDecorator implements DataSource {protected DataSource datasource; // 被装饰的数据源对象// 获取被装饰的组件public DataSource getDataSource() {return datasource;}// 设置被装饰的组件public void setDataSource(DataSource datasource) {this.datasource = datasource;}// 构造方法,接受被装饰的数据源对象public DataSourceDecorator(DataSource datasource) {this.datasource = datasource;}// 实现数据源接口中的读取数据方法@Overridepublic String readData() {return datasource.readData();}// 实现数据源接口中的写入数据方法@Overridepublic void writeData(String data) {datasource.writeData(data);}
}

具体装饰

package step1;import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Base64;
import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.InflaterInputStream;public class CompressionDecorator extends DataSourceDecorator {private int compLevel = 6; // 压缩级别,默认为6public CompressionDecorator(DataSource datasource) {super(datasource);}public int getCompressionLevel() {return compLevel;}public void setCompressionLevel(int value) {compLevel = value;}@Overridepublic String readData() {// 对读取的数据进行解压缩return decompress(datasource.readData());}@Overridepublic void writeData(String data) {// 对写入的数据进行压缩datasource.writeData(compress(data));}private String compress(String stringData) {// 使用压缩函数对字符串进行压缩byte[] data = stringData.getBytes();try {ByteArrayOutputStream bout = new ByteArrayOutputStream(512);DeflaterOutputStream dos = new DeflaterOutputStream(bout, new Deflater(compLevel));dos.write(data);dos.close();bout.close();return Base64.getEncoder().encodeToString(bout.toByteArray());} catch (IOException ex) {return null;}}private String decompress(String stringData) {// 使用解压缩函数对字符串进行解压缩byte[] data = Base64.getDecoder().decode(stringData);try {InputStream in = new ByteArrayInputStream(data);InflaterInputStream iin = new InflaterInputStream(in);ByteArrayOutputStream bout = new ByteArrayOutputStream(512);int b;while ((b = iin.read()) != -1) {bout.write(b);}in.close();iin.close();bout.close();return new String(bout.toByteArray());} catch (IOException ex) {return null;}}
}
package step1;import java.util.Base64;// 具体装饰者类,实现了数据加密和解密功能
public class EncryptionDecorator extends DataSourceDecorator {// 构造方法,接收被装饰的数据源对象public EncryptionDecorator(DataSource datasource) {super(datasource);}// 重写读取数据方法,对读取的数据进行解密操作@Overridepublic String readData() {String encryptedData = datasource.readData();return decode(encryptedData);}// 重写写入数据方法,对写入的数据进行加密操作@Overridepublic void writeData(String data) {String encryptedData = encode(data);datasource.writeData(encryptedData);}// 加密方法,使用加密函数对字符串进行加密private String encode(String data) {byte[] result = data.getBytes();for (int i = 0; i < result.length; i++) {result[i] += (byte) 1;}return Base64.getEncoder().encodeToString(result);}// 解密方法,使用解密函数对字符串进行解密private String decode(String data) {byte[] result = Base64.getDecoder().decode(data);for (int i = 0; i < result.length; i++) {result[i] -= (byte) 1;}return new String(result);}
}

客户端类

package step1;import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;public class XMLUtils {public static String getBean(String name) {try {//创建DOM文档对象DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();DocumentBuilder builder = dFactory.newDocumentBuilder();Document doc;doc = builder.parse(new File("/data/workspace/myshixun/src/config.xml"));//获取包含类名的文本结点NodeList nl = doc.getElementsByTagName(name);Node classNode = nl.item(0).getFirstChild();String value = classNode.getNodeValue();return value;}catch(Exception e) {e.printStackTrace();return null;}}
}
package step1;public class Client {public static void main(String[] args) {// 从XML配置文件中获取薪资记录和文件路径String salaryRecords = XMLUtils.getBean("stringValue");String filePath = XMLUtils.getBean("filePath");// 创建一个装饰者对象,先进行加密再进行压缩,最后写入文件DataSourceDecorator encoded = new CompressionDecorator(new EncryptionDecorator(new FileDataSource(filePath)));encoded.writeData(salaryRecords);// 创建一个普通的数据源对象,用于输出原始数据DataSource plain = new FileDataSource(filePath);// 输出原始薪资记录System.out.println("Input ----------------");System.out.println(salaryRecords);// 输出经过装饰者加密和压缩后的数据System.out.println("Encoded --------------");System.out.println(plain.readData());// 输出经过装饰者解密和解压缩后的数据System.out.println("Decoded --------------");System.out.println(encoded.readData());}
}

 

任务描述
某系统的读写文件模块,最初的业务逻辑类仅能读取和写入纯文本的数据。 现状系统需要进行安全升级,写数据需要先压缩再加密,读数据需要先解压缩再解密还原。本关任务:用装饰模式加强原来的文件读写模块。第一个封装器负责加密和解密数据, 而第二个则负责压缩和解压数据。实现方式
确保业务逻辑可用一个基本组件及多个额外可选层次表示;找出基本组件和可选层次的通用方法。 创建一个组件接口并在其中声明这些方法;创建一个具体组件类, 并定义其基础行为;创建装饰基类, 使用一个成员变量存储指向被封装对象的引用。 该成员变量必须被声明为组件接口类型, 从而能在运行时连接具体组件和装饰。 装饰基类必须将所有工作委派给被封装的对象;确保所有类实现组件接口;将装饰基类扩展为具体装饰。 具体装饰必须在调用父类方法 (总是委派给被封装对象) 之前或之后执行自身的行为;客户端代码负责创建装饰并将其组合成客户端所需的形式。编程提示
//压缩函数String compress(String stringData) {byte[] data = stringData.getBytes();try {ByteArrayOutputStream bout = new ByteArrayOutputStream(512);DeflaterOutputStream dos = new DeflaterOutputStream(bout, new Deflater(compLevel));dos.write(data);dos.close();bout.close();return Base64.getEncoder().encodeToString(bout.toByteArray());} catch (IOException ex) {return null;}}
//解压缩函数String decompress(String stringData) {byte[] data = Base64.getDecoder().decode(stringData);try {InputStream in = new ByteArrayInputStream(data);InflaterInputStream iin = new InflaterInputStream(in);ByteArrayOutputStream bout = new ByteArrayOutputStream(512);int b;while ((b = iin.read()) != -1) {bout.write(b);}in.close();iin.close();bout.close();return new String(bout.toByteArray());} catch (IOException ex) {return null;}}//加密函数String encode(String data) {byte[] result = data.getBytes();for (int i = 0; i < result.length; i++) {result[i] += (byte) 1;}return Base64.getEncoder().encodeToString(result);}
//解密函数String decode(String data) {byte[] result = Base64.getDecoder().decode(data);for (int i = 0; i < result.length; i++) {result[i] -= (byte) 1;}return new String(result);}
编程要求
根据提示,在右侧编辑器 Begin-End 内补充代码:
DataSource:抽象读写构件类;
FileDataSource:具体文件读写构件类;
DataSourceDecorator:抽象装饰类;
CompressionDecorator:具体的字符串压缩/解压缩装饰类;
EncryptionDecorator:具体的字符串加密/解密装饰类。"DataSourceDecorator.java"、"CompressionDecorator.java" 和 "EncryptionDecorator.java",请补全文件中的代码,其它文件的代码不需要修改。

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

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

相关文章

SpringBean的配置详解 --中

目录 Bean的初始化和销毁方法配置 Bean的初始化和销毁方法配置 扩展 Bean的实例化 Bean的初始化和销毁方法配置 当lazy-init设置为true时为延迟加载&#xff0c;也就是当Spring容器加载的时候&#xff0c;不会立即创建Bean实例&#xff0c;等待用到时再创建Bean实例并存储到单…

【鸿蒙应用ArkTS开发系列】- 云开发入门实战一使用鸿蒙登录组件实现客户端登录

目录 概述使用云端一体化开发模板创建项目工程创建登录入口页面集成登录SDK组件依赖登录组件SDK使用登录组件SDK 开启“手机号码”和“邮箱地址”认证方式 概述 通过本次课程&#xff0c;我们将学习怎么使用云端一体化开发模板来创建云开发工程&#xff0c;以及如何使用鸿蒙登…

外卖小程序系统:数字化餐饮的编码之道

在当今数字化时代&#xff0c;外卖小程序系统成为了餐饮业的一项技术巨制。这个系统不仅提供了便捷的点餐体验&#xff0c;更通过先进的技术手段&#xff0c;实现了高效订单处理、实时配送追踪以及个性化推荐。让我们深入了解外卖小程序系统的技术魔法&#xff0c;一起揭秘数字…

SSL加密

小王学习录 今日摘录前言HTTP + SSL = HTTPSSSL加密1. 对称加密2. 非对称加密 + 对称加密3. 证书今日摘录 但愿四海无尘沙,有人卖酒仍卖花。 前言 SSL表示安全套接层,是一个用于保护计算机网络中数据传输安全的协议。SSL通过加密来防止第三方恶意截取并篡改数据。在实际应用…

腾讯云标准型S5云主机性能评测_CPU内存_带宽系统盘测评

腾讯云服务器CVM标准型S5实例具有稳定的计算性能&#xff0c;CVM 2核2G S5活动优惠价格280.8元一年自带1M带宽&#xff0c;15个月313.2元、2核4G配置748.2元15个月&#xff0c;CPU内存配置还可以选择4核8G、8核16G等配置&#xff0c;公网带宽可选1M、3M、5M或10M&#xff0c;腾…

基于STM32CubeMX和keil采用RTC时钟周期唤醒和闹钟实现LED与BEEP周期开关

文章目录 前言1. RTC概念1.1 RTC的时钟信号源1.2 预分频器1.3 实时时钟与日历数据1.4 周期性自动唤醒1.5 可编程闹钟 2. RTC相关中断3. STM32CubeMX配置3.1 时钟配置3.2 引脚配置3.3 RTC配置3.3.1 模式选择3.3.2 RTC基本参数配置3.3 中断配置 4. 代码编写总结 前言 RTC的功能有…

Spring Boot中使用AOP统一处理Web请求日志

AOP AOP&#xff0c;也就是 Aspect-oriented Programming&#xff0c;译为面向切面编程&#xff0c;是计算机科学中的一个设计思想&#xff0c;旨在通过切面技术为业务主体增加额外的通知&#xff08;Advice&#xff09;&#xff0c;从而对声明为“切点”&#xff08;Pointcut…

1.0 Zookeeper 教程

分类 Zookeeper 教程 ZooKeeper 是 Apache 软件基金会的一个软件项目&#xff0c;它为大型分布式计算提供开源的分布式配置服务、同步服务和命名注册。 ZooKeeper 的架构通过冗余服务实现高可用性。 Zookeeper 的设计目标是将那些复杂且容易出错的分布式一致性服务封装起来&…

最长公共子串 动态规划

#include<iostream> #include<string.h> #include<stdio.h> #include<math.h> using namespace std; int main(){ string s1,s2; cin>>s1>>s2; if(s1.size() > s2.size()) //以短的作为s1 swap(s1,s2); int le…

详细讲解什么是观察者模式

观察者模式&#xff08;Observer Pattern&#xff09;是一种行为设计模式&#xff0c;它定义了一种一对多的依赖关系&#xff0c;让多个观察者对象同时监听某一个主题对象&#xff0c;当主题对象状态发生变化时&#xff0c;所有依赖于它的观察者都会得到通知并自动更新。 该模…

Git 服务器搭建

目录 1、安装Git 2、创建证书登录 3、初始化Git仓库 4、克隆仓库 上一章节中我们远程仓库使用了 Github&#xff0c;Github 公开的项目是免费的&#xff0c;2019 年开始 Github 私有存储库也可以无限制使用。 当然我们也可以自己搭建一台 Git 服务器作为私有仓库使用。 接…

COCO格式转化为YOLOv8格式

COCO格式转化为YOLOv8格式 目录格式代码 目录格式 yolov8仅支持YOLO格式的标签&#xff0c;COCO的默认标签为JSON格式&#xff0c;所以需要将COCO格式转换为YOLO格式。 如果训练COCO数据集的话一定要按照这个格式&#xff0c;摆放目录images&#xff0c;labels这两个目录名不…

学习c#的第二十三天

目录 C# 委托&#xff08;Delegate&#xff09; 委托概述 使用委托 带有命名方法的委托与匿名方法 示例 如何合并委托&#xff08;多播委托&#xff09; 示例 如何声明、实例化和使用委托 示例 C# 委托&#xff08;Delegate&#xff09; 委托是一种引用类型&#xff…

公网环境固定域名异地远程访问内网BUG管理系统

文章目录 前言1. 本地安装配置BUG管理系统2. 内网穿透2.1 安装cpolar内网穿透2.2 创建隧道映射本地服务3. 测试公网远程访问4. 配置固定二级子域名4.1 保留一个二级子域名5.1 配置二级子域名6. 使用固定二级子域名远程 前言 BUG管理软件,作为软件测试工程师的必备工具之一。在…

浅谈 JVM GC 收集器--系列(一)

又到一年大促时刻&#xff0c;今天我们一起探讨下JVM垃圾回收的问题&#xff0c;写代码的时候想一想如何减少FullGC问题的出现&#xff0c;因为一旦出现频繁FullGC&#xff0c;短时间内没有太好的解决办法&#xff0c;很有可能重启后服务接着FullGC&#xff0c;导致服务可用率降…

【探索嵌入式虚拟化技术与应用】— 虚拟化技术深入浅出自学系列

&#x1f308;个人主页: Aileen_0v0&#x1f525;系列专栏:【探索嵌入式虚拟化技术与应用】&#x1f4ab;个人格言:"没有罗马,那就自己创造罗马~" 目录 一、虚拟技术的发展历史 1.1传统技术的局限性&#xff1a; ​编辑 1.2云计算和万物互联技术的发展机遇&#x…

万宾科技智能井盖传感器,预防城市道路安全

随着城市交通的不断发展和城市化进程的加速推进&#xff0c;城市道路安全问题日益凸显。市政井盖作为城市道路的一部分&#xff0c;承担着重要的交通安全保障职责。然而传统的市政井盖管理方式存在许多不足。针对这些问题政府需要采取适当的措施&#xff0c;补足传统管理方式的…

react原理及合成事件原理

文章目录 react的理解react创建组件的三种写法react的工作原理初始化的渲染流程。页面更新的流程。diffing 算法计算更新视图diff策略 react合成事件原理一、React合成事件的概念二、React合成事件的原理三、React合成事件的优势四、React合成事件的使用方法五、总结 react的理…

小型机加工工厂MES系统选型指南

随着制造业的不断发展&#xff0c;越来越多的企业开始关注生产过程的管理和优化。对于小型机加工工厂来说&#xff0c;选择一款合适的MES系统&#xff08;制造执行系统&#xff09;能够显著提高生产效率、降低成本、优化资源利用&#xff0c;从而在激烈的市场竞争中脱颖而出。 …

HTTP之常见问答

1&#xff1a;HTTP/1.1 如何优化&#xff1f; &#xff1a;尽量避免发送 HTTP 请求&#xff1b;通过缓存技术&#xff0c;使用请求的 Etag 参数来处理判断缓存过期等问题&#xff0c;类似304状态码就是告诉客户端&#xff0c;缓存有效还能继续使用 &#xff1a;在需要发送 HTTP…