01 设计模式-创造型模式-工厂模式

news2024/10/22 23:23:29
  • 工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一,它提供了一种创建对象的方式,使得创建对象的过程与使用对象的过程分离。

  • 工厂模式提供了一种创建对象的方式,而无需指定要创建的具体类。

  • 通过使用工厂模式,可以将对象的创建逻辑封装在一个工厂类中,而不是在客户端代码中直接实例化对象,这样可以提高代码的可维护性和可扩展性。

设计模式,最近持续更新中,如需要请关注

如果你觉得我分享的内容或者我的努力对你有帮助,或者你只是想表达对我的支持和鼓励,请考虑给我点赞、评论、收藏。您的鼓励是我前进的动力,让我感到非常感激。

文章目录

  • 1 概要
  • 2 实现
  • 3 Demo代码
  • 4 开发案例
    • 4.1 算法执行服务里,不同的任务数据来源不同,执行脚本类型不同,结果处理不同,使用工厂模式
    • 4.2 网络还原时,不同的采集数据,解析处理方式不同

1 概要

意图
定义一个创建对象的接口,让其子类决定实例化哪一个具体的类。工厂模式使对象的创建过程延迟到子类。

主要解决
接口选择的问题。

何时使用
当我们需要在不同条件下创建不同实例时。

如何解决
通过让子类实现工厂接口,返回一个抽象的产品。

关键代码
对象的创建过程在子类中实现。

应用实例

  1. 汽车制造:你需要一辆汽车,只需从工厂提货,而不需要关心汽车的制造过程及其内部实现。
  2. Hibernate:更换数据库时,只需更改方言(Dialect)和数据库驱动(Driver),即可实现对不同数据库的切换。

优点

  1. 调用者只需要知道对象的名称即可创建对象。
  2. 扩展性高,如果需要增加新产品,只需扩展一个工厂类即可。
  3. 屏蔽了产品的具体实现,调用者只关心产品的接口。

缺点
每次增加一个产品时,都需要增加一个具体类和对应的工厂,使系统中类的数量成倍增加,增加了系统的复杂度和具体类的依赖。

使用场景

  1. 日志记录:日志可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志的位置。
  2. 数据库访问:当用户不知道最终系统使用哪种数据库,或者数据库可能变化时。
  3. 连接服务器的框架设计:需要支持 “POP3”、“IMAP”、“HTTP” 三种协议,可以将这三种协议作为产品类,共同实现一个接口。
  4. 在算法执行服务中,每个任务需要处理的数据来源不同,根据数据类型创建对应的数据出来handle类,不用关心handle里的内部处理逻辑

注意事项
工厂模式适用于生成复杂对象的场景。如果对象较为简单,通过 new 即可完成创建,则不必使用工厂模式。使用工厂模式会引入一个工厂类,增加系统复杂度。

结构
工厂模式包含以下几个主要角色:

  • 抽象产品(Abstract Product):定义了产品的共同接口或抽象类。它可以是具体产品类的父类或接口,规定了产品对象的共同方法。
  • 具体产品(Concrete Product):实现了抽象产品接口,定义了具体产品的特定行为和属性。
  • 抽象工厂(Abstract Factory):声明了创建产品的抽象方法,可以是接口或抽象类。它可以有多个方法用于创建不同类型的产品。
  • 具体工厂(Concrete Factory):实现了抽象工厂接口,负责实际创建具体产品的对象。

2 实现

我们将创建一个 Shape 接口和实现 Shape 接口的实体类。下一步是定义工厂类 ShapeFactory。

FactoryPatternDemo 类使用 ShapeFactory 来获取 Shape 对象。它将向 ShapeFactory 传递信息(CIRCLE / RECTANGLE / SQUARE),以便获取它所需对象的类型。
请添加图片描述

3 Demo代码

在这里插入图片描述
Shape

/**
 * 公共接口
 */
public interface Shape {
    void draw();
}

Circle

/**
 * 圆形,形状的实现类
 */
public class Circle implements Shape {

    @Override
    public void draw() {
        System.out.println("Inside Circle::draw() method.");
    }
}

Rectangle

/**
 * 矩形,形状的实现类
 */
public class Rectangle implements Shape {

    @Override
    public void draw() {
        System.out.println("Inside Rectangle::draw() method.");
    }
}

Square

/**
 * 方形,形状的实现类
 */
public class Square implements Shape {

    @Override
    public void draw() {
        System.out.println("Inside Square::draw() method.");
    }
}

ShapeFactory

/**
 * 形状工厂类【根据不同的参数创建对应的实例】
 */
public class ShapeFactory {

    //使用 getShape 方法获取形状类型的对象
    public Shape getShape(String shapeType) {
        if (shapeType == null) {
            return null;
        }
        if (shapeType.equalsIgnoreCase("CIRCLE")) {
            return new Circle();
        } else if (shapeType.equalsIgnoreCase("RECTANGLE")) {
            return new Rectangle();
        } else if (shapeType.equalsIgnoreCase("SQUARE")) {
            return new Square();
        }
        return null;
    }
}

FactoryPatternDemo

/**
 *    总结:用来根据不同的参数创建对象
 */
public class FactoryPatternDemo {

    public static void main(String[] args) {
        ShapeFactory shapeFactory = new ShapeFactory();

        //获取 Circle 的对象,并调用它的 draw 方法
        Shape shape1 = shapeFactory.getShape("CIRCLE");
        //调用 Circle 的 draw 方法
        shape1.draw();

        //获取 Rectangle 的对象,并调用它的 draw 方法
        Shape shape2 = shapeFactory.getShape("RECTANGLE");
        //调用 Rectangle 的 draw 方法
        shape2.draw();

        //获取 Square 的对象,并调用它的 draw 方法
        Shape shape3 = shapeFactory.getShape("SQUARE");
        //调用 Square 的 draw 方法
        shape3.draw();
    }
}

效果
在这里插入图片描述

4 开发案例

4.1 算法执行服务里,不同的任务数据来源不同,执行脚本类型不同,结果处理不同,使用工厂模式

请添加图片描述
说明:

  • TaskAbstractFactory命名成抽象工厂,可以创建出来DataSetHandle工厂,ScriptExecuteHandle工厂,ResultHandle工厂,但是实现时,时间关系,简化成简单工厂模式。
  • 工厂返回的对象,每次都是新创建出来的,因为这些handle每次创建初始化的参数是不同的,和下面的第二个案例有所不同

在这里插入图片描述

工厂类

/**
 * 任务抽象工厂类,创建各种处理类
 * @since 2023 -10-08 16:13
 */
public class TaskAbstractFactory {
    private static final Logger LOGGER = LoggerFactory.getLogger(TaskAbstractFactory.class);

    /**
     * Gets script execute handle.
     *
     * @param scriptParam the script param
     * @param algoId the algo id
     * @return the script execute handle
     */
    public static ScriptExecuteHandle getScriptExecuteHandle(ScriptParam scriptParam, String algoId) {
        if (Constants.ScriptType.PYTHON.equals(scriptParam.getScriptType()) || Constants.ScriptType.PYTHON3.equals(
            scriptParam.getScriptType())) {
            return new PythonScriptExecuteHandle(scriptParam);
        } else {
            LOGGER.error("The algorithm type is not supported. algoId: {} ,scriptType: {} ", algoId,
                scriptParam.getScriptType());
            throw new CommonServiceException(AIModelError.ALGO_TYPE_NOT_SUPPORTED);
        }
    }

    /**
     * Gets data set handle list.
     *
     * @param dataSets the data sets
     * @return the data set handle list
     */
    public static List<DataSetHandle> getDataSetHandleList(List<InputDataSource> dataSets) {
        ArrayList<DataSetHandle> dataSetHandleList = new ArrayList<>(10);
        for (InputDataSource inputDataSource : dataSets) {
            dataSetHandleList.add(getDataSetHandle(inputDataSource));
        }
        return dataSetHandleList;
    }

    /**
     * Gets data set handle.
     *
     * @param inputDataSource the input data source
     * @return the data set handle
     */
    public static DataSetHandle getDataSetHandle(InputDataSource inputDataSource) {
        if (Constants.DataSourceType.MINIO_FILE.equalsIgnoreCase(inputDataSource.getDatatype())) {
            DataSetHandle dataSetHandle = new S3DataSetHandle(inputDataSource.getDataSourceInfo(),
                inputDataSource.getDataSourceConfig());
            // 进行数据源校验
            dataSetHandle.checkDataSource();
            return dataSetHandle;
        } else if (Constants.DataSourceType.HDFS_FILE.equalsIgnoreCase(inputDataSource.getDatatype())) {
            DataSetHandle dataSetHandle = new HdfsDataSetHandle(inputDataSource.getDataSourceInfo(),
                inputDataSource.getDataSourceConfig());
            // 进行数据源校验
            dataSetHandle.checkDataSource();
            return dataSetHandle;
        } else {
            LOGGER.error("The data source type is not supported. datatype: {} ", inputDataSource.getDatatype());
            throw new CommonServiceException(AIModelError.DATA_TYPE_NOT_SUPPORTED);
        }
    }

    /**
     * Gets result handle.
     *
     * @param calculateParam the calculate param
     * @param scriptParam the script param
     * @return the result handle
     */
    public static ResultHandle getResultHandle(CalculateParam calculateParam, ScriptParam scriptParam) {
        // 【预留】 设置结果集存放的数据源。目前所有的算法都有数据集,取数据集的第一个数据源作为结果上传的数据源
        OutputDataSource outputDataSource = getOutDataSource(calculateParam.getDataSets().get(0));

        if (Constants.CustomizedAlgoId.POTENTIAL_GUEST_ALGO_ID.equals(calculateParam.getAlgoId())) {
            // 定制化的处理方式,需要走特有的处理方式。此类算法,只能提前预置后
            return new PotentialGuestResultHandle(scriptParam, outputDataSource);
        } else {
            // 任务结果走默认处理方式,此类算法可以通过算法管理界面可以添加
            return new ResultHandle(scriptParam, outputDataSource);
        }
    }

    private static OutputDataSource getOutDataSource(InputDataSource inputDataSource) {
        return new OutputDataSource(inputDataSource.getDatatype(), inputDataSource.getDataSourceConfig());
    }

}

使用

    public TaskEntity(TaskInfo taskInfo, CalculateParam calculateParam, String algoPackDir, String localFileDir) {
        this.scriptParam = getScriptParam(calculateParam, algoPackDir, localFileDir);
        this.taskInfo = taskInfo;
        this.shellParamHandle = TaskAbstractFactory.getScriptExecuteHandle(scriptParam, calculateParam.getAlgoId());
        this.dataSetHandleList = TaskAbstractFactory.getDataSetHandleList(calculateParam.getDataSets());
        this.resultHandle = TaskAbstractFactory.getResultHandle(calculateParam, scriptParam);
    }

其他说明
设计是工厂创建实例的是子类,返回的是父类。多态的体现。同事父类有默认方法,子类是对父类的扩展, 或者重写。如下:
DataSetHandle

/**
 * 数据集处理父类
 * @since 2023 -09-15 15:35
 */
public abstract class DataSetHandle {
    private static final Logger LOGGER = LoggerFactory.getLogger(DataSetHandle.class);

    /**
     * The Data source info.
     */
    protected String dataSourceInfo;

    /**
     * The Data source config.
     */
    protected String dataSourceConfig;

    /**
     * Instantiates a new Data set handle.
     *
     * @param dataSourceInfo the data source info
     * @param dataSourceConfig the data source config
     */
    public DataSetHandle(String dataSourceInfo, String dataSourceConfig) {
        this.dataSourceInfo = dataSourceInfo;
        this.dataSourceConfig = dataSourceConfig;
    }

    /**
     * Check data source.
     */
    public void checkDataSource() {
        // 对参数的json格式进行校验
        if (!MyStringUtil.checkJson(dataSourceInfo)) {
            LOGGER.error("dataSourceInfo json format error.");
            throw new CommonServiceException(AIModelError.PARAM_ERROR, "dataSourceInfo json format error.");
        }

        if (StringUtils.isNotEmpty(dataSourceConfig) && !MyStringUtil.checkJson(dataSourceConfig)) {
            LOGGER.error("dataSourceConfig json format error.");
            throw new CommonServiceException(AIModelError.PARAM_ERROR, "dataSourceConfig json format error.");
        }
    }

    /**
     * Handle data.
     *
     * @param taskId the task id
     * @param localFileDir the local file dir
     */
    public abstract void handleData(String taskId, String localFileDir);

}

S3DataSetHandle

/**
 * S3或者minio类型的数据集处理类
 * @since 2023 -09-15 15:35
 */
public class S3DataSetHandle extends DataSetHandle {
    private static final Logger LOGGER = LoggerFactory.getLogger(S3DataSetHandle.class);

    /**
     * Instantiates a new S 3 data set handle.
     *
     * @param dataSourceInfo the data source info
     * @param dataSourceConfig the data source config
     */
    public S3DataSetHandle(String dataSourceInfo, String dataSourceConfig) {
        super(dataSourceInfo, dataSourceConfig);
    }

    /**
     * Check data source.
     */
    @Override
    public void checkDataSource() {
        // 1 父类进行参数json格式的校验
        super.checkDataSource();
        // 2 具体子类,进行特性校验
        List<S3DataSourceInfo> s3DataSourceList = JSON.parseArray(dataSourceInfo, S3DataSourceInfo.class);
        for (S3DataSourceInfo s3DataSource : s3DataSourceList) {
            // 目前S3,仅支持zip文件
            if (!Constants.FileType.ZIP.equalsIgnoreCase(s3DataSource.getFileType())) {
                LOGGER.error("The file type is not supported. fileType:{}", s3DataSource.getFileType());
                throw new CommonServiceException(AIModelError.PARAM_ERROR,
                    "The file type is not supported. fileType: " + s3DataSource.getFileType());
            }
            if (StringUtils.isEmpty(s3DataSource.getFileId()) || StringUtils.isEmpty(s3DataSource.getStoreDirName())) {
                LOGGER.error("fileId and storeDirName cannot be empty.");
                throw new CommonServiceException(AIModelError.PARAM_ERROR, "fileId and storeDirName cannot be empty.");
            }
        }
    }

    /**
     * Handle data.
     *
     * @param taskId the task id
     * @param localFileDir the local file dir
     */
    @Override
    public void handleData(String taskId, String localFileDir) {
        // 1 获取配置
        S3DataSourceConfig s3DataSourceConfig = JSON.parseObject(dataSourceConfig, S3DataSourceConfig.class);
        // 2 初始化S3客户端
        S3ClientUtils s3ClientUtils = S3ClientUtils.getInstance(s3DataSourceConfig);
        for (S3DataSourceInfo s3DataSourceInfo : JSON.parseArray(dataSourceInfo, S3DataSourceInfo.class)) {
            InputStream s3InputStream = null;
            try {
                // 3 获取数据流
                s3InputStream = s3ClientUtils.download(s3DataSourceConfig.getBucketName(),
                    s3DataSourceInfo.getFileId());
                // 4 将文件保存在本地磁盘
                saveFileToLocal(s3InputStream, s3DataSourceInfo, taskId, localFileDir + taskId);
            } finally {
                MyIOUtils.closeInputStream(s3InputStream);
            }
        }
    }
}

4.2 网络还原时,不同的采集数据,解析处理方式不同

说明:

  • 创建对象由spring类管理,创建出来的对象是单例的,这个案例1有所不同,案例1,每个类初始化的参数不同。这个案例对象初始化方式一样,只是处理逻辑不同。
  • 在vimpim对象是,因为参数不同所有每次都需要new

工厂类:

/**
 * The type Ods process factory.
 *
 * @since 2024 -06-11 10:54
 */
@Slf4j
@Service
public class OdsProcessFactory {

    private static final List<VimPimModelDto> VIM_PIM_MODEL_V2_LIST = new ArrayList<>();
    private static final String VIM_PIM_MODEL_V2_FILE_PATH = "2.json";
    private static final List<String> VIM_PIM_MODEL_V2_WHITE_LIST = Arrays.asList();
    private static final List<VimPimModelDto> VIM_PIM_MODEL_V3_LIST = new ArrayList<>();
    private static final String VIM_PIM_MODEL_V3_FILE_PATH = "3.json";
    private static final List<String> VIM_PIM_MODEL_V3_WHITE_LIST = Arrays.asList();

    /**
     * The Data source.
     */
    @Resource(name = "gauss")
    DataSource dataSource;

    @Autowired
    private VimPimRepository vimPimRepository;

    @Autowired
    private VnfMaeCnDataProcessDomainService vnfMaeCnDataProcessDomainService;

    @Autowired
    private VnflocLcmDataProcessDomainService vnflocLcmDataProcessDomainService;

    static {
        // 初始化模型数据
        initModel(VIM_PIM_MODEL_V2_FILE_PATH, VIM_PIM_MODEL_V2_WHITE_LIST, VIM_PIM_MODEL_V2_LIST);
        initModel(VIM_PIM_MODEL_V3_FILE_PATH, VIM_PIM_MODEL_V3_WHITE_LIST, VIM_PIM_MODEL_V3_LIST);
    }

    private static void initModel(String vimPimModelFilePath, List<String> vimPimModelWhiteList,
        List<VimPimModelDto> vimPimModelList) {
    }

    /**
     * Gets ods process service.
     *
     * @param taskDto the task dto
     * @param dataFormat the data format
     * @return the ods process service
     */
    public DataProcessDomainService getOdsProcessService(CollectionTaskDto taskDto, int dataFormat) {
        if (OdsParseConstants.OdsDataFormat.VNF_MAECN == dataFormat
            || OdsParseConstants.OdsDataFormat.NIC_DSP == dataFormat) {
            return vnfMaeCnDataProcessDomainService;
        } else if (OdsParseConstants.OdsDataFormat.VIM_OV2 == dataFormat
            || OdsParseConstants.OdsDataFormat.PIM_OV2 == dataFormat) {
            return getVimPimDataProcessV2DomainService();
        } else if (OdsParseConstants.OdsDataFormat.VIM_OV3 == dataFormat
            || OdsParseConstants.OdsDataFormat.PIM_OV3 == dataFormat) {
            return getVimPimDataProcessV3DomainService();
        } else if (OdsParseConstants.OdsDataFormat.VNFLOC_LCM == dataFormat) {
            return vnflocLcmDataProcessDomainService;
        } else {
            // 采集来的数据格式不支持
            log.warn("Incorrect data dataFormat. taskId:{}, tasksSn:{}, dataFormat:{} ", taskDto.getTaskId(),
                taskDto.getTaskSn(), dataFormat);
        }
        return null;
    }

    public VimPimDataProcessV2DomainService getVimPimDataProcessV2DomainService() {
        return new VimPimDataProcessV2DomainService(VIM_PIM_MODEL_V2_LIST, dataSource, vimPimRepository);
    }

    public VimPimDataProcessV3DomainService getVimPimDataProcessV3DomainService() {
        return new VimPimDataProcessV3DomainService(VIM_PIM_MODEL_V3_LIST, dataSource, vimPimRepository);
    }
}

在这里插入图片描述

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

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

相关文章

vue 页面导出gif图片 img 导出gif 超简单~

1.首先需要新建一个文件件 新建gif文件夹。这两个文件在文章最后面需要可自提 2.出gif分为两种情况 第一种情况 页面是img标签&#xff0c;直接导出图片作为gif 第二种情况 页面是div标签&#xff0c;需要导出div里面的图片作为gif 2.1页面是img标签&#xff0c;直接导出图…

电脑异常情况总结

文章目录 笔记本无症状息屏黑屏 笔记本无症状息屏黑屏 &#x1f34e; 问题描述&#xff1a; 息屏导致黑屏&#xff1b;依次操作计算机--》右键--》管理--》事件查看器--》Windows日志--》系统&#xff1b;从息屏到异常黑屏之间出现了很多错误&#xff0c;如下&#xff1a;事件…

使用CMake生成动态链接库(.dll和.so)和静态链接库(.lib和.a)的方法

&#xff08;一&#xff09;简介 对于大型软件开发&#xff0c;动态连接库是必不可少的。不仅可以实现模块的封装&#xff0c;而且可以实现软件的热更新&#xff08;即替换windows下的.dll或Linux下的.so文件后直接实现软件更新&#xff0c;无需重新编译&#xff09;。有时也需…

力扣 困难 52.N皇后II

文章目录 题目介绍题解 题目介绍 题解 法一&#xff1a;返回51题N皇后List的长度 法二&#xff1a; class Solution {private int n, ans;private boolean[] onPath, diag1, diag2;public int totalNQueens(int n) {this.n n;onPath new boolean[n];diag1 new boolean[n * …

Standard IO

为了提高可移植性&#xff0c;将通用IO接口经过再封装就形成了标准IO&#xff0c;标准IO不仅适用于Unix环境&#xff0c;也兼容非Unix环境&#xff0c;这也是为什么说我们应该尽可能的使用标准IO&#xff0c;通用IO通过文件描述符fd来与文件交互&#xff0c;为了以示区分&#…

element plus e-table表格中使用多选,当翻页时已选中的数据丢失

摘要&#xff1a; 点击第一页选中两个&#xff0c;再选择第二页&#xff0c;选中&#xff0c;回到第一页&#xff0c;之前选中的要保留&#xff01; element ui table 解决办法&#xff1a; :row-key“getRowKeys” &#xff08;写在el-table中&#xff09; methods中声明 ge…

多一DY4100数字式接地电阻测试仪使用测量方法

接地电阻二线法测量步骤 1、找出一套简易测试线&#xff0c;一根红色两插头线和一根短绿线&#xff1b; 2、找出一根辅助地钉&#xff0c;距离被测接地桩/扁铁约五米远整根打下去&#xff1b; 3、绿线插头接仪器E孔&#xff08;绿色孔&#xff09;&#xff0c;夹子夹需测的接…

ArcGIS002:软件自定义设置

摘要&#xff1a;本文详细介绍安装arcgis10.2后软件自定义设置内容&#xff0c;包括工具条的启用、扩展模块的启用、如何加载项管理器、快捷键设置、样式管理器的使用以及软件常规设置。 一、工具条的启用 依次点击菜单栏【自定义】->【工具条】&#xff0c;根据工作需求勾…

医院信息化与智能化系统(5)

医院信息化与智能化系统(5) 这里只描述对应过程&#xff0c;和可能遇到的问题及解决办法以及对应的参考链接&#xff0c;并不会直接每一步详细配置 如果你想通过文字描述或代码画流程图&#xff0c;可以试试PlantUML&#xff0c;告诉GPT你的文件结构&#xff0c;让他给你对应的…

【漏洞修复/安全补丁】5.4之前版本通过api/products接口进行sql注入的问题

5.4之前版本&#xff0c;会使用api/products接口的selectId参数进行sql注入 修复办法 1、修改crmeb/app/api/controller/v1/store/StoreProductController.php文件中的lst方法里面的接收参数&#xff0c;修改成下图 如果你的版本没有coupon_category_id参数的话&#xff0c;…

No.19 笔记 | WEB安全 - 任意文件操作详解 part 1

1. 任意文件上传漏洞基础 什么是文件上传功能? 在网站和应用中,我们经常会看到允许用户上传文件的功能,比如: 更换头像:让用户上传自己的照片作为头像发布图片:在社交媒体或论坛上传图片提交文档:在办公系统中上传Word、Excel等文档 这些都是常见的文件上传功能。 任意文…

Kettle9.4支持Clickhouse数据源插件开发以及性能测试

前言 最近业务这边有个指标需要用到大数据这边的列式数据库进行处理&#xff0c;由于kettle不支持clickhouse数据源驱动&#xff0c;这里查了一下网上的相关资料&#xff0c;发现了一些别人开发好的驱动包&#xff0c;下载下来后使用效果不尽人意。总结下来有以下几个问题&…

重生之“我打数据结构,真的假的?”--1.顺序表(无习题)

C语言中的顺序表详细总结 1. 概述 顺序表&#xff08;Sequential List&#xff09;是一种线性数据结构&#xff0c;用于存储具有相同数据类型的一组元素。顺序表采用一段连续的存储空间&#xff0c;使用数组来实现&#xff0c;能够高效地支持随机访问操作。在 C 语言中&#…

基于ssm的校园车辆管理系统的设计与实现

文未可获取一份本项目的java源码和数据库参考。 一、选题背景与意义 &#xff08;一&#xff09;选题背景 自改革开放以来&#xff0c;国家开始加大力度对高等校园进行投入&#xff0c;深化了教育体制的改革&#xff0c;全国许多高等校园应运而生&#xff0c;越来越多的孩子走…

C++ —— 《模板进阶详解》,typedef和class的区别以及用法,非类型模板参数,模板的特化,模板的分离编译

目录 1.非类型模板参数 2.模板特化 2.1 概念 2.2 函数模板特化 2.3 类模板特化 2.3.1 全特化 2.3.2 偏特化 3 模板分离编译 3.1 什么是分离编译 3.2 模板的分离编译 4.模板总结 在讲解模板进阶之前&#xff0c;我想先简单单独聊聊class和typename的用法 我们在平时…

Mac使用Git仓库上传文件夹方法

环境&#xff1a;mac 仓库&#xff1a;GitLink Q: 首先我们先了解到&#xff0c;远程下载&#xff08;用ssh&#xff09;仓库会默认下载到电脑的哪里呢&#xff1f; A: 如下图&#xff0c;路径为/Users/bb&#xff0c;&#xff08;bb是我的用户名&#xff09; 那么我们在将a文…

筑牢理性防线,“卡游启智,理性护航”青少年健康消费倡议发布

新华网北京10月22日电&#xff08;记者周靖杰&#xff09;10月21日&#xff0c;国内针对青少年卡牌盲盒消费的倡议在京发布。 “卡游启智&#xff0c;理性护航”青少年健康消费倡议在京正式发布 近年来&#xff0c;卡牌盲盒在青少年中悄然流行&#xff0c;买卡、拆包、集卡成为…

临时配置linux Bridge网桥

Linux Bridge&#xff08;网桥&#xff09;是用纯软件实现的虚拟交换机&#xff0c;有着和物理交换机相同的功能&#xff0c;例如二层交换&#xff0c;MAC地址学习等。因此我们可以把tun/tap&#xff0c;veth pair等设备绑定到网桥上&#xff0c;就像是把设备连接到物理交换机上…

数据可视化示例

用eclipse软件 java 代码&#xff1a; <!DOCTYPE html> <html> <head> <meta charset"UTF-8"> <!-- 引入ECharts脚本&#xfe63;-> <script src"js/echarts.js"></script> <title&#xff1e;网格…

红日靶场(三)1、环境介绍及环境搭建

1、靶场介绍 红日靶场03是一个用于安全测试和渗透测试的虚拟化环境&#xff0c;可以帮助用户通过模拟攻击和防御场景来提升网络安全技能。该靶场包含了多个虚拟机和网络配置&#xff0c;用户可以在其中进行各种安全测试活动&#xff0c;如信息收集、漏洞利用、权限提升等。 2…