设计模式精讲:掌握工厂方法与抽象工厂的精髓

news2025/1/13 10:24:43

设计模式精讲:掌握工厂方法与抽象工厂的精髓

  • 一、引言:如何学习设计模式?
  • 二、工厂方法(也叫工厂模式)
    • 2.1、代码结构
    • 2.2、符合的设计原则
    • 2.3、小结
  • 三、抽象工厂
    • 3.1、代码结构
    • 3.2、符合的设计原则
    • 3.3、小结
  • 总结

一、引言:如何学习设计模式?

学习设计模式最主要要抓住一点:就是怎么分析这个稳定点和变化点。自己实现一个框架,或者是实现一个具体的小功能,本质上分析问题的思路都是一样的,首先要去把稳定点给它抽象出来,然后针对这个变化点想着怎么去扩展它。所以这里还是要反复的介绍怎么分析这个稳定点和变化点;具体不同的设计模式是怎么来处理这个扩展(就是扩展的问题);稳定点它是怎么处理的;用C++的语言特性是怎么去解决这些问题的;沿着这个思路去学习。

前面已经介绍了设计模式当中的模板方法、观察的模式、以及策略模式,这里再次强调以下学习、掌握设计模式的学习步骤。

  • 首先,需要来了解设计模式解决了什么问题。本质上是分析它的稳定点和变化点,实际在做具体功能开发的时候也需要去抽象具体的稳定点以及想办法去扩展变化点,这样在实际开发过程当中,尽量写少量的代码去应对未来需求的变化。
  • 第二点,设计模式的代码结构是什么。需要培养一个看代码、看一些框架或者看项目代码结构的时候马上能够反应出来使用了什么设计模式,或者它符合什么设计原则,从而可以推断出代码具体的意图。熟悉实现具体设计模式的代码结构能够帮助我们对一个代码有一个敏感度,以便能够快速的进行推断和反应。
  • 第三点,看这些设计模式符合了哪些设计原则。因为设计模式是由设计原则推导过来的,所以可以按照这一个设计模式的产生的流程重新去思考这一个问题,能够帮助我们去很好的去设计我们的代码。相信很多人在具体的工作当中都有自己不同的一些设计方式,它不一定符合某一些设计模式,未来大家应对的某些需求也会自己去设计一个框架,所以可以思考它符合哪些设计原则。
  • 第四点,如何在上面扩展代码。尤其是对于初学者或刚刚参加工作的朋友们,对这个扩展代码一定要非常的清楚,就是如果在这个设计模式的基础上要修改哪些代码,这个是必须要掌握的。
  • 第五点,按照自己的需求或者自己的项目以及自己的工作场景进行一个联系,哪些需求变化可以使用设计模式;在看开源框架的时候也可以去看一下它是怎么解决这一个问题的。记住几个关键设计模式的一些典型应用场景能够帮助我们快速的反应;当具体需求来了知道该怎么使用某一些设计模式。
学习步骤
设计模式解决什么问题
稳定点
变化点
设计模式的代码结构是什么
设计模式符合哪些设计原则
如何在上面扩展代码
该设计模式有哪些典型应用场景
联系工作场景
开源框架

这个就是设计模式具体的学习步骤。

二、工厂方法(也叫工厂模式)

首先从定义出发,工厂方法的定义:定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method 使得一个类的实例化延迟到子类

接下来就要分析它解决了什么问题。它的定义不需要特别的去关注,也不要记这个定义,主要分析这个定义当中的稳定点和变化点。

  • 稳定点:创建同类对象的接口,并且同类对象有一个相同的职责。要提供同类对象的一个接口,同类对象只有一个相同的职责(职责就是功能的意思)。
  • 变化点:对象的拓展。同一类对象会越来越多,要进行对象的扩展。

2.1、代码结构

举个例子来帮助理解:

实现一个导出数据的接口,让客户选择数据的导出方式。

这个的职责是导出数据,这里面的导出数据有很多方式可以让用户选择,即同类对象都有导出功能,而且都是导出数据。因此,要创建一个对象接口以满足同类对象有相同的职责(职责就是导出数据)。

不使用设计模式的时候,首先要使用设计原则,注意到的是“导出数据”是一个职责,马上要想到接口,对于这种问题想都不用想就是要考虑用接口去封装它,因为有多个不同导出数据的接口,就要用统一的接口,这个接口就代表它有一个什么样的功能(即导出功能),马上就可以写出这样的一个语句:

// 实现导出数据的接口, 导出数据的格式包含 xml,json,文本格式txt 后面可能扩展excel格式csv
class IExport {
public:
    virtual bool Export(const std::string &data) = 0;
    virtual ~IExport(){}
};

就是导出这个具体的数据有很多格式,比如说有xml,有json,有txt,还有excel格式csv等等。然后尝试实现接口:继承IExport ,实现Export()方法。

#include <string>
// 实现导出数据的接口, 导出数据的格式包含 xml,json,文本格式txt 后面可能扩展excel格式csv
class IExport {
public:
    virtual bool Export(const std::string &data) = 0;
    virtual ~IExport(){}
};

class ExportXml : public IExport {
public:
    virtual bool Export(const std::string &data) {
        return true;
    }
};

class ExportJson : public IExport {
public:
    virtual bool Export(const std::string &data) {
        return true;
    }
};
// csv
class ExportTxt : public IExport {
public:
    virtual bool Export(const std::string &data) {
        return true;
    }
};

// class ExportCSV : public IExport {
// public:
//     virtual bool Export(const std::string &data) {
//         return true;
//     }
// };

// =====1
int main() {
    std::string choose/* = */;
    if (choose == "txt") {
        /***/
        IExport *e = new ExportTxt();
        /***/
        e->Export("hello world");
    } else if (choose == "json") {
        /***/
        IExport *e = new ExportJson();
        /***/
        e->Export("hello world");
    } else if (choose == "xml") {
        IExport *e = new ExportXml();
        e->Export("hello world");
    } else if (choose == "csv") {
        IExport *e = new ExportXml();
        e->Export("hello world");
    }
}

实现导出功能,实现IExport类就可以导出为xml、json、txt等格式,因为在这里只有一个导出职责(导出功能),都是一样的,同样的接口Export把它实现好,未来可能会增加CSV也可以实现一下。接下来让用户选择数据的导出方式,int main() 就要实现这个功能了。在这里按用户的输入判断,然后new出来这个数据,然后再把它进行Export。这个是没有使用设计模式的时候,仅仅考虑面向接口编程写出来的这样的一个代码。

接下来看一下符合设计模式是怎么进行开发的,其实这里创建同类对象的接口可以用一个工厂接口来实现,有相同的职责可以用一个具体的职责接口来实现,也就是说这里应该要抽象出两个接口:一个是对象产生的接口,一个是具体的职责的接口。要理解这句话,先思考一个问题:

为什么要有工厂模式?

再来了解一下为什么在这里面要用两个接口,而不直接new对象来使用?刚刚举的例子,有一个业务可能没注意到,主要的原因是没有表现出来,因为通常如果使用模式的时候,new的对象不是直接用一个对象这么简单,new完之后还要去有复杂的构造流程。

设计模式通常是要解决创建过程比较复杂,而且希望对外隐藏这些细节的场景,也就是说某一个用户只使用需要使用的功能。比如说导出JSON数据除了要去把JSON库导出来(加载JSON库)可能还需要去调用一些配置参数(配置这个JSON的参数),还可能要进行初始化一些数据,把这些流程都封装起来,放到某一个对象的JSON导出的接口中,那么对于用户而言,只需要知道用JSON导出,用户根本不需要关注还要加载一个库、配置一些参数、初始化一些值才能够去使用它的导出功能。

当创建过程比较复杂,而用户只需要关注他的职责,并不关注他的创建过程,比较复杂的这个流程用户不需要知道,这种时候需要使用工厂方法模式。举个例子,比如说连接池、线程池等,使用连接池通常只需要把一个任务push进去就行了,对于用户而言,只需要有一个push的操作就行了,往里面去push后会自动帮用户完成;如果真正把线程池封装的好,那么对于用户而言,应该只需要知道有一个push接口就行了。都知道线程池要去构建它要初始化(这个把线程加载进来,把线程初始化好),把一些数据初始化,然后把一些入口函数初始化等等,这里面有复杂的构造流程,这些构造流程对于用户而言没什么用,用户根本不需要关注这些东西,用户只需要push任务,对于一个正确的封装者(做功能开发、做功能抽象的人)应该是让用户知道的越少越好。

要点:解决创建过程比较复杂,希望对外隐藏这些细节的场景。

  • 比如连接池、线程池
  • 隐藏对象真实类型;
  • 对象创建会有很多参数来决定如何创建;
  • 创建对象有复杂的依赖关系。

工厂方法实现的代码:

#include <string>
// 实现导出数据的接口, 导出数据的格式包含 xml,json,文本格式txt 后面可能扩展excel格式csv
class IExport {
public:
    virtual bool Export(const std::string &data) = 0;
    virtual ~IExport(){}
};

class ExportXml : public IExport {
public:
    virtual bool Export(const std::string &data) {
        return true;
    }
};

class ExportJson : public IExport {
public:
    virtual bool Export(const std::string &data) {
        return true;
    }
};

class ExportTxt : public IExport {
public:
    virtual bool Export(const std::string &data) {
        return true;
    }
};

class ExportCSV : public IExport {
public:
    virtual bool Export(const std::string &data) {
        return true;
    }
};

class IExportFactory {
public:
    IExportFactory() {
        _export = nullptr;
    }
    virtual ~IExportFactory() {
        if (_export) {
            delete _export;
            _export = nullptr;
        }
    }
    bool Export(const std::string &data) {
        if (_export == nullptr) {
            _export = NewExport();
        }
        return _export->Export(data);
    }
protected:
    virtual IExport * NewExport(/* ... */) = 0;
private:
    IExport* _export;
};

class ExportXmlFactory : public IExportFactory {
protected:
    virtual IExport * NewExport(/* ... */) {
        // 可能有其它操作,或者许多参数
        IExport * temp = new ExportXml();
        // 可能之后有什么操作
        return temp;
    }
};
class ExportJsonFactory : public IExportFactory {
protected:
    virtual IExport * NewExport(/* ... */) {
        // 可能有其它操作,或者许多参数
        IExport * temp = new ExportJson;
        // 可能之后有什么操作
        return temp;
    }
};
class ExportTxtFactory : public IExportFactory {
protected:
    IExport * NewExport(/* ... */) {
        // 可能有其它操作,或者许多参数
        IExport * temp = new ExportTxt;
        // 可能之后有什么操作
        return temp;
    }
};

class ExportCSVFactory : public IExportFactory {
protected:
    virtual IExport * NewExport(/* ... */) {
        // 可能有其它操作,或者许多参数
        IExport * temp = new ExportCSV;
        // 可能之后有什么操作
        return temp;
    }
};

int main () {
    IExportFactory *factory = new ExportCSVFactory();
    factory->Export("hello world");
    return 0;
}

为了实现封装出复杂的对象的构建过程,给了一个对象创建的一个接口IExportFactory ,那么这里的IExport 是功能接口(具体到底实现了一个什么功能)。具体的构造流程在NewExport()中实现,用户不会关注这个的实现,用户只关注Export功能(导出功能)。知道这些接口之后,还需要不同对象的构造,IExportFactory 是基类,使用ExportTxtFactory ExportCSVFactory ExportXmlFactory 等实现不同的导出对象,复杂的操作流程就在NewExport中实现。最后就是在main ()中使用它。

代码结构:

  • 对象创建接口。
  • 功能接口。
  • 对象扩展。
  • 多态调用的使用方式。

2.2、符合的设计原则

(1)最小知道原则。
(2)面向接口原则。

用户知道的越少越好,让用户只需要知道最需要的东西就行;想让用户知道的越少就要面向接口编程,把用户只关注的行为抽象成接口。

需要哪些接口?

  1. 具体的职责类的功能接口。简单来说就是提供了一个什么功能给用户,示例中用户只需要有一个导出的功能,只要给用户这样一个导出接口,用户只关注一个导出接口就行了,即功能接口是做什么事情的用户要知道。
  2. 对象创建接口。虽然用户不关注,但是要知道怎么去创建;就是有一个具体的对象,到底用户应该用什么东西来实现这个功能,比如说是用JSON来实现这个导出功能、用txt来实现这个导出功能、用CSV来现这个导出功能,就需要一个对象创建的接口;这个对象创建的接口就是所说的工厂接口。

2.3、小结

稳定点都是用抽象去解决,抽象那些万成不变的东西,具体可以用接口来实现。通过分析稳定点,发现要有一个创建同类对象的一个职责接口以及一个功能接口,这两个东西是稳定点,需要注意这个功能接口对于用户而言是不可见的,用户不需要关注这一个功能接口,这个功能接口它具体起到一个什么作用可以看到示例中IExportFactory的功能对象创建的流程。IExportFactory只提供了Export,并没有提供创建的流程,创建流程在NewExport()实现。

扩展代码:

  • 实现对象创建接口。
  • 实现功能接口。
  • 多态调用。

本质:延迟到子类来选择实现。

结构图:

Factory
+CreateProduct()
Product
+Operator()
ConcreteProduct1
+Operator()
ConcreteProduct2
+Operator()
ConcreteProduct3
+Operator()

思维导图:
在这里插入图片描述

三、抽象工厂

理解了工厂模式,抽象工厂就非常的简单,它其实就是在工厂模式当中增加了一个多职责,它除了创建同类对象的接口,它还有就是有多个相同的职责。

抽象工厂的定义:提供一个接口,让该接口负责创建一系列“相关或者相互依赖的对象”,无需指定它们具体的类。

解决了什么问题:

  • 稳定点:创建同类对象的接口,并且同类对象有多个相同的职责。和工厂方法模式唯一的区别是"有多个相同的职责"。
  • 变化点:对象的拓展。和工厂方法模式一样

3.1、代码结构

看一个例子:

实现一个拥有导出导入数据的接口,让客户选择数据的导出导入方式。

跟前面的工厂方法中的例子差不多,只是多了一个导入的功能,并且让用户去选择导入的方式。也就是说某一个对象(比如说CSV),它刚刚只需要有一个导出功能,现在还要有一个导入的功能;同样的,要把这个导入功能职责也要抽象成一个接口。

这个抽象工厂就非常的简单,它的代码结构基本和工厂方法一样的:

  • 对象创建接口。包括创建具体对象,提供多个功能接口来调用。在创建对象接口当中去调用功能接口。
  • 多个功能接口。

抽象工厂在工厂方法上面就多了一个,它是有多个职责的,它有多个功能的。

抽象工厂的代码实现:

#include <string>
// 实现导出数据的接口, 导出数据的格式包含 xml,json,文本格式txt 后面可能扩展excel格式csv
class IExport {
public:
    virtual bool Export(const std::string &data) = 0;
    virtual ~IExport(){}
};

class ExportXml : public IExport {
public:
    virtual bool Export(const std::string &data) {
        return true;
    }
};

class ExportJson : public IExport {
public:
    virtual bool Export(const std::string &data) {
        return true;
    }
};

class ExportTxt : public IExport {
public:
    virtual bool Export(const std::string &data) {
        return true;
    }
};

class ExportCSV : public IExport {
public:
    virtual bool Export(const std::string &data) {
        return true;
    }
};

class IImport {
public:
    virtual bool Import(const std::string &data) = 0;
    virtual ~IImport(){}
};

class ImportXml : public IImport {
public:
    virtual bool Import(const std::string &data) {
        return true;
    }
};

class ImportJson : public IImport {
public:
    virtual bool Import(const std::string &data) {
        return true;
    }
};

class ImportTxt : public IImport {
public:
    virtual bool Import(const std::string &data) {
        return true;
    }
};

class ImportCSV : public IImport {
public:
    virtual bool Import(const std::string &data) {
        // ....
        return true;
    }
};

class IDataApiFactory {
public:
    IDataApiFactory() {
        _export = nullptr;
        _import = nullptr;
    }
    virtual ~IDataApiFactory() {
        if (_export) {
            delete _export;
            _export = nullptr;
        }
        if (_import) {
            delete _import;
            _import = nullptr;
        }
    }
    bool Export(const std::string &data) {
        if (_export == nullptr) {
            _export = NewExport();
        }
        return _export->Export(data);
    }
    bool Import(const std::string &data) {
        if (_import == nullptr) {
            _import = NewImport();
        }
        return _import->Import(data);
    }
protected:
    virtual IExport * NewExport(/* ... */) = 0;
    virtual IImport * NewImport(/* ... */) = 0;
private:
    IExport *_export;
    IImport *_import;
};

class XmlApiFactory : public IDataApiFactory {
protected:
    virtual IExport * NewExport(/* ... */) {
        // 可能有其它操作,或者许多参数
        IExport * temp = new ExportXml;
        // 可能之后有什么操作
        return temp;
    }
    virtual IImport * NewImport(/* ... */) {
        // 可能有其它操作,或者许多参数
        IImport * temp = new ImportXml;
        // 可能之后有什么操作
        return temp;
    }
};

class JsonApiFactory : public IDataApiFactory {
protected:
    virtual IExport * NewExport(/* ... */) {
        // 可能有其它操作,或者许多参数
        IExport * temp = new ExportJson;
        // 可能之后有什么操作
        return temp;
    }
    virtual IImport * NewImport(/* ... */) {
        // 可能有其它操作,或者许多参数
        IImport * temp = new ImportJson;
        // 可能之后有什么操作
        return temp;
    }
};
class TxtApiFactory : public IDataApiFactory {
protected:
    virtual IExport * NewExport(/* ... */) {
        // 可能有其它操作,或者许多参数
        IExport * temp = new ExportTxt;
        // 可能之后有什么操作
        return temp;
    }
    virtual IImport * NewImport(/* ... */) {
        // 可能有其它操作,或者许多参数
        IImport * temp = new ImportTxt;
        // 可能之后有什么操作
        return temp;
    }
};

class CSVApiFactory : public IDataApiFactory {
protected:
    virtual IExport * NewExport(/* ... */) {
        // 可能有其它操作,或者许多参数
        IExport * temp = new ExportCSV;
        // 可能之后有什么操作
        return temp;
    }
    virtual IImport * NewImport(/* ... */) {
        // 可能有其它操作,或者许多参数
        IImport * temp = new ImportCSV;
        // 可能之后有什么操作
        return temp;
    }
};

// 相关性  依赖性    工作当中
int main () {
    IDataApiFactory *factory = new CSVApiFactory();
    factory->Import("hello world");
    factory->Export("hello world");
    return 0;
}

除了导出接口,还加入了导入接口,分别实现它们的职责。

3.2、符合的设计原则

基本和工厂方法的一样。
(1)最小知道原则。
(2)面向接口原则。

3.3、小结

抽象工厂模式和工厂方法的区别:工厂方法通常一个对象只有一个职责,而抽象工厂模式是一个对象有多个职责

结构图:
在这里插入图片描述
思维导图:
在这里插入图片描述

总结

在本文中,我们深入探讨了设计模式中两个重要的概念:工厂方法和抽象工厂。首先介绍了设计模式的重要性和应用场景,然后重点讲解了工厂方法模式和抽象工厂模式的原理和实现方式。通过详细的代码结构和实例分析,我们揭示了它们如何提供灵活性、可扩展性和可维护性,并如何符合设计原则。最后,我们强调了工厂方法和抽象工厂的精髓,帮助读者深入理解并将其应用于实际项目中,从而提高编程技能和代码质量。
在这里插入图片描述

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

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

相关文章

麒麟操作系统根目录权限777修复方法

在麒麟操作系统中&#xff0c;误操作执行&#xff1a;chmod -R 777 /后&#xff0c;整个根目录权限都变成了777&#xff0c;先找一台相同版本的系统&#xff0c;越干净越好&#xff0c;把该系统的权限导出。 1.在好的机器上执行&#xff1a; #cd / 到根目录 # sudo…

Docker容器中的OpenCV:轻松构建可移植的计算机视觉环境

前言 「作者主页」&#xff1a;雪碧有白泡泡 「个人网站」&#xff1a;雪碧的个人网站 构建可移植的计算机视觉环境 文章目录 前言引言简介&#xff1a;目的和重要性&#xff1a; 深入理解Docker和OpenCVDocker的基本概念和优势&#xff1a;OpenCV简介和应用领域&#xff1a;…

四、虚拟机网络配置

目录 1、VMware网卡配置模式 1.1 桥接模式 1.2 NAT模式 1.3 仅主机模式 ​​​​​​​2、编辑虚拟机的网络编辑器 ​​​​​​​3、编辑Window的虚拟网卡 ​​​​​​​4、修改IP地址为静态 4.1 查看网卡名字 4.2 编辑修改网卡IP地址的配置文件 4.3 重启网络: 4.…

【Docker】Swarm的overlay网络

对于理解swarm的网络来讲&#xff0c;个人认为最重要的两个点&#xff1a; 第一是外部如何访问部署运行在swarm集群内的服务&#xff0c;可以称之为入方向流量&#xff0c;在swarm里我们通过ingress来解决。 第二是部署在swarm集群里的服务&#xff0c;如何对外进行访问&…

探索Playwright的现代自动化测试力量

在当今数字化时代&#xff0c;Web应用程序的质量和稳定性对于企业的成功至关重要。为了确保Web应用程序的无缝运行&#xff0c;自动化测试工具成为了开发人员和测试团队的重要工具。多年来&#xff0c;Selenium一直是自动化测试的黄金标准&#xff0c;然而&#xff0c;在不久前…

从0开始学习JavaScript--JavaScript CommonJS 和 Node.js 模块系统

JavaScript 的模块系统是实现模块化开发的关键&#xff0c;而 CommonJS 规范及其在 Node.js 中的应用是其中最为突出的例子。本文将深入研究 JavaScript 中的 CommonJS 模块规范&#xff0c;解析其核心概念&#xff0c;并通过丰富的示例代码展示其在 Node.js 中的实际应用。 C…

MySQL核心知识点整理大全1-笔记

MySQL 是一种流行的关系型数据库管理系统&#xff0c;它是以C和C语言编写的&#xff0c;最初是由瑞典公司MySQL AB开发的&#xff0c;现在是由Oracle公司维护和支持。MySQL是开源软件&#xff0c;可在Windows、Linux、Mac OS、FreeBSD等各种操作系统上运行。MySQL的主要特点是速…

数字图像处理(实践篇)十四 图像金字塔

目录 一 图像金字塔 二 涉及的函数 三 实践 一 图像金字塔 在某些情况下&#xff0c;需要处理不同分辨率的&#xff08;相同&#xff09;图像。比如&#xff0c;在图像中搜索某些目标&#xff08;比如人脸&#xff09;的时候&#xff0c;不确定该目标在所述图像中会以多大的…

智能优化算法应用:基于象群算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于象群算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于象群算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.象群算法4.实验参数设定5.算法结果6.参考文献7.MATLAB…

HTML5+CSS3小实例:纯CSS实现文字组成肖像特效

实例:纯CSS实现文字组成肖像特效 技术栈:HTML+CSS 效果: 源码: 【HTML】 <!DOCTYPE html> <html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"><meta name="viewport" conten…

Java SpringBoot Controller常见写法

文章目录 环境Controller调用脚本运行结果总结 环境 系统: windows 11 工具: java, idea, git bash Controller 接口常见有以下几种方式 其中&#xff1a; Tobj 调用脚本 我的是windows 系统&#xff0c;使用 git bash 窗口运行, 用 cmd 或者 power shell 会有问题 curl …

《合成孔径雷达成像算法与实现》_使用CS算法对RADARSAT-1数据进行成像

CSA 简介&#xff1a;Chirp Scaling 算法 (简称 CS 算法&#xff0c;即 CSA) 避免了 RCMC 中的插值操作。该算法基于 Scaling 原理&#xff0c;通过对 chirp 信号进行频率调制&#xff0c;实现了对信号的尺度变换或平移。基于这种原理&#xff0c;可以通过相位相乘代替时域插值…

ssh连接docker容器处理备忘

1、查看容器ip&#xff0c;记下来之后要用 docker inspect elastic | grep IPAddress 2、使用root进入docker容器 docker exec -it -u root elastic /bin/bash 3、安装openssh #更新apt apt-get update#安装ssh client apt-get install openssh-client#安装ssh server apt-…

子类拷贝构造函数会调用父类拷贝构造函数吗?

一. 编译器提供的默认子类拷贝构造函数会调用父类拷贝构造函数。 #include <iostream> #include <string> using namespace std;class Parent { public:Parent(string home_address "中国") : m_home_address(home_address) {cout << "调用…

【网络安全】用永恒之蓝(Eternal blue)测试windows系统的安全性

一、kali默认账户和密码都为kali 攻击机&#xff1a;Linux 的 kali 目标机&#xff1a;Windows7 x64 二、kali、metasploit、metasploit 攻击 windows操作系统、metasploit 攻击 永恒之蓝 全流程 ①kali&#xff1a;是黑客攻击机。开源免费的Linux操作系统&#xff0c;含有300…

11.30_黑马Redis实战篇分布式锁

实战篇9 设立一个在jvm外的锁监视器&#xff0c;可以处理多线程的问题 实战篇10 获取锁的时候&#xff0c;要同时发生获取锁以及设置到期时间。 实战篇11 thinking&#xff1a;JAVA中的自动拆箱与装箱&#xff1f; 【Java基础】自动拆装箱_Elephant_King的博客-CSDN博客 TR…

【微服务 SpringCloudAlibaba】实用篇 · Feign服务远程调用

微服务&#xff08;7&#xff09; 文章目录 微服务&#xff08;7&#xff09;1. Feign替代RestTemplate1&#xff09;引入依赖2&#xff09;添加注解3&#xff09;编写Feign的客户端4&#xff09;测试5&#xff09;总结 2.自定义配置2.1 配置文件方式2.2 Java代码方式 3. Feign…

Unity随笔1 - 安卓打包JDK not found

今天遇到一个很奇怪的事情&#xff0c;之前可以正常打安卓包&#xff0c;但是突然报错如下&#xff1a; 提示很明显&#xff0c;找不到JDK了。可是我在下载Unity的时候明明安装了所有需要的组件&#xff0c;为什么今天突然不行。 看了眼Unity hub里面&#xff0c;没问题。 那就…

go elasticsearch 测试实例

// 查询列表数据 func QueryOperateList(ctx context.Context, esClient *elastic.Client, index string, pageNum, pageSize int, start, end int64, execSql string, list []interface{}, operateAccount string, operateAddr string, maxRows, minRows int, dbAddr, namespa…

【UGUI】实现背包的常用操作

1. 添加物品 首先&#xff0c;你需要一个包含物品信息的类&#xff0c;比如 InventoryItem&#xff1a; using UnityEngine;[CreateAssetMenu(fileName "NewInventoryItem", menuName "Inventory/Item")] public class InventoryItem : ScriptableObje…