C++设计模式之:工厂模式factory

摘要:

   it人员无论是使用哪种高级语言开发东东,想要更高效有层次的开发程序的话都躲不开三件套:数据结构,算法和设计模式。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案,使用设计模式是为了重用代码、让代码更容易被他人理解、保证代码可靠性。

   设计模式坚持七大原则:开闭原则,单一职责原则,里氏替换原则,依赖倒转原则,接口隔离原则,迪米特原则,合成复用原则,而各项设计模式又区分为三大模式,创建型模式,结构型模式,行为模式。

   此系列专注讲解C++开发过程中高需求好用的设计模式类型,能更好的简练项目架构和代码量,通过使用场景以及代码实现来介绍对应的模式。本文介绍的是工厂模式factory。

(开发环境:VSCode,GCC13.2.0,cmake3.8)

关键词C++设计模式工厂模式Design patternfactory

声明:本文作者原创,转载请附上文章出处与本文链接。

文章目录

      • 摘要:
      • 正文:
        • 介绍
        • 使用场景
        • 注意事项
        • 代码实现
          • 简易工厂模式
          • 抽象工厂模式(工厂方法模式的拓展)
          • 用类模板或者宏定义实现通用工厂模式
      • 推荐阅读

正文:

介绍

   工厂模式(Factory Pattern)是一种常用的创建型设计模式,它提供了一种创建对象的最佳方式。工厂模式的核心在于将对象的实例化过程封装在一个或多个专门的工厂类中,从而降低了客户端代码与具体产品类之间的耦合度,使得系统更加灵活和易于扩展。工厂模式主要包括三种类型:简单工厂模式(Simple Factory Pattern)、工厂方法模式(Factory Method Pattern)和抽象工厂模式(Abstract Factory Pattern),以及最后用类模板搭建的通用抽象工厂。

   其模式实质是对C++虚函数,继承,类模板等特性的抽象应用,所以需要对这几个特性有一定理解才能更好的搭建工厂。

  1. 简单工厂模式:
    • 又称静态工厂模式,通过一个专门的工厂类来创建对象,无需将对象的实例化过程放在客户端代码中。
    • 工厂类根据客户端的请求,返回相应的产品实例。
    • 优点:简单易用,减少了客户端代码与具体产品类的直接耦合。
    • 缺点:当产品种类增多时,工厂类的代码会变得复杂,违反了开闭原则(对扩展开放,对修改关闭)。
  2. 工厂方法模式:
    • 定义一个用于创建对象的接口,但让子类决定实例化哪个类。
    • 工厂方法让类的实例化推迟到子类中进行。
    • 优点:提高了系统的可扩展性和灵活性,符合开闭原则。
    • 缺点:当产品种类非常多时,会增加系统的复杂性。
  3. 抽象工厂模式(工厂方法模式的拓展):
    • 提供了一个创建一系列相关或相互依赖对象的接口,而无需指定具体类。
    • 客户端可以通过抽象工厂来创建多个系列的产品族。
    • 优点:能够创建一系列相互关联或相互依赖的对象,并且增加了新的产品族时无需修改现有代码。
    • 缺点:系统结构的复杂性较高,增加了系统的抽象性和理解难度。
使用场景

   作为一种创建类模式,在需要生成复杂对象的地方,都可以使用工厂模式,例如可以创建页面工厂,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用设计模式。

  • 简单工厂模式:适用于产品种类较少且不经常变化的场景。
  • 工厂方法模式:适用于产品种类较多,且每种产品都有对应的创建逻辑的场景。
  • 抽象工厂模式:适用于需要创建一系列相互关联或相互依赖的对象的场景,如产品族。

   简单工厂模式和抽象工厂模式并没有上下级之分,具体运用就看项目需求,通常简单工厂模式用的会更多点。

注意事项

   系统复杂性增加,特别是抽象工厂模式,当产品族较多时,系统结构会变得复杂,并增加代码量,需要为每种产品或产品族创建相应的类和接口。

代码实现
简易工厂模式

   简单工厂模式就是用于获取建立在堆上的对象,根据具体的类编写具体的工厂生产方式,这里举个例子:你要去买一台手机,你不用关心手机是怎么生产出来的,里面的零件具体又是怎么制造的,这些通通都交给工厂去处理,你尽管去买手机就好了。

#pragma once
#include <iostream>
using namespace std;

// 手机基类
class Phone {
public:
    virtual void boot_up() = 0;
    virtual ~Phone() {}

public:
    int id;
};

class XiaoMi_Phone :public Phone {
public:
    XiaoMi_Phone() {}
    XiaoMi_Phone(int _id) {}                // 无需但必要的
    XiaoMi_Phone(int _id, string _str):str(_str)
    {
        id = _id;
    }

    virtual void boot_up() {
            cout << "XIAOMI:" << id << endl;
        }
protected:
    string str;
};

class OnePuls_Phone :public Phone {
public:
    OnePuls_Phone() {}
    OnePuls_Phone(int _id)
    {
        id = _id;
    }
    OnePuls_Phone(int _id, string _str) {}  // 无需但必要的

    virtual void boot_up() {
        cout << "ONEPULS:" << id << endl;
    }
};

enum
{
    XiaoMi,
    OnePuls
};

// 简易工厂类
class Factory {
public:
    template<typename ... Args>
    Phone *pruduce(int val, Args ... args);
};

template<typename ... Args>
inline Phone *Factory::pruduce(int val, Args ... args)
{
    if (val == XiaoMi) {
        return new XiaoMi_Phone(std::forward<Args>(args)...);   
    }
    else if (val == OnePuls) {
        return new OnePuls_Phone(std::forward<Args>(args)...);
    }
    else
        return nullptr;
}
int main()
{
    Factory factory;
    XiaoMi_Phone *p1 = (XiaoMi_Phone *)factory.pruduce(XiaoMi, 1, "卢总");
    OnePuls_Phone *p2 = (OnePuls_Phone *)factory.pruduce(OnePuls, 2);
    OnePuls_Phone *p3 = (OnePuls_Phone *)factory.pruduce(OnePuls, 3);
    p1->boot_up();
    p2->boot_up();
    p3->boot_up();
    return 0;
}

   简单工厂模式可以完成对对象的生成,但是可扩展性和维护性并不强,每多出一个品牌例如华为手机便需要修改已有的工厂pruduce生产接口,也没有产品族设计,小米也不只有手机,一加也还有做平板,如果后续需要拓展维护就会变得困难,工厂方法模式就是进一步优化这些问题。

用了个可变参数模板template<typename ... Args>,应付于类不同的构造函数参数,不过此方式有一定的不足,不足在于Phone的派生类需要有相同的构造函数参数集,为了Factory::pruduce能正常编译,这个涉及到if或者说条件分支的条件检查是一个运行时行为。即使编译期就知道条件一定是个false,then也必须编,或许可以用constexpr解决,博文后面也有更完善的做法,此代码主要用来理解概念。

抽象工厂模式(工厂方法模式的拓展)

   抽象工厂模式是工厂方法模式的拓展,工厂方法模式上做出的改动不大,实现实质以及拥有的功能基本一致的,抽象工厂模式比工厂方法模式多解决了产品族的问题,也即是搭建“品牌”工厂。现在在原有的工厂基础上需要添加华为品牌以及能够生产华为手环和小米手环,并且为了后续可能存在的拓展维护,我们就需要设计抽象工厂模式。

#pragma once
#include <iostream>
#include <memory>

using namespace std;

// 手机基类
class Phone {
public:
    virtual void boot_up() = 0;
    virtual ~Phone() {}

    int id;
};
class XiaoMi_Phone :public Phone {
public:
    XiaoMi_Phone() {}
    XiaoMi_Phone(int _id, string _str):str(_str)
    {
        id = _id;
    }

    virtual void boot_up() {
            cout << "XIAOMI_Phone:" << id << endl;
        }
protected:
    string str;
};
class OnePuls_Phone :public Phone {
public:
    OnePuls_Phone() {}
    OnePuls_Phone(int _id)
    {
        id = _id;
    }

    virtual void boot_up() {
        cout << "ONEPULS_Phone:" << id << endl;
    }
};
class HuaWei_Phone :public Phone {
public:
    HuaWei_Phone() {}
    HuaWei_Phone(int _id)
    {
        id = _id;
    }
    virtual void boot_up() {
        cout << "HUAWEI_Phone:" << id << endl;
    }
};

//  手表基类
class Watch {
public:
    virtual void start() = 0;
    virtual ~Watch() {}

    string brand;
};
class XiaoMi_Watch :public Watch {
public:
    XiaoMi_Watch() {}
    XiaoMi_Watch(string _str)
    {
        brand = _str;
    }

    virtual void start() {
        cout << "XIAOMI_WATCH:" << brand << endl;
    }
};
class HuaWei_Watch :public Watch {
public:
    HuaWei_Watch() {}
    HuaWei_Watch(string _str, int _price):price(_price)
    {
        brand = _str;
    }
    virtual void start() {
        cout << "HUAWEI_WATCH:" << brand << price << endl;
    }
protected:
    int price;
};

// 抽象工厂基类
class Factory {
public:
    virtual Phone *pruduce_phone() = 0;
    virtual Watch *pruduce_watch() = 0;     // 可拓展产品族,无需改动Phone相关代码      
    virtual ~Factory() {}
};
// 品牌工厂
class XiaoMi_Factory : public Factory {
public:
    virtual XiaoMi_Phone *pruduce_phone() { return new XiaoMi_Phone; }
    virtual XiaoMi_Watch *pruduce_watch() { return new XiaoMi_Watch; }
};
class OnePuls_Factory : public Factory {
public:
    virtual OnePuls_Phone *pruduce_phone() { return new OnePuls_Phone; }
    virtual Watch *pruduce_watch() { return nullptr; }
};
class HuaWei_Factory : public Factory {      // 添加品牌而不需要改Factory
public:
    virtual HuaWei_Phone *pruduce_phone() { return new HuaWei_Phone; }
    virtual HuaWei_Watch *pruduce_watch() { return new HuaWei_Watch; }
};
int main()
{
    shared_ptr<Factory> f1(new XiaoMi_Factory());
	shared_ptr<Factory> f2(new OnePuls_Factory());
    shared_ptr<Factory> f3(new HuaWei_Factory());

    XiaoMi_Phone *p1 = (XiaoMi_Phone *)f1->pruduce_phone();
    OnePuls_Phone *p2 = (OnePuls_Phone *)f2->pruduce_phone();
    HuaWei_Phone *p3 = (HuaWei_Phone *)f3->pruduce_phone();
    p1->boot_up();
    p2->boot_up();
    p3->boot_up();

    XiaoMi_Watch *w1 = (XiaoMi_Watch *)f1->pruduce_watch();
    HuaWei_Watch *w3 = (HuaWei_Watch *)f3->pruduce_watch();
    w1->start();
    w3->start();
    return 0;
}

   这样就存在了品牌工厂,并且各个工厂只能生产对应的品牌,方便后续拓展和维护,不过代码还存在一定问题,如果各个产品有不同的数据,则不方便在构造时进行初始化数据,主要原因是虚函数继承问题,子类想要重写父类的virtual函数,那么子类的这个函数的返回类型,名字,参数列表全部都得和父类一样,有两种解决方式,一种设置InitData成员函数,实例成功后调用设置初始值;另一种为统一同一类型产品的构造函数,Factory虚函数参数好进行统一,或者接着往下看通用工厂模式~。

用类模板或者宏定义实现通用工厂模式

   虽然抽象工厂模式维护相对比较方便,不过每次有新的类出现的时候,我们还是需要去增加接口以便能够产生新的产品,所以为了能更方便的工作(摸鱼),我们可以用类模板或者宏定义实现通用的工厂模式,以下两种方式都是一个工厂生产所有。

用类模板和可变参数模板实现:

#pragma once
#include <iostream>

using namespace std;

// 手机基类
class Phone {
public:
    virtual void boot_up() = 0;
    virtual ~Phone() {}

public:
    int id;
};
class XiaoMi_Phone :public Phone {
public:
    XiaoMi_Phone(int _id, string _str):str(_str)
    {
        id = _id;
    }

    virtual void boot_up() {
            cout << "XIAOMI:" << id << str << endl;
        }
protected:
    string str;
};
class OnePuls_Phone :public Phone {
public:
    OnePuls_Phone(int _id)
    {
        id = _id;
    }

    virtual void boot_up() {
        cout << "ONEPULS:" << id << endl;
    }
};

//  手表基类
class Watch {
public:
    virtual void start() = 0;
    virtual ~Watch() {}

    string brand;
};
class XiaoMi_Watch :public Watch {
public:
    XiaoMi_Watch(string _str)
    {
        brand = _str;
    }

    virtual void start() {
        cout << "XIAOMI_WATCH:" << brand << endl;
    }
};
class HuaWei_Watch :public Watch {
public:
    HuaWei_Watch(string _str, int _price):price(_price)
    {
        brand = _str;
    }
    virtual void start() {
        cout << "HUAWEI_WATCH:" << brand << price << endl;
    }
protected:
    int price;
};

class Factory {
public:
    template<typename T, typename ... Args>
    T *pruduce(Args ... args);
};

template<typename T, typename ... Args>
inline T *Factory::pruduce(Args ... args)
{
    return new T(std::forward<Args>(args)...);
}
int main()
{
    Factory factory;
    XiaoMi_Phone *p1 = factory.pruduce<XiaoMi_Phone>(1, "王腾大帝");
    OnePuls_Phone *p2 = factory.pruduce<OnePuls_Phone>(2);
    XiaoMi_Watch *w1 = factory.pruduce<XiaoMi_Watch>("areyouok");
    HuaWei_Watch *w2 = factory.pruduce<HuaWei_Watch>("遥遥领先", 666);

    p1->boot_up();
    p2->boot_up();
    w1->start();
    w2->start();
    return 0;
}

   另一种有意思的宏定义写法,用到__VA_ARGS__lamble,对象切割,类型推导等,并且多加了个STL容器保存产出的产品实例,不过由于需要保存产品的话,则多加一种产品需要多加一个STL容器,并重载registerGoods,unregisterGoods函数。

#pragma once
#include <iostream>
#include <memory>
#include <functional>
#include <unordered_map>
#include <string>

using namespace std;

// 手机基类
class Phone {
public:
    virtual void boot_up() = 0;
    virtual ~Phone() {}

public:
    int id;
};

class XiaoMi_Phone :public Phone {
public:
    XiaoMi_Phone(int _id, string _str):str(_str)
    {
        id = _id;
    }

    virtual void boot_up() {
            cout << "XIAOMI" << endl;
        }
protected:
    string str;
};

class ONEPULS_Phone :public Phone {
public:
    ONEPULS_Phone(int _id)
    {
        id = _id;
    }

    virtual void boot_up() {
        cout << "ONEPULS" << endl;
    }
};

// 注册产品(根据控件类名)
#define REGISTER(factory, T, ctrlClass, ...)    \
    factory.registerGoods(#ctrlClass,           \
        []() -> T {                             \
            T obj(new ctrlClass(__VA_ARGS__));  \
            return obj;                         \
        });
// 注销产品(根据类名)
#define UNREGISTER(factory, T, ctrlClass) factory.registerGoods(#ctrlClass, T);

class Factory 
{
public:
    // 防止拷贝
	Factory& operator=(const Factory&) = default;
    virtual ~Factory() { PgoodsHash.clear(); }

    using Pptr = std::function<shared_ptr<Phone> ()>;           // shared_ptr<Phone>对象切割,std::function类型推导
    // using Wptr = std::function<shared_ptr<Watch> ()>;

    void registerGoods(const string key, const Pptr &value);    // 注册Phone产品
    void unregisterGoods(const string key, const Pptr &value);  // 注销Phone产品
    unordered_multimap<string, Pptr> *GetHash(){ return &PgoodsHash; }

private:
    // 工厂保存出厂产品的实例
    unordered_multimap<string, Pptr> PgoodsHash;
};

inline void Factory::registerGoods(const string key, const Pptr &value)
{
    PgoodsHash.insert(make_pair(key, value));
}

inline void Factory::unregisterGoods(const string key, const Pptr &value)
{
    PgoodsHash.erase(key);
}
int main()
{
	Factory factory;
    REGISTER(factory, shared_ptr<Phone>, XiaoMi_Phone, 1, "雷神")
    REGISTER(factory, shared_ptr<Phone>, ONEPULS_Phone, 1)
    REGISTER(factory, shared_ptr<Phone>, ONEPULS_Phone, 2)

    auto value = factory.GetHash()->equal_range("XiaoMi_Phone");
    value.first->second()->boot_up();

    cout << "遍历:" << endl;
    for (const auto pair : *factory.GetHash()) {
        cout << pair.first << pair.second()->id << endl;
        pair.second()->boot_up() ;
    }

    return 0;
}

over~

推荐阅读

博客主页:https://blog.csdn.net/weixin_45068267
(客官逛一逛,有许多其它有趣的专栏博文)

C/C++专栏:https://blog.csdn.net/weixin_45068267/category_12268204.html
(内含其它设计模式的介绍和实现)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

副本集 Election succeeded

目录 1. 分析mongo副本集 Election succeeded 的全过程&#xff1a;2. 从日志里面看到数据库一致性的对比吗&#xff1f;3. 模拟主备不同步&#xff0c;副本集切换步骤注意事项&#xff1a; not master and slaveOkfalse解释&#xff1a; 其他方案方法一&#xff1a;使用 rs.st…

时间同步服务

多主机协作工作时&#xff0c;各个主机的时间同步很重要&#xff0c;时间不一致会造成很多重要应用的故障&#xff0c;如&#xff1a;加密协 议&#xff0c;日志&#xff0c;集群等。 利用NTP&#xff08;Network Time Protocol&#xff09; 协议使网络中的各个计算机时间达到…

全英文地图/天地图和谷歌瓦片地图杂交/设备分布和轨迹回放/无需翻墙离线使用

一、前言说明 随着风云局势的剧烈变化&#xff0c;对我们搞软件开发的人员来说&#xff0c;影响也是越发明显&#xff0c;比如之前对美对欧的软件居多&#xff0c;现在慢慢的变成了对大鹅和中东以及非洲的居多&#xff0c;这两年明显问有没有俄语或者阿拉伯语的输入法的增多&a…

vmware用ghost镜像ios、esd格式装系统

1、需要下载一个pe.iso镜像&#xff0c;可以用大白菜&#xff0c;老毛桃什么的&#xff0c;vmware选择从光盘启动 然后在PE里面把磁盘分为两个区&#xff0c;C,D盘 然后修改ISO镜像&#xff0c;变成要恢复的ghost包 把iso里面文件拷贝到D盘&#xff0c;用桌面PE工具开始重…

鸿蒙开发中实现自定义弹窗 (CustomDialog)

效果图 #思路 创建带有 CustomDialog 修饰的组件 &#xff0c;并且在组件内部定义controller: CustomDialogController 实例化CustomDialogController&#xff0c;加载组件&#xff0c;open()-> 打开对话框 &#xff0c; close() -> 关闭对话框 #定义弹窗 (CustomDial…

视频汇聚平台LntonAIServer视频质量诊断功能--偏色检测与噪声检测

随着视频监控技术的不断进步&#xff0c;视频质量成为了决定监控系统性能的关键因素之一。LntonAIServer新增的视频质量诊断功能&#xff0c;特别是偏色检测和噪声检测&#xff0c;进一步强化了视频监控系统的可靠性和实用性。下面我们将详细介绍这两项功能的技术细节、应用场景…

[数据集][目标检测]机油泄漏检测数据集VOC+YOLO格式43张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;43 标注数量(xml文件个数)&#xff1a;43 标注数量(txt文件个数)&#xff1a;43 标注类别数…

图形语言传输格式glTF和三维瓦片数据3Dtiles(b3dm、pnts)学习

文章目录 一、3DTiles二、b3dm三、glTF1.glTF 3D模型格式有两种2.glTF 场景描述结构和坐标系3.glTF的索引访问与ID4.glTF asset5.glTF的JSON结构scenesscene.nodes nodesnodes.children transformations对外部数据的引用buffers 原始二进制数据块&#xff0c;没有固有的结构或含…

【Day09】

目录 Mybatis-基础操作-环境准备 Mybatis-基础操作-删除 Mybatis-基础操作-删除&#xff08;预编译SQL&#xff09; Mybatis-基础操作-新增 Mybatis-基础操作-新增(主键返回) Mybatis-基础操作-更新 Mybatis-基础操作-查询&#xff08;根据ID查询&#xff09; Mybatis-基…

Apache Pig

目录 一、配置说明1.本地模式2.集群模式 二、pig的数据模型三、pig的数据类型四、惰性执行五、pig的基本语法5.1语法说明5.2案例操作 六、pig的自定义函数 一、配置说明 1.本地模式 操作的是Linux系统文件 pig -x local关键日志 当前处于root目录下 2.集群模式 连接的是…

14.1 为什么说k8s中监控更复杂了

本节重点介绍 : k8s中监控变得复杂了&#xff0c;挑战如下 挑战1: 监控的目标种类多挑战2: 监控的目标数量多挑战3: 对象的变更和扩缩特别频繁挑战4: 监控对象访问权限问题 k8s架构图 k8s中监控变得复杂了&#xff0c;挑战如下 挑战1: 监控的目标种类多 对象举例 podnodese…

资料分析系统课-刘文超老师

1、考试大纲 2、解题的问题->解决方法 3、统计术语 基期量与现期量&#xff1a;作为对比参照的时期称为基期&#xff0c;而相对于基期的称为现期。描述具体数值时我们称之为基期量和现期量。 增长量&#xff1a;是指基期量与现期量增长(或减少)的绝对量。增长量是具体值&…

点云数据常见的坐标系有哪些,如何进行转换?

文章目录 一、点云坐标系分类1. 世界坐标系2. 相机坐标系3. 极坐标系4. 笛卡尔坐标系(直角坐标系):5. 传感器坐标系6. 地理坐标系二、坐标系转换方法1. 地理坐标系与投影坐标系之间的转换2. 投影坐标系与局部坐标系之间的转换3. 局部坐标系与3D模型坐标系之间的转换4. 相机坐…

【Grafana】Prometheus结合Grafana打造智能监控可视化平台

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

Jenkins+Svn+Vue自动化构建部署前端项目(保姆级图文教程)

目录 介绍 准备工作 配置jenkins 构建部署任务 常见问题 介绍 在平常开发前端vue项目时,我们通常需要将vue项目进行打包构建,将打包好的dist目录下的静态文件上传到服务器上,但是这种繁琐的操作是比较浪费时间的,可以使用jenkins进行自动化构建部署前端vue 准备工作 准备…

【论文阅读】CiteTracker: Correlating Image and Text for Visual Tracking

paper&#xff1a;[2308.11322] CiteTracker: Correlating Image and Text for Visual Tracking (arxiv.org) code&#xff1a;NorahGreen/CiteTracker: [ICCV23] CiteTracker: Correlating Image and Text for Visual Tracking (github.com) 简介 现有的视觉跟踪方法通常以…

[C#学习笔记]注释

官方文档&#xff1a;Documentation comments - C# language specification | Microsoft Learn 一、常用标记总结 1.1 将文本设置为代码风格的字体&#xff1a;<c> 1.2 源代码或程序输出:<code> 1.3 异常指示:<exception> 1.4 段落 <para> 1.5 换行&…

Ubuntu 22.04 make menuconfig 失败原因

先 安装一些配置 linux下使用menuconfig需要安装如下库_menuconfig 安装-CSDN博客 然后 cd 到指定源代码 需要在内核文件目录下编译 Linux 内核源码&#xff08;kernel source&#xff09;路径_--kernel-source-path-CSDN博客 make menuconfig 又报错 说是gcc 12什么什么&…

QT6聊天室项目 网络通信实现逻辑分析

实现逻辑 模块话网络通信设计分析 NetClient类 功能&#xff1a;负责与服务器进行通信httpClient:处理HTTP请求websocketClient&#xff1a;处理WebSocket通信 HTTP请求封装 设计请求和服务器响应的接口设计函数测试网络连接性设计处理的函数处理HTTP请求&#xff08;后期实现…

file | 某文件夹【解耦合】下的文件查找功能实现及功能单元测试

文件查找工具 概要思路OS模块 --- 学习版os.getcwd()os.path.dirname(os.getcwd())os.path.dirname() 和 os.path.basename() OS模块 — 实战版单元测试解耦合 概要 梳理业务主逻辑&#xff1a; 查看存放被采集JSON数据的文件夹内的文件列表【所有 包含文件夹下的文件夹下的文…