常见的设计模式(模板与方法,观察者模式,策略模式)

news2024/11/26 14:34:10

前言

随着时间的推移,软件代码越来越庞大,随着而来的就是如何维护日趋庞大的软件系统。在面向对象开发出现之前,使用的是面向过程开发来设计大型的软件程序,面向过程开发将软件分成一个个单独的模块,模块之间使用函数进行组合,最后完成系统的开发,每次需要修改软件,如果不涉及好各个模块的关系,就会导致软件系统难以维护,从而导致软件变得不可使用。面向对象方法用对象模拟问题域中的实体,以对象间的联系刻画实体间联系。面向对象具有以下优点:
1)系统的稳定性好:当系统的功能需求变化时,不会引起软件结构的整体变化,仅需做一些局部的修改。由于现实世界中的实体是相对稳定的,因此,以对象为中心构造的软件系统也会比较稳定。
2) 可重用性好:面向对象方法具有的继承性和封装性支持软件复用。有两种方法可以重复使用一个对象类。一是创建类的实例,从而直接使用它;二是从它派生出一个满足需要的新类,子类可以重用其父类的数据结构和程序代码,并且可以在父类的基础上方便地修改和扩充,而且子类的修改并不影响父类的使用。
3)可维护性好:由于面向对象的软件稳定性比较好,容易修改、容易理解、易于测试和调试,因而软件的可维护性就会比较好。
4)易于理解:传统的结构化软件开发方法是面向过程的,以算法为核心,数据和过程作为相互独立的部分,数据和过程分离,忽略了数据和操作之间的内在的联系,问题空间和解空间并不是一致的。面向对象的方法是以对象为核心,尽可能接近人类习惯的抽象思维方法,易于理解,并尽量一致地描述问题空间和解空间,从而自然而然地解决问题。
5)较易于开发大型软件产品:用面向对象方法开发大型软件时,把大型软件产品看作是一系列相互独立的小产品,采用RUP(统一开发过程)的迭代开发模型,可以降低开发时的技术难度和开发工作管理的难度。
为了充分利用面向对象的一系列特点,一些开发者就将自己多年来的开发经验整合起来,提出了解决某个特定问题的解决思路。从而形成了设计模式。

基础

由于设计模式是以面向对象为基础的,因此有必要先了解一下类模型和相应的类图,为后面的讲解打下一个基础。

类模型

耦合性:也称块间联系。指软件系统结构中各模块间相互联系紧密程度的一种度量。模块之间联系越紧密,其耦合性就越强,模块的独立性则越差。模块间耦合高低取决于模块间接口的复杂性、调用的方式及传递的信息
内聚性:又称块内联系。指模块的功能强度的度量,即一个模块内部各个元素彼此结合的紧密程度的度量。若一个模块内各元素(语名之间、程序段之间)联系的越紧密,则它的内聚性就越高。
所谓高内聚是指一个软件模块是由相关性很强的代码组成,只负责一项任务,也就是常说的单一责任原则。

耦合:一个软件结构内不同模块之间互连程度的度量。
对于低耦合,粗浅的理解是:一个完整的系统,模块与模块之间,尽可能的使其独立存在。也就是说,让每个模块,尽可能的独立完成某个特定的子功能。模块与模块之间的接口,尽量的少而简单。如果某两个模块间的关系比较复杂的话,最好首先考虑进一步的模块划分。这样有利于修改和组合。
高内聚低耦合是软件工程中的概念,在类模型的体现就是一个类要尽可能包含某类对象的所有属性和实现与之对应的功能,同时类与其他类的依赖要尽可能的小。

类继承(不存在虚函数)和对象组合

图1
图1 继承父类(不存在虚函数)和将对象作为自己的属性

图1使用了2种方式来复用已有的属性。 图1上半部分是一个简单的子类继承父类,下半部分是将父属性作为自己的属性,从而达到复用已有的属性。这2种方式在内存中的布局都是base和subject的其他属性是连续的。这2种方式复用属性都具有很强的耦合。其表现就是subject对base的依赖很强,base不能随意更新,如果一旦更新(比如添加了一个属性),subject会添加大量的代码。,在我们的开发中,要尽量避免这样的情况。

组合对象指针

在这里插入图片描述
这与组合对象有明显的区别,在subject中存放Base指针,这样,Subject对象在内存中的布局就是一个base指针(4字节或者8个字节)+subject,这样,base就可以指向任意的Base类型的对象(Base类对象或者Base子类对象),而不仅仅是Base类对象,这样,Subject和Base的耦合度就大大降低

继承(存在虚函数)

在这里插入图片描述
同不存在虚函数的继承相比而言,存在虚函数的情况,对象的内存布局中存在一个虚函数指针(vptr),vptr在父类和不同的子类中分别指向不同的地址,这也是实现多态的原因。设计模式中的面向接口编程也使用了虚函数。

类图

如果想了解设计模式中的对象中的关系,需要看懂类图,对象与对象之间的关系主要包括关联关系,聚合关系,组合关系,依赖关系,继承关系,实现关系,具体的可以参考博客UML类图

概述

为什么需要设计模式

设计模式是只在软件开发中,经过验证的,用于解决在特定环境下,重复出现的,特定问题的解决方案。
使用设计模式的主要作用包括
(1)重用设计,重用设计比重用代码更有意义,它会自动带来代码重用
(2)在重构中学习设计模式, 在空余时间中对代码进行持续重构.
(3) 增加代码的可维护性, 可扩展性.

设计模式分类

根据设计的类型,主要将设计模式分为:创建型模式,结构型模式和行为型 模式
网上也有很多关于各种设计模式的介绍 和说明,这里我就不一一例举出所有的设计模式了,因为在我们的工作中,一般就使用几种常用的设计模式或者对于刚刚编程的同学,根本不会考虑设计模式,只要把功能实现即可。我们对待设计模式的态度也是如果没有足够的编程经验,也不要过分的考虑设计模式,因为意义不大。

如何设计设计模式

当我们想增加软件的可维护型和可扩展性的时候,需要设计设计模式时,主要遵循以下两个观点:
(1)找出稳定点和变化点,稳定点使用固定的编程模式,然后将变化点隔离出来,进行各自的设计,设计设计模式也主要时设计变化点,根据变化点的不同设计出不同的设计模式。
(2)设计原则优于设计模式,设计时优先满足设计原则,然后慢慢迭代出设计模式。

设计原则

前面提到在设计设计模式时,需要优先考虑设计原则。设计原则主要包括以下几个

依赖倒置

高层模块 (稳定) 不应该依赖低层模块 (变化) , 两者都依赖抽象 (稳定)

抽象(稳定)不依赖于具体实现 (变化), 具体实现依赖于抽象 (稳定).

如何理解: 降低耦合性, 让高层模块和底层模块解耦合.

如何解释这个问题.

具体的类是不稳定的,是变化的. 高层模块一般是稳定的, 稳定的模块不应该依赖一个变化的模块.
依赖倒置原则是最常用, 也是最容易使用的设计原则 (一般在看见稳定依赖低层模块(变化)的代码的时候) 我们完全可以考虑将这个具体类中的特征抽象出来一个抽象层, 接口类(稳定点)
在这里插入图片描述
以自动驾驶为例:
在这里插入图片描述
自动驾驶系统公司是高层,汽车生产厂商为低层,它们不应该互相依赖,一方变动另一方也会跟着变动;而应该抽象一个自动驾驶行业标准,高层和低层都依赖它;这样以来就解耦了两方的变动;自动驾驶系统、汽车生产厂商都是具体实现,它们应该都依赖自动驾驶行业标准(抽象);

开放封闭

一个类应该对扩展(组合和继承)开放,对修改关闭;

面向接口

  • 不将变量类型声明为某个特定的具体类,而是声明为某个接口;
  • 客户程序无需获知对象的具体类型,只需要知道对象所具有的接口;
  • 减少系统中各部分的依赖关系,从而实现“高内聚、松耦合”的类型设计方案;

单一职责

一个类应该仅有一个引起它变化的原因;
相同的职责放到一起,不同的职责分解到不同的接口和实现中去,这个是最容易也是最难运用的原则,关键还是要从业务出发,从需求出发,识别出同一种类型的职责。举个例子:人的行为分析,包括了生理、生活和工作等行为的分析,生理行为包括吃、跑、睡等行为,生活行为包括穿衣等行为,工作行为包括上下班,开会等行为

里氏替换

子类型必须能够替换掉它的父类型;主要出现在子类覆盖父类实现,原来使用父类型的程序可能出现错误;覆盖了父类方法却没有实现父类方法的职责;

接口隔离

  • 不应该强迫客户依赖于它们不用的方法;
  • 一般用于处理一个类拥有比较多的接口,而这些接口涉及到很多职责;

常见设计模式

模板与方法

定义

模板与方法应该是最常使用的设计模式,在GOF(设计模式)中的定义:定义一个操作中的算法的骨架 ,而将一些步骤延迟到子类中。 Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

要点

  • 最常用的设计模式,子类可以复写父类子流程,使父类的骨架流程丰富;
  • 反向控制流程的典型应用;
  • 父类 protected 保护子类需要复写的子流程;这样子类的子流程只能父类来调用;

本质

通过固定算法骨架来约束子类的行为;

结构图

在这里插入图片描述

示例代码

背景:比如使用播放器播放不同类型的视频(文件,摄像头,网络流等)

不使用模板方法来实现可以按照以下代码来模拟实现过程

class Player {
public:
    Player(int type = 1) : _type(type) {}
public:
    void play() {
        if(initial()){
            doplay();
           uninitial();
        }
    }
// 接口隔离 不要让用户去选择它们不需要的接口
private:
    bool initial() {
        cout << _type << "initial" << endl;
        return true;
    }
    void doplay() {
        if (_type == 1) {
            cout << _type << "播放操作" << endl;
        } else if (_type == 2) {
            cout << _type << "播放操作" << endl;
        } else if (_type == 3) {
			cout << _type << "播放操作" << endl;
        }
    }
    void uninitial() {
        cout << "释放操作" << endl;
    }
private:
    int _type;
};

int main () {
    Player *player = new Player(1);
    player->play();
    return 0;
}

以上代码没有使用设计模式,同样能实现不同类型的播放代码的模拟功能,但是以上代码有很多问题,代码随着时间,变得越来越难以维护,这里的代码主要不满足开闭原则

通过观察发现,播放器播放视频的初始化和释放操作对所有的视频类型都是一样的,然后不同的播放主流程却是不一致的,因此我们仅仅需要修改播放主流程。我们可以将其修改为以下的代码

class IPlayer {
public:
    IPlayer() {}
    virtual ~IPlayer() {}
public:
    void play() {
        if(initial()){
            doplay();
           uninitial();
        }
    }
// 接口隔离 不要让用户去选择它们不需要的接口
protected:
   virtual bool initial() {
        cout << "initial" << endl;
        return true;
    }
   virtual void doplay() {
       cout << "播放操作" << endl;
    }
   virtual void uninitial() {
        cout << "释放操作" << endl;
    }
};

class FilePlayer{
   FilePlayer() {}
   ~FilePlayer(){}
protected:
   virtual void doplay(){
      cout << "文件播放操作" << endl;
   }
};

class NetworkPlayer{
   NetworkPlayer() {}
   ~NetworkPlayer(){}
protected:
   virtual void doplay(){
      cout << "网络流播放操作" << endl;
   }
};

class CameraPlayer{
   CameraPlayer() {}
   ~CameraPlayer(){}
protected:
   virtual void doplay(){
      cout << "摄像头播放操作" << endl;
   }
};

int main () {
    IPlayer *ip = new CameraPlayer;
    ip->play();
    return 0;
}

经过修改,使用了模板与方法编程,并且遵循了设计模式中的开闭原则(能够扩展多个类,但是不需要对已有类进行修改),接口编程(IPlayer作为接口进行调用),单一职责原则(在具体的类里只实现一个具体的操作,那就是播放)

观察者模式

如果有QT开发经验的同学,肯定使用过QT中的信号槽。信号槽是 Qt 框架引以为豪的机制之一。所谓信号槽,实际就是观察者模式。当某个事件发生之后,比如,按钮检测到自己被点击了一下,它就会发出一个信号(signal)。这种发出是没有目的的,类似广播。如果有对象对这个信号感兴趣,它就会使用连接(connect)函数,意思是,将想要处理的信号和自己的一个函数(称为槽(slot))绑定来处理这个信号。也就是说,当信号发出时,被连接的槽函数会自动被回调。

定义

定义对象间的一种一对多(变化)的依赖关系,以便当一个对象(Subject)的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。 ——《 设计模式》 GoF

要点

  • 观察者模式使得我们可以独立地改变目标与观察者,从而使二者之间的关系松耦合;
  • 观察者自己决定是否订阅通知,目标对象并不关注谁订阅了;
  • 观察者不要依赖通知顺序,目标对象也不知道通知顺序;
  • 常用在基于事件的ui框架中,也是 MVC 的组成部分;
  • 常用在分布式系统中、actor框架中;

本质

触发联动

结构图

实现观察者模式时要注意具体目标对象和具体观察者对象之间不能直接调用,否则将使两者之间紧密耦合起来,这违反了面向对象的设计原则。
在这里插入图片描述
观察者模式的主要角色如下。
抽象主题(Subject)角色:也叫抽象目标类或目标接口类,它提供了一个用于保存观察者对象的聚集类和增加、删除观察者对象的方法,以及通知所有观察者的抽象方法。
具体主题(Concrete Subject)(被观察目标)角色:也叫具体目标类,它是被观察的目标,它实现抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象。
观察者接口(Observer)角色:它是一个抽象类或接口,它包含了一个更新自己的抽象方法,当接到具体主题的更改通知时被调用。
具体观察者(Concrete Observer)角色:实现抽象观察者中定义的抽象方法,以便在得到目标的更改通知时更新自身的状态。

示例代码

示例:气象站发布气象资料给数据中心,数据中心经过处理,将气象信息更新到2个不同的显示终端(A和B)
示例中主要有数据中心,气象资料,气象信息和显示终端。经过分析可以得出显示终端为观察者,数据中心为主题对象。

如果不使用观察者模式,可以用如下代码实现


class DisplayA {
public:
    void Show(float temperature);
};

class DisplayB {
public:
    void Show(float temperature);
};

class DisplayC {
public:
    void Show(float temperature);
}

class WeatherData {
    WeaterData(){ temper = 0;}
   float getTemper() {return temper;}
private: 
   float temper;
};

class DataCenter {
public:
    float CalcTemperature() {
        WeatherData * data = GetWeatherData();
        float temper = getTemper();
        return temper;
    }
private:
    WeatherData * GetWeatherData(); // 不同的方式
};

// 订阅发布
int main() {
    DataCenter *center = new DataCenter;
    DisplayA *da = new DisplayA;
    DisplayB *db = new DisplayB;
    DisplayC *dc = new DisplayC;
    float temper = center->CalcTemperature();
    da->Show(temper);
    db->Show(temper);
    dc->Show(temper);
    return 0;
}

以上代码没有使用观察者模式,由数据中心得到气温信息,然后由终端自行去显示气温信息。这样,数据中心和各个终端对象的耦合性非常强,只要数据中心更新了气温信息,显示终端都要自行去显示气温信息,如果增加了一个显示终端,也需要显示终端的显示气温信息。那么当数据中心更新气象信息时,都会主动去通知显示终端(观察者 )。这样显示终端和数据中心的耦合度就减少了.

当我们使用观察者模式,先来找稳定点和变化点
稳定点:气象资料(代码不需要做修改)
变化点:显示终端(不同显示终端有不同的显示和一些定制内容-比如建议等等),数据中心处理气象资料
以下是使用观察者模式使用的代码

#include <vector>

//
class IDisplay {
public:
    virtual void Show(float temperature) = 0;
    virtual ~IDisplay() {}
};

class DisplayA : public IDisplay {
public:
    virtual void Show(float temperature);
private:
    void jianyi();
};

class DisplayB : public IDisplay{
public:
    virtual void Show(float temperature);
};

class DisplayC : public IDisplay{
public:
    virtual void Show(float temperature);
};

class WeatherData {
    WeaterData(){ temper = 0;}
   float getTemper() {return temper;}
private: 
   float temper;
};

class DataCenter {
public:
    void Attach(IDisplay * ob);
    void Detach(IDisplay * ob);
    void Notify() {
        float temper = CalcTemperature();
        for (auto iter = obs.begin(); iter != obs.end(); iter++) {
            (*iter)->Show(temper);
        }
    }

// 接口隔离
private:
    virtual WeatherData * GetWeatherData();

    virtual float CalcTemperature() {
        WeatherData * data = GetWeatherData();
        float temper = getTemper();
        return temper;
    }
    std::vector<IDisplay*> obs;
};

int main() {
    DataCenter *center = new DataCenter;
    IDisplay *da = new DisplayA();
    IDisplay *db = new DisplayB();
    IDisplay *dc = new DisplayC();
    center->Attach(da);
    center->Attach(db);
    center->Attach(dc);
    center->Notify();
    //-----
    center->Detach(db);
    center->Notify();
    return 0;
}

策略模式

定义

定义一系列算法,把它们一个个封装起来,并且使它们可互相替换。该模式使得算法可独立于使用它的客户程序而变化。 ——《设计模式》 GoF

要点

策略模式提供了一系列可重用的算法,从而可以使得类型在运⾏时方便地根据需要在各个算法之间进行切换;

策略模式消除了条件判断语句;也就是在解耦合;

本质

分离算法,选择实现;

结构图

在这里插入图片描述

示例代码

示例:某商场节假日有固定促销活动,为了加大促销力度,现提升国庆节促销活动规格;
按照一般的想法,对于商场而言,各种节假日都有各种的促销活动,那么很容易就想到使用if和else来完成各种节假日的促销活动,代码如下:

enum VacationEnum {
	VAC_Spring,
    VAC_QiXi,
	VAC_Wuyi,
	VAC_GuoQing,
    VAC_ShengDan,
};

class Promotion {
    VacationEnum vac;
public:
    double CalcPromotion(){
        if (vac == VAC_Spring {
            // 春节
        }
        else if (vac == VAC_QiXi) {
            // 七夕
        }
        else if (vac == VAC_Wuyi) {
            // 五一
        }
		else if (vac == VAC_GuoQing) {
			// 国庆
		}
        else if (vac == VAC_ShengDan) {

        }
     }
    
};

如果不使用策略模式,以上代码很容易就知道如果需要添加一种节假日,就会添加一个if代码块,整个文件的代码会越来越大,不符合软件设计种的单一职责和开闭原则等。可以考虑使用策略模式,每种节假日的活动都使用一种策略,代码如下

class Context {

};

class ProStategy {
public:
    virtual double CalcPro(const Context &ctx) = 0;
    virtual ~ProStategy(); 
};
// cpp
class VAC_Spring : public ProStategy {
public:
    virtual double CalcPro(const Context &ctx){}
};
// cpp
class VAC_QiXi : public ProStategy {
public:
    virtual double CalcPro(const Context &ctx){}
};
class VAC_QiXi1  : public VAC_QiXi {
public:
    virtual double CalcPro(const Context &ctx){}
};
// cpp
class VAC_Wuyi : public ProStategy {
public:
    virtual double CalcPro(const Context &ctx){}
};
// cpp
class VAC_GuoQing : public ProStategy {
public:
    virtual double CalcPro(const Context &ctx){}
};

class VAC_Shengdan : public ProStategy {
public:
    virtual double CalcPro(const Context &ctx){}
};

class Promotion {
public:
    Promotion(ProStategy *sss) : s(sss){}
    ~Promotion(){}
    double CalcPromotion(const Context &ctx){
        return s->CalcPro(ctx);
    }
private:
    ProStategy *s;
};

int main () {
    Context ctx;
    ProStategy *s = new VAC_QiXi1();
    Promotion *p = new Promotion(s);
    p->CalcPromotion(ctx);
    return 0;
}

总结

合理的使用设计模式能够使代码更易维护和扩展,但是对于经验不足的同学,不要一味的考虑使用设计模式,那样可能会使你失去对软件开发的乐趣,在没有实现软件功能的前提下,不要考虑使用设计模式,而是优先考虑完成软件的功能。

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

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

相关文章

C语言通过MSXML6.0读写XML文件(同时支持char[]和wchar_t[]字符数组)

开发环境&#xff1a;Visual Studio 2010 运行环境&#xff1a;Windows XP SP3 第一节 读取XML文件&#xff08;使用wchar_t[]字符数组&#xff09; /* 这个程序只能在C编译器下编译成功, 请确保源文件的扩展名为c */ #define COBJMACROS #include <stdio.h> #include …

web蓝桥杯真题--8、和手机相处的时光

介绍 现在都提倡健康使用手机&#xff0c;那么统计一下在一周中每天使用手机的情况吧&#xff01;本题使用 ECharts 实现统计手机使用时长的折线图&#xff0c;但是代码中存在 Bug 需要你去修复。 准备 开始答题前&#xff0c;需要先打开本题的项目代码文件夹&#xff0c;目…

深度学习记录--梯度检验

数值逼近 为了对梯度进行检验&#xff0c;需要计算近似误差值来接近梯度 对于单边误差和双边误差公式&#xff0c;其中双边误差与真实梯度相差更小&#xff0c;故一般采用双边误差公式 双边误差 公式&#xff1a; 梯度检验(gradient checking) 对于成本函数&#xff0c;求出…

Python 邮件发送

首先需要有一个邮箱&#xff0c;没有就注册一个&#xff0c;最好是163邮箱。 本文将使用163邮箱讲解python发送邮件。 目录 开启邮箱smtp服务 打开SMTP设置 开启SMTP服务 记录授权密码和服务端配置 脚本发送邮件 引入所需的库 发送配置 邮箱登录发送邮件 查看收到邮件…

如何做标准化?| 京东云技术团队

在现代信息化的市场环境和社会中&#xff0c;标准化已经成为了各种行业的一个重要的标志。标准化不仅可以提升生产效率&#xff0c;减轻质量问题&#xff0c;还可以增加产品的可靠性和互通性。在这篇文章中&#xff0c;我们将探讨如何做标准化&#xff0c;为您提供详细的指导和…

一个小程序跳转到另一个小程序中如何实现

小程序 保证两个小程序是一样的主体才可以跳转。怎么知道是不是同样的主体呢&#xff1f; 小程序的后台管理-设置-基本设置-基本信息。查看主体信息。 跳转 <button clicktoOtherMini()>跳转到另一个小程序</button> function toOtherMini(){wx.navigateToMini…

chromedriver+Selenium+springboot+Java实现后端截图

chromedriver这种方法实现截图&#xff0c;依赖服务器端的谷歌浏览器软件&#xff0c;需要在服务器端下载谷歌浏览器。 Windows服务器说明 1.下载谷歌浏览器 2.根据第一步下载的谷歌浏览器版本&#xff0c;下载chromedriver&#xff0c;可以在这个页面找到和版本相近的版本去下…

开源进程/任务管理服务Meproc使用之HTTP API

本文讲述如何使用开源进程/任务管理服务Meproc的HTTP API管理整个服务。 Meproc所提供的全部 API 的 URL 都是相同的。 http://ip:port/proc例如 http://127.0.0.1:8606/proc在下面的小节中&#xff0c;我们使用curl命令向您展示 API 的方法、参数和请求正文。 启动任务 …

嵌入式-Stm32-江科大基于标准库的GPIO4个小实验

文章目录 一 、硬件介绍二 、实验&#xff1a;LED闪烁、LED流水灯、蜂鸣器提示2.1 需求1&#xff1a;面包板上的LED以1s为周期进行闪烁。亮0.5s,灭0.5s.....2.2 需求2: 8个LED实现流水灯 三、硬件介绍-按键开关、光敏电阻四、 实验 按键控制LED、光敏传感器控制蜂鸣器4.1 需求1…

CNAS中兴新支点——软件测试7条原则

软件测试报告 最需要注意的就是测试思考&#xff0c;而非测试执行。而对软件测试菜鸟来说&#xff0c;初入行&#xff0c;首先要知道软件测试的7条原则&#xff0c;了解这些可以让你事倍功半。 软件测试的7条原则 1&#xff09;测试的不可穷尽原则 是的&#xff01;任何产品…

【K8S 云原生】K8S的对外服务—ingress

目录 一、K8S的Service 1、Service的作用 2、Service类型&#xff1a; 二、ingress 1、ingress的组成&#xff1a; 2、ingress资源的定义项&#xff1a; 3、ingress暴露服务端的方式 3.1、DeploymentLoadBalancer模式&#xff1a; 1、工作流程图&#xff1a; 3.2、Dae…

Eclipse闪退 打开eclipse闪退 打开eclipse图标一闪而过 eclipse闪退 eclipse打不开

Eclipse闪退 打开eclipse闪退 打开eclipse图标一闪而过 eclipse闪退 eclipse打不开 问题描述切换为命令行启动 查看异常日志 问题描述 双击图标&#xff0c;窗口一闪而过&#xff0c;马上关闭了 切换为命令行启动 查看异常日志 进入Eclipse安装目录&#xff0c;运行终端启动…

Tektronix/泰克MSO5204B混合信号示波器

181/2461/8938产品概述&#xff1a; 带宽:2 GHz通道:4个模拟通道和16个数字通道采样速率:10 GS/s记录长度:50分钟FastAcq采集的最大波形捕获速率超过250&#xff0c;000 wfms/sFastFrame分段内存采集模式&#xff0c;每秒可采集多达290&#xff0c;000个分段和超过310&#xff…

Blender——将模型及其所有纹理与材质导入unity

前期准备 参考视频&#xff1a;7分钟教会你如何将Blender的模型材质导入unity_哔哩哔哩_bilibili 实验模型官网下载地址&#xff1a;Hoi An Ancient House Model free VR / AR / low-poly 3D model CSDN下载链接&#xff1a; 【免费】Blender三维模型-古代房屋模型&#xff…

逆向使用webpack打包的网站

webpack webpack 是 JavaScript 应用程序的模块打包器,可以把开发中的所有资源&#xff08;图片、js文件、css文件等&#xff09;都看成模块&#xff0c;通过loader&#xff08;加载器&#xff09;和 plugins &#xff08;插件&#xff09;对资源进行处理&#xff0c;打包成符…

【银行测试】银行项目,信贷/贷款业务测试+常问面试(二)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 银行测试-信贷&am…

1358. 素数环-深度优先搜索-DFS

代码&#xff1a; #include <bits/stdc.h> using namespace std; int n, a[19], c 0; bool f[19]; bool prime(int n){if(n 1)return false;for ( int i 2 ; i * i < n ; i )if(n % i 0)return false;return true; } void print(){c;cout << c << &q…

Kafka-消费者-KafkaConsumer分析-ConsumerNetworkClient

前面介绍过NetworkClient的实现&#xff0c;它依赖于KSelector、InFlightRequests、Metadata等组件&#xff0c;负责管理客户端与Kafka集群中各个Node节点之间的连接&#xff0c;通过KSelector法实现了发送请求的功能&#xff0c;并通过一系列handle*方法处理请求响应、超时请求…

el-table里面存在固定列获取video的ref的时候无法获取原始DOM

el-table里面存在固定列获取video的ref的时候无法获取原始DOM 问题复现 这是通过ref获取的dom实例&#xff0c;却变成了fixed固定出现了表格里面的video的实例 我现在的需求是修改里面的currentTime&#xff0c;但是获取的是固定列的video的ref&#xff0c;修改了&#xff0c…

Redis 笔记一

概览 1.Redis核心数据存储结构 2.Redis底层String编码int&embstr&raw 3.Redis底层压缩列表&跳表&哈希表 4.Redis底层Zset实现压缩列表和跳表如何选择 5.基于Redis实现微博&抢红包&12306核心业务 辅助学习&#xff1a;Redis 教程 | 菜鸟教程 1.Redis为什…