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

news2024/11/19 12:40:39

一·、装饰者模式

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();
    }

    @Override
    public String getDesc() {
        return super.getDesc() + getFastFood().getDesc();
    }
}
package com.yanyu.Decorator;

//培根配料
public class Bacon extends Garnish {

    public Bacon(FastFood fastFood) {

        super(fastFood,2,"培根");
    }

    @Override
    public float cost() {
        return getPrice() + getFastFood().getPrice();
    }

    @Override
    public 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;
    }

    @Override
    public 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());
        }
    }

    @Override
    public 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;
    }

    // 实现数据源接口中的读取数据方法
    @Override
    public String readData() {
        return datasource.readData();
    }

    // 实现数据源接口中的写入数据方法
    @Override
    public 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; // 压缩级别,默认为6

    public CompressionDecorator(DataSource datasource) {
        super(datasource);
    }

    public int getCompressionLevel() {
        return compLevel;
    }

    public void setCompressionLevel(int value) {
        compLevel = value;
    }

    @Override
    public String readData() {
        // 对读取的数据进行解压缩
        return decompress(datasource.readData());
    }

    @Override
    public 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);
    }

    // 重写读取数据方法,对读取的数据进行解密操作
    @Override
    public String readData() {
        String encryptedData = datasource.readData();
        return decode(encryptedData);
    }

    // 重写写入数据方法,对写入的数据进行加密操作
    @Override
    public 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.coloradmin.cn/o/1233250.html

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

相关文章

如何给面试官解释什么是分布式和集群?

分布式&#xff08;distributed&#xff09; 是指在多台不同的服务器中部署不同的服务模块&#xff0c;通过远程调用协同工作&#xff0c;对外提供服务。 集群&#xff08;cluster&#xff09; 是指在多台不同的服务器中部署相同应用或服务模块&#xff0c;构成一个集群&#…

你真的了解 Cookie 和 Session 吗?

文章目录 Cookie 和 Session总结 Cookie 和 Session cookie HTTP cookie&#xff08;web cookie、browser cookie&#xff09;是服务器发送给用户 web 浏览器的一小段数据。浏览器可能会存储 cookie&#xff0c;并在以后的请求中将其发送回同一台服务器。通常&#xff0c;HTTP …

数据库中的undo与redo的作用

undo与redo的作用 ​专栏内容&#xff1a; 手写数据库toadb 本专栏主要介绍如何从零开发&#xff0c;开发的步骤&#xff0c;以及开发过程中的涉及的原理&#xff0c;遇到的问题等&#xff0c;让大家能跟上并且可以一起开发&#xff0c;让每个需要的人成为参与者。 本专栏会定期…

Git - 版本控制系统

一、概述 git是一个免费开源&#xff0c;分布式的代码版本控制系统&#xff0c;帮助开发团队维护代码。作用是记录代码内容&#xff0c;切换代码版本&#xff0c;多人开发时高效合并代码内容。 直接安装git&#xff0c;默认下一步&#xff0c;然后就能在VScode中看到git控制台 …

YOLOv8更换骨干网络HorNet:递归门控卷积的高效高阶空间交互——涨点神器!

🗝️YOLOv8实战宝典--星级指南:从入门到精通,您不可错过的技巧   -- 聚焦于YOLO的 最新版本, 对颈部网络改进、添加局部注意力、增加检测头部,实测涨点 💡 深入浅出YOLOv8:我的专业笔记与技术总结   -- YOLOv8轻松上手, 适用技术小白,文章代码齐全,仅需 …

海康威视监控相机的SDK与opencv调用(非工业相机)

1.研究内容 本篇主要对海康威视的监控相机的SDK回调进行研究&#xff0c;并于opencv结合&#xff0c;保存图像,以供后续其他处理&#xff0c;开发语言为C 2.步骤及方法 2.1 海康SDK介绍 海康SDK下载地址 根据自身编译环境&#xff0c;下载对应的SDK&#xff0c;需要注意的是…

rotation matrix reflection matrix

文章目录 1. rotation matrix1.1 结论 2. reflection matrix2.1 结论 1. rotation matrix 图像逆时针旋转 θ \theta θ的矩阵 Q r o t a t e [ cos ⁡ θ − sin ⁡ θ sin ⁡ θ cos ⁡ θ ] (1) Q_{rotate}\begin{bmatrix}\cos\theta&-\sin\theta\\\sin\theta&\c…

LOJ #10134. 「一本通 4.4 练习 1」Dis

分析 根据数据范围分析一下复杂度&#xff0c;Floyd和dj算法都必爆。 发现题目说的是树&#xff0c;还是边还是双向的&#xff08;树本身就是无向的&#xff0c;连通无回路的无向图叫做无向树&#xff0c;简称树。如果题目说了树&#xff0c;那么默认边就是双向的&#xff09…

腾讯云服务器标准型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;腾…

uniapp和vue3+ts创建自定义下拉选择框组件

使用uniapp开发小程序的时候&#xff0c;使用了uview的ui组件&#xff0c;但是里面没有下拉选择组件&#xff0c;只有Picker 选择器&#xff0c;但是我们想要使用下拉选择的组件&#xff0c;所以需要自定义个一个下拉选择的自定义组件&#xff0c;我就只能自己动手创建这个自定…

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

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

【OpenGauss源码学习 —— 列存储(ColumnTableSample)】

执行算子&#xff08;ColumnTableSample&#xff09; 概述ColumnTableSample 类ColumnTableSample::ColumnTableSample 构造函数ColumnTableSample::~ColumnTableSample 析构函数ExecCStoreScan 函数ColumnTableSample::scanVecSample 函数ColumnTableSample::getMaxOffset 函数…

前端实现移动端Tab栏(附带源码)

文章目录 先上图,附带源码index.html 主要视图层Main.css 主要样式demo.css主要的JS,在index.html 引入即可先上图,附带源码 提示:一款JS和CSS3炫酷Tabbar导航栏动画特效。该Tabbar导航栏在点击切换时,会有类似波浪运动的动画效果,非常炫酷。 index.html 主要视图层 &l…

Hadoop-- hdfs

1、HDFS中的三个进程&#xff1a;NameNode&#xff08;NN&#xff09;、DataNode(DN)、SecondNameNode(SNN) 2、NameNode&#xff08;NN&#xff09; 1、作用&#xff1a; 1、接收客户端的一个读、写的服务&#xff0c;在namenode上存储了数据文件和datanode的映射的关系。 …

Python实现WOA智能鲸鱼优化算法优化随机森林回归模型(RandomForestRegressor算法)项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 鲸鱼优化算法 (whale optimization algorithm,WOA)是 2016 年由澳大利亚格里菲斯大学的Mirjalili 等提…

[C语言 数据结构] 栈

1.什么是栈&#xff1f; 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端 称为栈顶&#xff0c;另一端称为栈底。栈中的数据元素遵守后进先出LIFO&#xff08;Last In First Out&#xff09;的原则。 压…

Java高级编程-----网络编程

网络通信协议 通过计算机网络可以实现多台计算机连接&#xff0c;但是不同计算机的操作系统和硬件体系结构不同&#xff0c;为了提供通信支持&#xff0c;位于同一个网络中的计算机在进行连接和通信时必须要遵守一定的规则&#xff0c;这就好比在道路中行驶的汽车一定要遵守交…

C++ LibCurl实现Web隐藏目录扫描

LibCurl是一个开源的免费的多协议数据传输开源库&#xff0c;该框架具备跨平台性&#xff0c;开源免费&#xff0c;并提供了包括HTTP、FTP、SMTP、POP3等协议的功能&#xff0c;使用libcurl可以方便地进行网络数据传输操作&#xff0c;如发送HTTP请求、下载文件、发送电子邮件等…

Android AIDL中使用Surface问题

1.构建ITest.aidl文件 package com.xxx.xxxx;import android.view.Surface;interface IMonitorService {boolean addSurface(in Surface surface);boolean removeSurface(in Surface surface); } 2.构建时报错 3.Surface源码分析 android.view.Surface中包含两个Surface类&am…

泛型进阶:通配符

基本概念 对泛型不了解的可以看这篇博客&#xff1a;数据结构前瞻-CSDN博客 一般来说&#xff0c;&#xff1f;在泛型里的使用就是通配符 看看下面的代码 class Message<T> {private T message ;public T getMessage() {return message;}public void setMessage(T m…