设计模式(单例模式、工厂模式及适配器模式、装饰器模式)

目录

0 、设计模式简介

一、单例模式

二、工厂模式

三、适配器模式

四、装饰器模式 


0 、设计模式简介

设计模式可以分为以下三种: 

  1. 创建型模式:用来描述 “如何创建对象”,它的主要特点是 “将对象的创建和使用分离”。包括单例、原型、工厂方法抽象工厂和建造者 5 种模式。
  2. 结构型模式:用来描述如何将类或对象按照某种布局组成更大的结构。包括代理、适配器、桥接、装饰、外观、享元和组合 7 种模式。
  3. 行为型模式:用来识别对象之间的常用交流模式以及如何分配职责。包括模板方法、策略、命令、职责链、状态、观察者、中介者、迭代器、访问者、备忘录和解释器 11 种模式。

下面将介绍单例模式、工厂模式、适配器模式、组合模式。 

一、单例模式

单例模式的实现需要三个必要的条件

  1. 单例类的构造函数必须是私有的,这样才能将类的创建权控制在类的内部,从而使得类的外部不能创建类的实例。
  2. 单例类通过一个私有的静态变量来存储其唯一实例。
  3. 单例类通过提供一个公开的静态方法,使得外部使用者可以访问类的唯一实例。

注意:
因为单例类的构造函数是私有的,所以单例类不能被继承。

另外,实现单例类时,还需要考虑三个问题:

  • 创建单例对象时,是否线程安全。
  • 单例对象的创建,是否延时加载。
  • 获取单例对象时,是否需要加锁(锁会导致低性能)。

单例模式分为两种:饿汉方式和懒汉方式

饿汉方式:在类加载的时候就实例化对象,懒汉方式:只有在使用的时候才初始化对象。

饿汉式单例优缺点:

  • 优点:
    • 单例对象的创建是线程安全的;
    • 获取单例对象时不需要加锁。
    • 不用考虑线程安全问题,不需要加锁,执行效率较高
  • 缺点:
    • 单例对象的创建,不是延时加载。
    • 类加载的时候就初始化,不管后期用不用都占着空间,浪费了内存

懒汉式单例优缺点:

  • 优点:
    • 对象的创建是线程安全的。
    • 支持延时加载
  • 缺点:获取对象的操作被加上了锁,影响了并发度。
    • 如果单例对象需要频繁使用,那这个缺点就是无法接受的。
    • 如果单例对象不需要频繁使用,那这个缺点也无伤大雅。

使用场景: 

  • 饿汉式单例模式在类加载时就创建实例,适用于单例对象的创建频繁的场景,以及对于性能要求较高的场景。
  • 懒汉式单例模式在第一次被使用时创建实例,适用于单例对象的创建不是很频繁的场景。

饿汉方式代码:

class singleton

{

    protected:

        singleton()

        {}
    
    private:

        static singleton* p;

    public:

    static singleton* initance();

};

singleton* singleton::p = new singleton;

singleton* singleton::initance()

{

    return p;

}

懒汉方式代码:

class singleton
{
protected:
    singleton()
    {
        pthread_mutex_init(&mutex);
    }
private:
    static singleton* p;
public:
    static pthread_mutex_t mutex;
    static singleton* initance();
};
 
pthread_mutex_t singleton::mutex;
singleton* singleton::p = NULL;
singleton* singleton::initance()
{
    if (p == NULL)
    {
        pthread_mutex_lock(&mutex);
        if (p == NULL)
            p = new singleton();
        pthread_mutex_unlock(&mutex);
    }
    return p;
}

二、工厂模式

工厂模式分为简单工厂模式、工厂方法模式、抽象工厂模式。

简单工厂模式:提供一个工厂类,在这个工厂类中生产A、B、C类产品。

工厂方法模式:提供n个工厂类,都继承自同一个总工厂类,然后分别生产各自的产品。

抽象工厂模式:提供n个工厂类,都继承自同一个总工厂类,然后分别生产各自的产品,产品可以包含多种。也就是对于每一个工厂,有多条生产线。

1.简单工厂模式:

NiKeShoes、AdidasShoes、LiNingShoes为具体鞋子的类,分别是耐克、阿迪达斯和李宁鞋牌的鞋,它们都继承于Shoes抽象类。 

// 鞋子抽象类
class Shoes
{
public:
    virtual ~Shoes() {}
    virtual void Show() = 0;
};

// 耐克鞋子
class NiKeShoes : public Shoes
{
public:
    void Show()
    {
        std::cout << "我是耐克球鞋,我的广告语:Just do it" << std::endl;
    }
};

// 阿迪达斯鞋子
class AdidasShoes : public Shoes
{
public:
    void Show()
    {
        std::cout << "我是阿迪达斯球鞋,我的广告语:Impossible is nothing" << std::endl;
    }
};

// 李宁鞋子
class LiNingShoes : public Shoes
{
public:
    void Show()
    {
        std::cout << "我是李宁球鞋,我的广告语:Everything is possible" << std::endl;
    }
};

ShoesFactory为工厂类,类里实现根据鞋子类型创建对应鞋子产品对象的CreateShoes(SHOES_TYPE type)函数。 

enum SHOES_TYPE
{
    NIKE,
    LINING,
    ADIDAS
};

// 总鞋厂
class ShoesFactory
{
public:
    // 根据鞋子类型创建对应的鞋子对象
    Shoes *CreateShoes(SHOES_TYPE type)
    {
        switch (type)
        {
        case NIKE:
            return new NiKeShoes();
            break;
        case LINING:
            return new LiNingShoes();
            break;
        case ADIDAS:
            return new AdidasShoes();
            break;
        default:
            return NULL;
            break;
        }
    }
};

main函数,先是构造了工厂对象,后创建指定类型的具体鞋子产品对象,创建了具体鞋子产品的对象便可直接打印广告。因为采用的是`new`的方式创建了对象,用完了要通过`delete` 释放资源资源。

int main()
{
    // 构造工厂对象
    ShoesFactory shoesFactory;

    // 从鞋工厂对象创建阿迪达斯鞋对象
    Shoes *pNikeShoes = shoesFactory.CreateShoes(NIKE);
    if (pNikeShoes != NULL)
    {
        // 耐克球鞋广告喊起
        pNikeShoes->Show();

        // 释放资源
        delete pNikeShoes;
        pNikeShoes = NULL;
    }

    // 从鞋工厂对象创建阿迪达斯鞋对象
    Shoes *pLiNingShoes = shoesFactory.CreateShoes(LINING);
    if (pLiNingShoes != NULL)
    {
        // 李宁球鞋广告喊起
        pLiNingShoes->Show();

        // 释放资源
        delete pLiNingShoes;
        pLiNingShoes = NULL;
    }

    // 从鞋工厂对象创建阿迪达斯鞋对象
    Shoes *pAdidasShoes = shoesFactory.CreateShoes(ADIDAS);
    if (pAdidasShoes != NULL)
    {
        // 阿迪达斯球鞋广告喊起
        pAdidasShoes->Show();

        // 释放资源
        delete pAdidasShoes;
        pAdidasShoes = NULL;
    }

    return 0;
}

输出结果:

2.工厂方法模式: (有的地方直接是抽象工厂模式,即对工厂也进行了抽象)

ShoesFactory抽象工厂类,提供了创建具体鞋子产品的纯虚函数。

NiKeProducer、AdidasProducer、LiNingProducer具体工厂类,继承持续工厂类,实现对应具体鞋子产品对象的创建。

// 总鞋厂
class ShoesFactory
{
public:
    virtual Shoes *CreateShoes() = 0;
    virtual ~ShoesFactory() {}
};

// 耐克生产者/生产链
class NiKeProducer : public ShoesFactory
{
public:
    Shoes *CreateShoes()
    {
        return new NiKeShoes();
    }
};

// 阿迪达斯生产者/生产链
class AdidasProducer : public ShoesFactory
{
public:
    Shoes *CreateShoes()
    {
        return new AdidasShoes();
    }
};

// 李宁生产者/生产链
class LiNingProducer : public ShoesFactory
{
public:
    Shoes *CreateShoes()
    {
        return new LiNingShoes();
    }
};

main函数针对每种类型的鞋子,构造了每种类型的生产线,再由每个生产线生产出对应的鞋子。需注意的是具体工厂对象和具体产品对象,用完了需要通过delete释放资源。 

int main()
{
    // ================ 生产耐克流程 ==================== //
    // 鞋厂开设耐克生产线
    ShoesFactory *niKeProducer = new NiKeProducer();
    // 耐克生产线产出球鞋
    Shoes *nikeShoes = niKeProducer->CreateShoes();
    // 耐克球鞋广告喊起
    nikeShoes->Show();
    // 释放资源
    delete nikeShoes;
    delete niKeProducer;

    // ================ 生产阿迪达斯流程 ==================== //
    // 鞋厂开设阿迪达斯生产者
    ShoesFactory *adidasProducer = new AdidasProducer();
    // 阿迪达斯生产线产出球鞋
    Shoes *adidasShoes = adidasProducer->CreateShoes();
    // 阿迪达斯球鞋广喊起
    adidasShoes->Show();
    // 释放资源
    delete adidasShoes;
    delete adidasProducer;

    return 0;
}

 输出结果:

3.抽象工厂模式: 

鞋厂为了扩大了业务,不仅只生产鞋子,把运动品牌的衣服也一起生产了。

Clothe和Shoes,分别为衣服和鞋子的抽象产品类。

NiKeClothe和NiKeShoes,分别是耐克衣服和耐克衣服的具体产品类。

// 基类 衣服
class Clothe
{
public:
    virtual void Show() = 0;
    virtual ~Clothe() {}
};

// 耐克衣服
class NiKeClothe : public Clothe
{
public:
    void Show()
    {
        std::cout << "我是耐克衣服,时尚我最在行!" << std::endl;
    }
};

// 基类 鞋子
class Shoes
{
public:
    virtual void Show() = 0;
    virtual ~Shoes() {}
};

// 耐克鞋子
class NiKeShoes : public Shoes
{
public:
    void Show()
    {
        std::cout << "我是耐克球鞋,让你酷起来!" << std::endl;
    }
};

Factory为抽象工厂,提供了创建鞋子CreateShoes()和衣服产品CreateClothe()对象的接口。

NiKeProducer为具体工厂,实现了创建耐克鞋子和耐克衣服的方式。

// 总厂
class Factory
{
public:
    virtual Shoes *CreateShoes() = 0;
	virtual Clothe *CreateClothe() = 0;
    virtual ~Factory() {}
};

// 耐克生产者/生产链
class NiKeProducer : public Factory
{
public:
    Shoes *CreateShoes()
    {
        return new NiKeShoes();
    }
	
	Clothe *CreateClothe()
    {
        return new NiKeClothe();
    }
};

main函数,构造耐克工厂对象,通过耐克工厂对象再创建耐克产品族的衣服和鞋子对象。同样,对象不再使用时,需要手动释放资源。 

int main()
{
    // ================ 生产耐克流程 ==================== //
    // 鞋厂开设耐克生产线
    Factory *niKeProducer = new NiKeProducer();
    
	// 耐克生产线产出球鞋
    Shoes *nikeShoes = niKeProducer->CreateShoes();
	// 耐克生产线产出衣服
    Clothe *nikeClothe = niKeProducer->CreateClothe();
    
	// 耐克球鞋广告喊起
    nikeShoes->Show();
	// 耐克衣服广告喊起
    nikeClothe->Show();
	
    // 释放资源
    delete nikeShoes;
	delete nikeClothe;
    delete niKeProducer;


    return 0;
}

 输出结果:

三、适配器模式

适配器模式的作用是解决两个软件实体间的接口不兼容的问题。将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作。

适配器模式有两种实现方法,类适配器和对象适配器。类适配器以多继承方式实现。对象适配器以组合的方式实现,即适配器类中包含了适配者类对象。(调用了适配者类中的方法,即新的方法)

一共包含三个类:目标类、适配者类、适配器类。目标类中的接口和适配者类中的接口不兼容,可通过引入适配器类,在适配器类中,保留了原目标类的接口名,调用了适配者类接口中的功能,对功能重新进行了封装,然后通过目标类指针指向适配器类对象或者目标类引用引用适配器类对象就可调用新的功能,完成对该接口的功能完善。
 

应用场景:通过适配器完成USB与TypeC的对接。原先接口功能是USB接口,新的接口是TypeC接口,需要完成适配,即可以把USB接口当成TypeC接口来使用。(相当于给中间连了转换器)

类适配器代码:

/* Connect Usb port */
class CUsbDisk
{
public:
    virtual ~CUsbDisk() {}

    virtual void ConnectDevice()
    {
       
        cout << "Connect usb port." << endl;
    }
};

/* Connect Type-C port */
class CTypeCInterface
{
public:
    virtual ~CTypeCInterface() {}

    void ConnectDevice()
    {
        cout << "Connect Type-C port." << endl;
    }
};

/* Not only connect Usb port, but also connect Type-C port */
class CAdapter : public CUsbDisk, public CTypeCInterface
{
public:
    void ConnectDevice()
    {
         //调用了适配者类中的方法,即完善了功能,用旧的名字调用了新的功能。
        CTypeCInterface::ConnectDevice();
    }
};

int main(int argc, char *argv[])
{
 //通过目标类指针指向适配器类对象,调用了新的方法(原先的接口及新的功能)
    CUsbDisk *theDisk = new CAdapter();

    theDisk->ConnectDevice();

    delete theDisk;
    return 0;
}

输出结果:

对象适配器模式:

/* Connect Usb port */
class CUsbDisk
{
public:
    virtual ~CUsbDisk() {}

    virtual void ConnectDevice()
    {
        cout << "Connect usb port." << endl;
    }
};

/* Connect Type-C port */
class CTypeCInterface
{
public:
    virtual ~CTypeCInterface() {}

    void ConnectDevice()
    {
        cout << "Connect Type-C port." << endl;
    }
};

/* Usb device connect phone */
class CAdapter : public CUsbDisk
{
public:
    CAdapter()
    {
        mpAdaptee = new CTypeCInterface();
    }

    ~CAdapter()
    {
        if (NULL != mpAdaptee) {
            delete mpAdaptee;
        }
    }

    void ConnectDevice()
    {
        if (NULL != mpAdaptee) {
            mpAdaptee->ConnectDevice();
        } else {
            cout << "Adapter abnormal. Connect fail!" << endl;
        }
    }

private:
    //包含了适配者类对象,通过该对象调用了新的方法,重新封装了旧接口。
    CTypeCInterface *mpAdaptee;
};


int main(int argc, char *argv[])
{
    CUsbDisk *theDisk = new CAdapter();

    theDisk->ConnectDevice();

    delete theDisk;
    return 0;
}

输出结果:

 或:

/**
 * The Target defines the domain-specific interface used by the client code.
 */
class Target {
 public:
  virtual ~Target() = default;
  virtual std::string Request() const {
    return "Target: The default target's behavior.";
  }
};

/**
 * The Adaptee contains some useful behavior, but its interface is incompatible
 * with the existing client code. The Adaptee needs some adaptation before the
 * client code can use it.
 */
class Adaptee {
 public:
  std::string SpecificRequest() const {
    return ".eetpadA eht fo roivaheb laicepS";
  }
};

/**
 * The Adapter makes the Adaptee's interface compatible with the Target's
 * interface using multiple inheritance.
 */
class Adapter : public Target, public Adaptee {
 public:
  Adapter() {}
  std::string Request() const override {
    std::string to_reverse = SpecificRequest();
    std::reverse(to_reverse.begin(), to_reverse.end());
    return "Adapter: (TRANSLATED) " + to_reverse;
  }
};

/**
 * The client code supports all classes that follow the Target interface.
 */
void ClientCode(const Target *target) {
  std::cout << target->Request();
}

int main() {
  std::cout << "Client: I can work just fine with the Target objects:\n";
  Target *target = new Target;
  ClientCode(target);
  std::cout << "\n\n";
  Adaptee *adaptee = new Adaptee;
  std::cout << "Client: The Adaptee class has a weird interface. See, I don't understand it:\n";
  std::cout << "Adaptee: " << adaptee->SpecificRequest();
  std::cout << "\n\n";
  std::cout << "Client: But I can work with it via the Adapter:\n";
  Adapter *adapter = new Adapter;
  ClientCode(adapter);
  std::cout << "\n";

  delete target;
  delete adaptee;
  delete adapter;

  return 0;
}

 结果如下:

Client: I can work just fine with the Target objects:
Target: The default target's behavior.

Client: The Adaptee class has a weird interface. See, I don't understand it:
Adaptee: .eetpadA eht fo roivaheb laicepS

Client: But I can work with it via the Adapter:
Adapter: (TRANSLATED) Special behavior of the Adaptee.s

四、装饰器模式 

装饰器模式是比较常用的一种设计模式,Python中就内置了对于装饰器的支持。

具体来说,装饰器模式是用来给对象增加某些特性或者对被装饰对象进行某些修改。

如上图所示,需要被装饰的对象在最上方,它自身可以有自己的实例,一般通过抽象类来实现(Java中也可以通过接口实现)。

右侧中间是一个装饰器类或者接口,其实内容与原对象基本一致,不过我们自定义的装饰器一般会继承这个装饰器基类。

最下层就是具体的装饰器了,可以看到,具体装饰器类中需要包含被装饰对象成员(也就是说,装饰器需要和被装饰对象有同样的子类),然后增加一些额外的操作。

下面的代码是一个买煎饼的例子,如我们生活中所见,可以选基础煎饼(鸡蛋煎饼,肉煎饼等),然后再额外加别的东西。

 代码如下:

#include<iostream>
#include<string>
using namespace std;

class Pancake//基类
{
public:
    string description = "Basic Pancake";
    virtual string getDescription(){ return description; }
    virtual double cost() = 0;
};

class CondimentDecorator :public Pancake//装饰器基类
{
public:
    string getDescrition();
};

class MeatPancake :public Pancake//肉煎饼
{
public:
    MeatPancake(){ description = "MeatPancake"; }
    double cost(){ return 6; }
};
class EggPancake :public Pancake//鸡蛋煎饼
{
public:
    EggPancake(){ description = "EggPancake"; }
    double cost(){ return 5; }
};

class Egg :public CondimentDecorator//额外加鸡蛋
{
public:
    Pancake* base;
    string getDescription(){ return base->getDescription() + ", Egg"; }
    Egg(Pancake* d){ base = d; }
    double cost(){ return base->cost() + 1.5; }
};
class Potato :public CondimentDecorator//额外加土豆
{
public:
    Pancake* base;
    string getDescription(){ return base->getDescription() + ", Potato"; }
    Potato(Pancake* d){ base = d; }
    double cost(){ return base->cost() + 1; }
};
class Bacon :public CondimentDecorator//额外加培根
{
public:
    Pancake* base;
    string getDescription(){ return base->getDescription() + ", Bacon"; }
    Bacon(Pancake* d){ base = d; }
    double cost(){ return base->cost() + 2; }
};


int main()
{
    Pancake* pan = new EggPancake();
    pan = &Potato(pan);
    pan = &Bacon(pan);
    cout << pan->getDescription() << "  $ : " << pan->cost() << endl;
    system("pause");
    return 0;
}

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

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

相关文章

141. 环形链表、Leetcode的Python实现

博客主页&#xff1a;&#x1f3c6;看看是李XX还是李歘歘 &#x1f3c6; &#x1f33a;每天分享一些包括但不限于计算机基础、算法等相关的知识点&#x1f33a; &#x1f497;点关注不迷路&#xff0c;总有一些&#x1f4d6;知识点&#x1f4d6;是你想要的&#x1f497; ⛽️今…

数字孪生技术与VR:创造数字未来

在当今数字化浪潮中&#xff0c;数字孪生和虚拟现实&#xff08;VR&#xff09;技术是两大亮点&#xff0c;它们以独特的方式相互结合&#xff0c;为各个领域带来了创新和无限可能。本篇文章将探讨数字孪生与VR之间的关系&#xff0c;以及它们如何共同开辟未来的新前景。 数字…

数字化如何赋能企业降本增效?

在当前高度不确定的市场环境下&#xff0c;降本增效已成为传统企业热议的话题。在这个背景下&#xff0c;企业内部各种“卷”现象层出不穷&#xff0c;各部门都在积极降本、开源节流&#xff0c;同时也在争夺本就不足的企业资源。因此&#xff0c;数字部门在资源受限的情况下&a…

【Amazon】跨AWS账号资源授权存取访问

文章目录 一、实验框架图二、实验过程说明三、实验演示过程1、在A账号中创建S3存储桶2、在A账号创建S3存储桶访问策略3、在A账号创建信任开发账号的角色4、在B账号为用户添加内联策略5、在B账号中切换角色&#xff0c;以访问A账号中的S3资源 四、实验总结 一、实验框架图 本次…

dash--项目的前端展示简单基础

1.前置工作 创建虚拟环境&#xff1a; sudo apt-get install python3-venv # 安装 python3 -m venv venv # 在本目录下创建venv虚拟环境&#xff08;也是一个文件夹。如果用不到这个虚拟环境以后就rm -rf venv&#xff09; source venv/bin/activate # 激活虚拟环境临时使用清华…

C语言实现贪吃蛇小游戏

#include <stdio.h> #include <easyx.h> #include <iostream> #include <math.h> #include <stdlib.h> #include <conio.h> #include <time.h> #define PI 3.14 #define NODE_WIDTH 40 //绘制蛇的节点 typedef struct {int x;int y;…

vue中electron与vue通信(fs.existsSync is not a function解决方案)

electron向vue发送消息 dist/main.js (整个文件配置在另一条博客里) win new BrowserWindow({width:1920,height:1080,webPreferences: {// 是否启用Node integrationnodeIntegration: true, // Electron 5.0.0 版本之后它将被默认false// 是否在独立 JavaScript 环境中运行…

三.RocketMQ单机安装及集群搭建

RocketMQ单机安装及集群搭建 一&#xff1a;安装环境1.软硬件要求2.下载RocketMQ 二.安装单机MQ1.上传并解压2.目录介绍3.修改MQ启动时初始JVM内存4.启动NameServer与Broker5.测试RocketMQ 三.RocketMQ集群搭建1.集群概念特点2.集群模式分类3.集群工作流程4.双主双从集群搭建4.…

GORM:在Go中轻松管理数据库

GORM综合介绍 - Go对象关系映射库 在现代软件开发中&#xff0c;高效的数据库管理对于构建强大的应用程序至关重要。GORM是Go开发人员寻求与数据库进行交互的简化方式的宝贵工具。GORM是Go对象关系映射的缩写&#xff0c;它为Go的面向对象世界与数据库的关系世界之间提供了桥梁…

小红书协议算法最新版

如果您想通过学习来了解小红书的点赞、关注、私信等功能的实现&#xff0c;以下是一些一般性的思路和示例代码&#xff0c;供您学习参考&#xff1a; 1. 点赞功能&#xff1a; - 后端实现&#xff1a;在后端&#xff0c;您可以创建一个用于存储用户点赞信息的数据库表。对于每…

测试遇到bug怎么分析,这可能是我看到最专业的总结了!

为什么定位问题如此重要&#xff1f; 可以明确一个问题是不是真的“bug” 很多时候&#xff0c;我们找到了问题的原因&#xff0c;结果发现这根本不是bug。原因明确&#xff0c;误报就会降低多个系统交互&#xff0c;可以明确指出是哪个系统的缺陷&#xff0c;防止“踢皮球”&…

Qlik Sense Enterprise 忘记PostgreSQL密码

在 Windows 上安装 Qlik Sense Enterprise 期间会提供密码。如果您忘记了该密码&#xff0c;则无法找回&#xff1b;但是&#xff0c;可以按照以下步骤重置密码。 如何在 Qlik 中重置忘记的 PostgreSQL 密码... - Qlik Community - 1712725 如果该过程完成后记录了密码错误的…

Python开发技能实战-通过配置的代理服务器在具有外网连接的环境中在Pycharm中运行python代码

实现功能 打开科学上网工具&#xff0c;使得能够在浏览器科学上网&#xff0c;通过科学上网工具的配置文件&#xff0c;可以看出本地和远程代理的映射关系&#xff0c;此时&#xff0c;远程地址本地地址&#xff0c;远程端口本地端口。 1、在程序中配置请求网页代理请求。不需…

Http代理与socks5代理有何区别?如何选择?(二)

上篇文章我们基本分别了解了http代理与socks5代理的定义与优缺点&#xff0c;接下来我们继续来了解http代理与socks5代理之间的比较与区别。 一、两者的比较 1、功能比较 HTTP代理专门用于Web流量&#xff0c;并在处理HTTP和HTTPS协议方面非常高效。它们可以修改正在传输的数据…

解析mfc100u.dll文件丢失的修复方法,快速解决mfc100u.dll问题

在计算机使用过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中最常见的就是“缺少某个文件”的错误。最近&#xff0c;我也遇到了一个这样的问题&#xff0c;那就是“mfc100u.dll丢失”。这个问题可能会导致某些应用程序无法正常运行&#xff0c;给我们带来困扰。…

ANGR初识

首页&#xff1a; https://angr.io 项目存储库&#xff1a; GitHub - angr/angr: A powerful and user-friendly binary analysis platform! 文档&#xff1a; https://docs.angr.io API 文档&#xff1a; angr documentation 练习项目&#xff1a; https://github.com/angr/an…

Python的web自动化学习(五)Selenium的隐式等待(元素定位)

引言&#xff1a; WebDriver隐式等待是一种全局性的等待方式&#xff0c;它会在查找元素时设置一个固定的等待时间。当使用隐式等待时&#xff0c;WebDriver会在查找元素时等待一段时间&#xff0c;如果在等待时间内找到了元素&#xff0c;则立即执行下一步操作&#xff1b;如果…

Windows下多Chrome谷歌浏览器版本共存

场景 某些年代久远的 WEB 应用&#xff0c;必须在指定的浏览器或版本才能正常运行&#x1f602;&#xff0c;此时就需要多个版本 chrome 浏览器共存。 解决方案 下载指定版本 可以从 https://www.chromedownloads.net/ 下载需要的版本&#xff0c;此处下载的是87.0.4280.14…

【MySQL索引与优化篇】数据库设计实操(含ER模型)

数据库设计实操&#xff08;含ER模型&#xff09; 文章目录 数据库设计实操&#xff08;含ER模型&#xff09;1. ER模型1.1 概述1.2 建模分析1.3 ER 模型的细化1.4 ER 模型图转换成数据表1. 一个实体转换成一个数据库表2. 一个多对多的关系转换成一个数据表3. 通过外键来表达1对…

VS2019 C# mysql数据库使用EF

mysql 安装mysql-8.0.18-winx64 mysql-connector-net-8.0.18.msi mysql数据库.net开发驱动&#xff0c; 要在工程中引入connector安装后目录中的mysql.data.dll;如果直接在nutget中下载mysql.data.dll&#xff0c;那么就不用下载.net开发驱动包 mysql-for-visualstudio-1.…
最新文章