【六】设计模式~~~结构型模式~~~适配器模式(Java)

news2024/11/19 2:31:58

【学习难度:★★☆☆☆,使用频率:★★★★☆】

1.1. 模式动机

  • 在软件开发中采用类似于电源适配器的设计和编码技巧被称为适配器模式。
  • 通常情况下,客户端可以通过目标类的接口访问它所提供的服务。有时,现有的类可以满足客户类的功能需要,但是它所提供的接口不一定是客户类所期望的,这可能是因为现有类中方法名与目标类中定义的方法名不一致等原因所导致的。
  • 在这种情况下,现有的接口需要转化为客户类期望的接口,这样保证了对现有类的重用。如果不进行这样的转化,客户类就不能利用现有类所提供的功能,适配器模式可以完成这样的转化。
  • 在适配器模式中可以定义一个包装类,包装不兼容接口的对象,这个包装类指的就是适配器(Adapter),它所包装的对象就是适配者(Adaptee),即被适配的类。
  • 适配器提供客户类需要的接口,适配器的实现就是把客户类的请求转化为对适配者的相应接口的调用。也就是说:当客户类调用适配器的方法时,在适配器类的内部将调用适配者类的方法,而这个过程对客户类是透明的,客户类并不直接访问适配者类。因此,适配器可以使由于接口不兼容而不能交互的类可以一起工作。这就是适配器模式的模式动机。

1.2. 模式定义

       适配器模式(Adapter Pattern) :将一个接口转换成客户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。

1.3. 模式结构

       适配器模式包含如下角色:

  • Target:目标抽象类,目标抽象类定义客户所需接口,可以是一个抽象类或接口,也可以是具体类。
  • Adapter:适配器类,适配器可以调用另一个接口,作为一个转换器,对Adaptee和Target进行适配,适配器类是适配器模式的核心,在对象适配器中,它通过继承Target并关联一个Adaptee对象使二者产生联系。
  • Adaptee:适配者类,适配者即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配,适配者类一般是一个具体类,包含了客户希望使用的业务方法,在某些情况下可能没有适配者类的源代码。
  • Client:客户类,根据对象适配器模式结构图,在对象适配器中,客户端需要调用request()方法,而适配者类Adaptee没有该方法,但是它所提供的specificRequest()方法却是客户端所需要的。为了使客户端能够使用适配者类,需要提供一个包装类Adapter,即适配器类。这个包装类包装了一个适配者的实例,从而将客户端与适配者衔接起来,在适配器的request()方法中调用适配者的specificRequest()方法。因为适配器类与适配者类是关联关系(也可称之为委派关系),所以这种适配器模式称为对象适配器模式。典型的对象适配器代码如下所示:
class Adapter extends Target {
	private Adaptee adaptee; //维持一个对适配者对象的引用
	
	public Adapter(Adaptee adaptee) {
		this.adaptee=adaptee;
	}
	
	public void request() {
		adaptee.specificRequest(); //转发调用
	}
}

       适配器模式有对象适配器和类适配器两种实现:在对象适配器模式中,适配器与适配者之间是关联关系;在类适配器模式中,适配器与适配者之间是继承(或实现)关系。在实际开发中,对象适配器的使用频率更高。

对象适配器:
在这里插入图片描述

类适配器:
在这里插入图片描述

1.4. 时序图

在这里插入图片描述

1.5. 代码分析
       Sunny软件公司开发人员决定使用适配器模式来重用算法库中的算法,其基本结构如图9-4所示:
在这里插入图片描述

1.5.1 生产

       在图9-4中,ScoreOperation接口充当抽象目标,QuickSort和BinarySearch类充当适配者,OperationAdapter充当适配器。完整代码如下所示:

package com.zyz.demo;

/**
 * @author zyz
 * @version 1.0
 * @data 2023/5/13 10:20
 * @Description: 定义适配者:具体完成的接口方法、适配器:接口请求转化。抽象目标接口:客户端实际调用接口
 */
/**
 *  抽象成绩操作类:目标接口
 */
interface ScoreOperation {
    public int[] sort(int array[]); //成绩排序
    public int search(int array[],int key); //成绩查找
}


/**
 * 快速排序类:适配者
 */
class QuickSort {
    public int[] quickSort(int array[]) {
        sort(array,0,array.length-1);
        return array;
    }

    public void sort(int array[],int p, int r) {
        int q=0;
        if(p<r) {
            q=partition(array,p,r);
            sort(array,p,q-1);
            sort(array,q+1,r);
        }
    }

    public int partition(int[] a, int p, int r) {
        int x=a[r];
        int j=p-1;
        for (int i=p;i<=r-1;i++) {
            if (a[i]<=x) {
                j++;
                swap(a,j,i);
            }
        }
        swap(a,j+1,r);
        return j+1;
    }

    public void swap(int[] a, int i, int j) {
        int t = a[i];
        a[i] = a[j];
        a[j] = t;
    }
}

/**
 * 二分查找类:适配者
 */
class BinarySearch {
    public int binarySearch(int array[],int key) {
        int low = 0;
        int high = array.length -1;
        while(low <= high) {
            int mid = (low + high) / 2;
            int midVal = array[mid];
            if(midVal < key) {
                low = mid +1;
            }
            else if (midVal > key) {
                high = mid -1;
            }
            else {
                return 1; //找到元素返回1
            }
        }
        return -1;  //未找到元素返回-1
    }
}


/**
 * 适配器
 */
class OperationAdapter implements ScoreOperation{
    private QuickSort sortObj; //定义适配者QuickSort对象
    private  BinarySearch searchObj; //定义适配者BinarySearch对象

    public OperationAdapter(){
        sortObj = new QuickSort();
        searchObj = new BinarySearch();
    }

    @Override
    public int[] sort(int[] array) {
        return sortObj.quickSort(array);
    }

    @Override
    public int search(int[] array, int key) {
        return searchObj.binarySearch(array,key);
    }
}

1.5.2 客户端

编写如下客户端测试代码:

package com.zyz.demo;

/**
 * @author zyz
 * @version 1.0
 * @data 2023/5/13 10:29
 * @Description: 客户端
 */
public class Client {
    public static void main(String[] args) {
        ScoreOperation operation; //针对抽象目标接口编程
        operation = new OperationAdapter();

        int scores[] = {84, 76, 50, 69, 90, 91, 88, 96}; //定义成绩数组
        int result[];
        int score;

        System.out.println("成绩排序结果:");
        result = operation.sort(scores);
        for (int i : result) {
            System.out.print(i + ",");
        }
        System.out.println(" ");

        System.out.println("查找成绩90:");
        score = operation.search(result,90);
        if (score != -1) {
            System.out.println("找到成绩90。");
        }
        else {
            System.out.println("没有找到成绩90。");
        }

        System.out.println("查找成绩92:");
        score = operation.search(result,92);
        if (score != -1) {
            System.out.println("找到成绩92。");
        }
        else {
            System.out.println("没有找到成绩92。");
        }
    }
}

1.5.3 结果

在这里插入图片描述

1.6. 模式分析

1.7. 实例

同1.5 代码分析案例

1.8. 优点

  • 将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,而无须修改原有代码。
  • 增加了类的透明性和复用性,将具体的实现封装在适配者类中,对于客户端类来说是透明的,而且提高了适配者的复用性。
  • 灵活性和扩展性都非常好,通过使用配置文件,可以很方便地更换适配器,也可以在不修改原有代码的基础上增加新的适配器类,完全符合“开闭原则”。

       类适配器模式还具有如下优点:
由于适配器类是适配者类的子类,因此可以在适配器类中置换一些适配者的方法,使得适配器的灵活性更强。

       对象适配器模式还具有如下优点:
一个对象适配器可以把多个不同的适配者适配到同一个目标,也就是说,同一个适配器可以把适配者类和它的子类都适配到目标接口。

1.9. 缺点

       类适配器模式的缺点如下:
       对于Java、C#等不支持多重继承的语言,一次最多只能适配一个适配者类,而且目标抽象类只能为抽象类,不能为具体类,其使用有一定的局限性,不能将一个适配者类和它的子类都适配到目标接口。

       对象适配器模式的缺点如下:
       与类适配器模式相比,要想置换适配者类的方法就不容易。如果一定要置换掉适配者类的一个或多个方法,就只好先做一个适配者类的子类,将适配者类的方法置换掉,然后再把适配者类的子类当做真正的适配者进行适配,实现过程较为复杂。

1.10. 适用环境

在以下情况下可以使用适配器模式:

  • 系统需要使用现有的类,而这些类的接口不符合系统的需要。
  • 想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。

1.11. 模式应用

       Sun公司在1996年公开了Java语言的数据库连接工具JDBC,JDBC使得Java语言程序能够与数据库连接,并使用SQL语言来查询和操作数据。JDBC给出一个客户端通用的抽象接口,每一个具体数据库引擎(如SQL Server、Oracle、MySQL等)的JDBC驱动软件都是一个介于JDBC接口和数据库引擎接口之间的适配器软件。抽象的JDBC接口和各个数据库引擎API之间都需要相应的适配器软件,这就是为各个不同数据库引擎准备的驱动程序。

1.12. 模式扩展

       认适配器模式(Default Adapter Pattern)或缺省适配器模式
当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可有选择地覆盖父类的某些方法来实现需求,它适用于一个接口不想使用其所有的方法的情况。因此也称为单接口适配器模式。

1.13. 总结

  • 结构型模式描述如何将类或者对象结合在一起形成更大的结构。
  • 适配器模式用于将一个接口转换成客户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作,其别名为包装器。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。
  • 适配器模式包含四个角色:目标抽象类定义客户要用的特定领域的接口;适配器类可以调用另一个接口,作为一个转换器,对适配者和抽象目标类进行适配,它是适配器模式的核心;适配者类是被适配的角色,它定义了一个已经存在的接口,这个接口需要适配;在客户类中针对目标抽象类进行编程,调用在目标抽象类中定义的业务方法。
  • 在类适配器模式中,适配器类实现了目标抽象类接口并继承了适配者类,并在目标抽象类的实现方法中调用所继承的适配者类的方法;在对象适配器模式中,适配器类继承了目标抽象类并定义了一个适配者类的对象实例,在所继承的目标抽象类方法中调用适配者类的相应业务方法。
  • 适配器模式的主要优点是将目标类和适配者类解耦,增加了类的透明性和复用性,同时系统的灵活性和扩展性都非常好,更换适配器或者增加新的适配器都非常方便,符合“开闭原则”;类适配器模式的缺点是适配器类在很多编程语言中不能同时适配多个适配者类,对象适配器模式的缺点是很难置换适配者类的方法。
  • 适配器模式适用情况包括:系统需要使用现有的类,而这些类的接口不符合系统的需要;想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类一起工作。

1.14 扩展(读取xml文件)

       为了让系统具备良好的灵活性和可扩展性,我们引入了工具类XMLUtil和配置文件,其中,XMLUtil类的代码如下所示:

package com.zyz.demo.config;

import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.xml.sax.SAXException;
import java.io.*;

/**
 * @author zyz
 * @version 1.0
 * @data 2023/5/13 11:04
 * @Description:
 */
public class XMLUtil {
    /**
     * 该方法用于从XML配置文件中提取具体类类名,并返回一个实例对象
     * @return
     */
    public static Object getBean() {
        try {
            String path = "F:\\java学习资料(后端)\\github管理后端学习资料\\后端学习\\设计模式\\代码\\DesignPatterns-Java-Examples\\6. 适配器模式\\src\\main\\resources\\config.xml";
            //创建文档对象
            DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = dFactory.newDocumentBuilder();
            Document doc;
            doc = builder.parse(new File(path));

            //获取包含类名的文本节点
            NodeList nl = doc.getElementsByTagName("className");
            Node classNode=nl.item(0).getFirstChild();
            String cName=classNode.getNodeValue();

            //通过类名生成实例对象并将其返回
            Class c=Class.forName("com.zyz.demo."+cName);
            Object obj=c.newInstance();
            return obj;
        }
        catch(Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

配置文件config.xml中存储了适配器类的类名,代码如下所示:

<?xml version="1.0"?>
<config>
    <className>MyOperationAdapter</className>
</config>

编写如下客户端测试代码:

package com.zyz.demo;

import com.zyz.demo.config.XMLUtil;

/**
 * @author zyz
 * @version 1.0
 * @data 2023/5/13 11:04
 * @Description:
 */
public class Client1 {
    public static void main(String[] args) {
        ScoreOperation operation; //针对抽象目标接口编程
        operation = (ScoreOperation) XMLUtil.getBean();

        int scores[] = {84, 76, 50, 69, 90, 91, 88, 96}; //定义成绩数组
        int result[];
        int score;

        System.out.println("成绩排序结果:");
        result = operation.sort(scores);
        for (int i : result) {
            System.out.print(i + ",");
        }
        System.out.println(" ");

        System.out.println("查找成绩90:");
        score = operation.search(result,90);
        if (score != -1) {
            System.out.println("找到成绩90。");
        }
        else {
            System.out.println("没有找到成绩90。");
        }

        System.out.println("查找成绩92:");
        score = operation.search(result,92);
        if (score != -1) {
            System.out.println("找到成绩92。");
        }
        else {
            System.out.println("没有找到成绩92。");
        }
    }
}

测试结果
在这里插入图片描述

1.15 类适配器

       除了对象适配器模式之外,适配器模式还有一种形式,那就是类适配器模式,类适配器模式和对象适配器模式最大的区别在于适配器和适配者之间的关系不同,对象适配器模式中适配器和适配者之间是关联关系,而类适配器模式中适配器和适配者是继承关系,类适配器模式结构如图9-5所示:
在这里插入图片描述

                    图 9-5 类适配器模式结构图

       根据类适配器模式结构图,适配器类实现了抽象目标类接口Target,并继承了适配者类,在适配器类的request()方法中调用所继承的适配者类的specificRequest()方法,实现了适配。
典型的类适配器代码如下所示:

class Adapter extends Adaptee implements Target {
	public void request() {
		specificRequest();
	}
}

       由于Java、C#等语言不支持多重类继承,因此类适配器的使用受到很多限制,例如如果目标抽象类Target不是接口,而是一个类,就无法使用类适配器;此外,如果适配者Adaptee为最终(Final)类,也无法使用类适配器。在Java等面向对象编程语言中,大部分情况下我们使用的是对象适配器,类适配器较少使用。

1.16双向适配器

       在对象适配器的使用过程中,如果在适配器中同时包含对目标类和适配者类的引用,适配者可以通过它调用目标类中的方法,目标类也可以通过它调用适配者类中的方法,那么该适配器就是一个双向适配器,其结构示意图如图9-6所示:

在这里插入图片描述

双向适配器的实现较为复杂,其典型代码如下所示:

class Adapter implements Target,Adaptee {
    //同时维持对抽象目标类和适配者的引用
	private Target target;
	private Adaptee adaptee;
	
	public Adapter(Target target) {
		this.target = target;
	}
	
	public Adapter(Adaptee adaptee) {
		this.adaptee = adaptee;
	}
	
	public void request() {
		adaptee.specificRequest();
	}
	
	public void specificRequest() {
		target.request();
	}
}

在实际开发中,我们很少使用双向适配器。

1.17缺省适配器

       缺省适配器模式是适配器模式的一种变体,其应用也较为广泛。缺省适配器模式的定义如下:

       缺省适配器模式(Default Adapter Pattern):当不需要实现一个接口所提供的所有方法时,可先设计一个抽象类实现该接口,并为接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可以选择性地覆盖父类的某些方法来实现需求,它适用于不想使用一个接口中的所有方法的情况,又称为单接口适配器模式。

缺省适配器模式结构如图9-7所示:
在这里插入图片描述

       在缺省适配器模式中,包含如下三个角色:

  • ServiceInterface(适配者接口):它是一个接口,通常在该接口中声明了大量的方法。
  • AbstractServiceClass(缺省适配器类):它是缺省适配器模式的核心类,使用空方法的形式实现了在ServiceInterface接口中声明的方法。通常将它定义为抽象类,因为对它进行实例化没有任何意义。
  • ConcreteServiceClass(具体业务类):它是缺省适配器类的子类,在没有引入适配器之前,它需要实现适配者接口,因此需要实现在适配者接口中定义的所有方法,而对于一些无须使用的方法也不得不提供空实现。在有了缺省适配器之后,可以直接继承该适配器类,根据需要有选择性地覆盖在适配器类中定义的方法。

       在JDK类库的事件处理包java.awt.event中广泛使用了缺省适配器模式,如WindowAdapter、KeyAdapter、MouseAdapter等。下面我们以处理窗口事件为例来进行说明:在Java语言中,一般我们可以使用两种方式来实现窗口事件处理类,一种是通过实现WindowListener接口,另一种是通过继承WindowAdapter适配器类。如果是使用第一种方式,直接实现WindowListener接口,事件处理类需要实现在该接口中定义的七个方法,而对于大部分需求可能只需要实现一两个方法,其他方法都无须实现,但由于语言特性我们不得不为其他方法也提供一个简单的实现(通常是空实现),这给使用带来了麻烦。而使用缺省适配器模式就可以很好地解决这一问题,在JDK中提供了一个适配器类WindowAdapter来实现WindowListener接口,该适配器类为接口中的每一个方法都提供了一个空实现,此时事件处理类可以继承WindowAdapter类,而无须再为接口中的每个方法都提供实现。如图9-8所示:
在这里插入图片描述

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

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

相关文章

全渠道电子商务指南

希望将全渠道电子商务纳入您的业务战略&#xff0c;但不确定从哪里开始。这篇博文将指导您了解全渠道商务的基础知识&#xff0c;以及它与多渠道方法的区别。 全渠道电子商务旨在为线上和线下多个平台的客户创造全面的购物体验。客户可以通过各种接触点&#xff08;例如网站、…

网络安全-02-BurpSuite工具安装

网络安全-02-BurpSuite工具安装 &#x1f53b;一、BurpSuite简介&下载&#x1f4d7; 二、Windows安装Jdk&#x1f4f0; 2.1 下载Jdk并安装&#x1f4f0; 2.2 设置Jdk环境变量&#x1f4dc; 2.2.1 设置JAVA_HOME&#x1f4dc; 2.2.2 设置path&#x1f4dc; 2.2.3 Jdk验证 &a…

数据库基础——8.单行函数

这篇文章我们来讲一下数据库里面的单行函数 目录 1. 函数的理解 1.1 什么是函数 1.2 不同DBMS函数的差异 1.3 MySQL的内置函数及分类 2. 数值函数 2.1 基本函数 2.2 角度与弧度互换函数 2.3 三角函数 2.4 指数与对数 2.5 进制间的转换 3. 字符串函数 4. 日期和…

PyCharm配置Opencv

具体方案&#xff1a; Plan A&#xff1a;可以直接在pycharm中配置&#xff08;如果有梯子可以试试&#xff0c;没有可能下载不了&#xff09; 参考视频&#xff1a;直接在Pycharm中配置即可 两分钟用Pycharm安装并配置OpenCV_哔哩哔哩_bilibili PlanB&#xff1a;通过终端安…

dolphinscheduler3.1.7windows部署启动说明

简介 Apache DolphinScheduler是一个新一代分布式大数据工作流任务调度平台&#xff0c;致力于“解决大数据任务之间错综复杂的依赖关系&#xff0c;整个数据处理开箱即用”。它以 DAG(有向无环图) 的方式将任务连接起来&#xff0c;可实时监控任务的运行状态&#xff0c;同时…

01Redis单线程 VS 多线程

不同版本&#xff0c;情况不同 Redis的版本很多3.x、4.x、6.x&#xff0c;版本不同架构也是不同的&#xff0c;不限定版本问是否单线程也不太严谨。 版本3.x &#xff0c;最早版本&#xff0c;也就是大家口口相传的redis是单线程 数据结构简单避免锁的开销和上下文切换可以有…

YOLOv7 tiny 新增小目标检测层

YOLOv7 tiny 新增小目标检测层 YOLOv7 tiny 新增小目标检测层修改yolov7-tiny.yaml文件YOLOv7 tiny 结构图调用 models/yolo.py验证 YOLOv7 tiny 新增小目标检测层 根据已有的结构进行新增小目标层&#xff0c;&#xff0c;个人理解&#xff0c;仅供参考&#xff01;&#xff…

GPT模型应用及遥感云大数据在灾害、水体与湿地领域典型案例展示

GPT GPT的全称&#xff0c;是Generative Pre-Trained Transformer&#xff08;生成式预训练Transformer模型&#xff09;是一种基于互联网的、可用数据来训练的、文本生成的深度学习模型。 GPT与专注于下围棋或机器翻译等某一个具体任务的“小模型”不同&#xff0c;AI大模型…

Seata XA 模式理论学习、使用及注意事项 | Spring Cloud54

一、前言 通过以下系列章节&#xff1a; docker-compose 实现Seata Server高可用部署 | Spring Cloud 51 Seata AT 模式理论学习、事务隔离及部分源码解析 | Spring Cloud 52 Spring Boot集成Seata利用AT模式分布式事务示例 | Spring Cloud 53 我们对Seata及其AT事务模式的…

chatgpt赋能python:Python下载后的打开方法

Python 下载后的打开方法 Python是一种流行的编程语言&#xff0c;用于开发各种类型的应用程序&#xff0c;从Web应用程序到数据分析工具再到科学计算应用程序。许多人下载了Python&#xff0c;但是不确定如何在计算机上安装和打开它。在本篇文章中&#xff0c;我们将讨论Pyth…

如何实现UI交互?

UE5 插件开发指南 前言0 交易物品1 拖放物品1.0 拖放在背包里1.1 拖放在快捷栏里1.2 拖放在商店里1.3 放在其他地方2 小结前言 之前实现了UI的显示,这一篇补充UI的交互功能实现,先把思维导图画出来,程序员的自我修养就是任何时候都要理清思路,思路清晰是编码的前提,如下所…

解决幂等问题

1.概述 作为开发人员&#xff0c;我们每天都要开发大量的接口&#xff0c;其中包括了读接口和写接口&#xff0c;而对于写接口来说&#xff0c;除了要保证他的性能、可用性以外&#xff0c;还需要有一个重要的问题&#xff0c;那就是考虑如何保证接口的幂等性。 “幂等&#x…

mysql数据库连接失败授权认证 reading authorization packet --别的电脑可以,但是本地电脑连接失败

1.分析原因是否由于本机多次失败连接导致 在能连接的主机上或者服务器上 查看当前数据库阻断连接次数是否在不断增长 SHOW GLOBAL STATUS LIKE Aborted_connects;1.2观察这个终端连接次数是否一直在增长 如果一直在增长&#xff0c;说明mysql服务器被计算机不断的进行连接且被…

安科瑞针对HVDC方案推出的解决方案介绍

​安科瑞虞佳豪 摘 要&#xff1a;近年来&#xff0c;随着数据中心的迅猛发展&#xff0c;数据中心的能耗问题也越来越突出&#xff0c;有关数据中心的能源管理和供配电设计已经成为热门问题&#xff0c;合理可靠的数据中心配电系统方案&#xff0c;是提高数据中心电能使用效率…

数据结构与算法05:跳表和散列表

目录 【跳表】 跳表的实现原理 如何确定跳表的层高&#xff1f; 【散列表】 散列函数的设计 散列冲突 &#xff08;1&#xff09;开放寻址法&#xff08;Open Addressing&#xff09; &#xff08;2&#xff09;链表法&#xff08;chaining&#xff09; 装载因子 如何…

如何把两个视频拼接在一起?这些工具太棒了!

如何把两个视频拼接在一起&#xff1f;对于自媒体行业的人来讲&#xff0c;平时在进行短视频的剪辑时&#xff0c;总会需要合并多个视频&#xff0c;之后再剪辑处理。不过对于大部分没有从事自媒体行业的人来讲&#xff0c;可能对合并视频并不了解。下面我就来给大家分享可以拼…

paddleclas pp-shitu v1/v2

null集开放数据、开源算法、免费算力三位一体&#xff0c;为开发者提供高效学习和开发环境、高价值高奖金竞赛项目&#xff0c;支撑高校老师轻松实现AI教学&#xff0c;并助力开发者学习交流&#xff0c;加速落地AI业务场景https://aistudio.baidu.com/aistudio/projectdetail/…

BlackIce病毒分析

概述 blackice是一个古老的感染型病毒&#xff0c;可感染系统中exe、doc和xls文件&#xff0c;通过USB设备和网络驱动器来传播&#xff0c;会向C&C下载pe执行&#xff0c;会关闭常用的杀软进程。下面找了一个样本&#xff0c;这个样本的代码结构清晰&#xff0c;用IDA pro…

客服配置-shopro

客服配置 注意事项 shopro客服系统 采用 workerman 的 gateway-worker 作为服务基础&#xff0c;请先安装 gateway-worker 扩展包shopro商城 已不再支持 workerman 在线客服插件 安装部署 安装扩展包 composer require workerman/gateway-worker:~3.0 删除禁用函数(如有未列…

Postman中加url环境变量和token全局变量

环境变量引用 语法&#xff1a;{{变量名}} 环境变量分类 1. 全局变量&#xff1a;全局有效&#xff0c;在Postman中的任何集合中都可以使用该变量&#xff0c;作用域最大。 2. 环境变量&#xff1a;要申明环境变量&#xff0c;先要创建环境&#xff0c;然后在该环境中创建变…