C++ 设计模式----“单一职责“模式

二、“单一职责”模式

在软件组件的设计中,如果责任划分的不清晰,使用继承得到的结果往往是随着需求的变化,子类急剧膨胀,同时充斥着重复代码,这时候的关键是划清责任。

 典型模式

• Decorator

• Bridge

【1】Decorator 装饰模式

**动机(Motivation)**💡:

 在某些情况下我们可能会“过度地使用继承来扩展对象的功能”由于继承为类型引入的静态特质使得这种扩展方式缺乏灵活性;并且随着子类的增多(扩展功能的增多),各种子类的组合(扩展功能的组合)会导致更多子类的膨胀

**问题思考(Consider)**🤔:

 如何使**“对象功能的扩展”能够根据需要来动态地实现?同时避免“扩展功能的增多”带来的子类膨胀问题**?从而使得任何“功能

扩展变化”所导致的影响将为最低?

业务需求:加密的操作是一样的,只是流的读取操作是不一样的。

C++执行代码:

decorator1.cpp(【编译时装配】会产生代码冗余)

过度地使用继承来扩展对象的功能”由于继承为类型引入的静态特质使得这种扩展方式缺乏灵活性;例如如下代码:

在这里插入图片描述

//业务操作
class Stream{
publicvirtual char Read(int number)=0;
    virtual void Seek(int position)=0;
    virtual void Write(char data)=0;
    
    virtual ~Stream(){}
};

//主体类
class FileStream: public Stream{
public:
    virtual char Read(int number){
        //读文件流
    }
    virtual void Seek(int position){
        //定位文件流
    }
    virtual void Write(char data){
        //写文件流
    }

};

class NetworkStream :public Stream{
public:
    virtual char Read(int number){
        //读网络流
    }
    virtual void Seek(int position){
        //定位网络流
    }
    virtual void Write(char data){
        //写网络流
    }
    
};

class MemoryStream :public Stream{
public:
    virtual char Read(int number){
        //读内存流
    }
    virtual void Seek(int position){
        //定位内存流
    }
    virtual void Write(char data){
        //写内存流
    }
    
};

//扩展操作
class CryptoFileStream :public FileStream{
public:
    virtual char Read(int number){
       
        //额外的加密操作...
        FileStream::Read(number);//读文件流
        
    }
    virtual void Seek(int position){
        //额外的加密操作...
        FileStream::Seek(position);//定位文件流
        //额外的加密操作...
    }
    virtual void Write(byte data){
        //额外的加密操作...
        FileStream::Write(data);//写文件流
        //额外的加密操作...
    }
};

class CryptoNetworkStream : :public NetworkStream{
public:
    virtual char Read(int number){
        
        //额外的加密操作...
        NetworkStream::Read(number);//读网络流
    }
    virtual void Seek(int position){
        //额外的加密操作...
        NetworkStream::Seek(position);//定位网络流
        //额外的加密操作...
    }
    virtual void Write(byte data){
        //额外的加密操作...
        NetworkStream::Write(data);//写网络流
        //额外的加密操作...
    }
};

class CryptoMemoryStream : public MemoryStream{
public:
    virtual char Read(int number){
        
        //额外的加密操作...
        MemoryStream::Read(number);//读内存流
    }
    virtual void Seek(int position){
        //额外的加密操作...
        MemoryStream::Seek(position);//定位内存流
        //额外的加密操作...
    }
    virtual void Write(byte data){
        //额外的加密操作...
        MemoryStream::Write(data);//写内存流
        //额外的加密操作...
    }
};

class BufferedFileStream : public FileStream{
    //...
};

class BufferedNetworkStream : public NetworkStream{
    //...
};

class BufferedMemoryStream : public MemoryStream{
    //...
}




class CryptoBufferedFileStream :public FileStream{
public:
    virtual char Read(int number){
        
        //额外的加密操作...
        //额外的缓冲操作...
        FileStream::Read(number);//读文件流
    }
    virtual void Seek(int position){
        //额外的加密操作...
        //额外的缓冲操作...
        FileStream::Seek(position);//定位文件流
        //额外的加密操作...
        //额外的缓冲操作...
    }
    virtual void Write(byte data){
        //额外的加密操作...
        //额外的缓冲操作...
        FileStream::Write(data);//写文件流
        //额外的加密操作...
        //额外的缓冲操作...
    }
};



void Process(){

    //编译时装配
    CryptoFileStream *fs1 = new CryptoFileStream();

    BufferedFileStream *fs2 = new BufferedFileStream();

    CryptoBufferedFileStream *fs3 =new CryptoBufferedFileStream();

}

在这里插入图片描述

类的总数:1+n+n*m!/2

【改进decorator1.cpp】用组合代替继承
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

【改进decorator1.cpp】三个子类变为一个子类(CryptoFileStream、CryptoNetworkStream、CryptoMemoryStream)->(CryptoStream)

在这里插入图片描述

【改进decorator1.cpp】缓冲流也是一样的改造(BufferedFileStream、BufferedNetworkStream、BufferedMemoryStream)->(BufferedStream)

在这里插入图片描述

decorator2.cpp(【运行时装配】)

//业务操作
class Stream{

public:
    virtual char Read(int number)=0;
    virtual void Seek(int position)=0;
    virtual void Write(char data)=0;
    
    virtual ~Stream(){}
};

//主体类
class FileStream: public Stream{
public:
    virtual char Read(int number){
        //读文件流
    }
    virtual void Seek(int position){
        //定位文件流
    }
    virtual void Write(char data){
        //写文件流
    }

};

class NetworkStream :public Stream{
public:
    virtual char Read(int number){
        //读网络流
    }
    virtual void Seek(int position){
        //定位网络流
    }
    virtual void Write(char data){
        //写网络流
    }
    
};

class MemoryStream :public Stream{
public:
    virtual char Read(int number){
        //读内存流
    }
    virtual void Seek(int position){
        //定位内存流
    }
    virtual void Write(char data){
        //写内存流
    }
    
};

//扩展操作
// 三个子类变为一个子类,用组合代替继承
class CryptoStream: public Stream {
    Stream* stream;//...
public:
    CryptoStream(Stream* stm):stream(stm){
    
    }
    virtual char Read(int number){
       
        //额外的加密操作...
        stream->Read(number);//读文件流
    }
    virtual void Seek(int position){
        //额外的加密操作...
        Stream::Seek(position);//定位文件流
        //额外的加密操作...
    }
    virtual void Write(char data){
        //额外的加密操作...
        Stream::Write(data);//写文件流
        //额外的加密操作...
    }
};


// 三个子类变为一个子类,用组合代替继承
class BufferedStream : public Stream{
    
    Stream* stream;//...
    
public:
    BufferedStream(Stream* stm):stream(stm){
        
    }
    virtual char Read(int number){
        // ...
    }
    virtual void Seek(int position){
        // ...
    }
    virtual void Write(char data){
        // ...
    }
};


void Process(){
    //运行时装配
    FileStream* s1=new FileStream();
    CryptoStream* s2=new CryptoStream(s1);
    
    BufferedStream* s3=new BufferedStream(s1);
    
    BufferedStream* s4=new BufferedStream(s2);
    
}

改造decorator2.cpp(由于两个子类有相同的成员Stream*,所以这个成员要往上提)

在这里插入图片描述

在这里插入图片描述

1、编写DecoratorStream中间类继承Stream(装饰类)

在这里插入图片描述

2、

在这里插入图片描述

3、

在这里插入图片描述

decorator3.cpp

//业务操作
class Stream{

public:
    virtual char Read(int number)=0;
    virtual void Seek(int position)=0;
    virtual void Write(char data)=0;
    
    virtual ~Stream(){}
};

//主体类
class FileStream: public Stream{
public:
    virtual char Read(int number){
        //读文件流
    }
    virtual void Seek(int position){
        //定位文件流
    }
    virtual void Write(char data){
        //写文件流
    }

};

class NetworkStream :public Stream{
public:
    virtual char Read(int number){
        //读网络流
    }
    virtual void Seek(int position){
        //定位网络流
    }
    virtual void Write(char data){
        //写网络流
    }
    
};

class MemoryStream :public Stream{
public:
    virtual char Read(int number){
        //读内存流
    }
    virtual void Seek(int position){
        //定位内存流
    }
    virtual void Write(char data){
        //写内存流
    }
    
};

//扩展操作
// 由于两个子类有相同的成员Stream*,所以这个成员要往上提
class DecoratorStream: public Stream{
protected:
    Stream* stream;//...
    
    DecoratorStream(Stream * stm):stream(stm){
    
    }
    
};

class CryptoStream: public DecoratorStream {
 
public:
    CryptoStream(Stream* stm):DecoratorStream(stm){
    
    }
    virtual char Read(int number){
       
        //额外的加密操作...
        stream->Read(number);//读文件流
    }
    virtual void Seek(int position){
        //额外的加密操作...
        Stream::Seek(position);//定位文件流
        //额外的加密操作...
    }
    virtual void Write(char data){
        //额外的加密操作...
        Stream::Write(data);//写文件流
        //额外的加密操作...
    }
};

class BufferedStream : public DecoratorStream{
    Stream* stream;//...
public:
    BufferedStream(Stream* stm):DecoratorStream(stm){
        
    }

    virtual char Read(int number){
        // ...
    }
    virtual void Seek(int position){
        // ...
    }
    virtual void Write(char data){
        // ...
    }
};

void Process(){
    //运行时装配
    FileStream* s1=new FileStream();
    
    CryptoStream* s2=new CryptoStream(s1);
    
    BufferedStream* s3=new BufferedStream(s1);
    
    BufferedStream* s4=new BufferedStream(s2);
}

在这里插入图片描述

类的总数:1+n+1+m

使**“对象功能的扩展”能够根据需要来动态**地实现:

在这里插入图片描述

网上代码(来自这个博主):https://github.com/chouxianyu/design-patterns-cpp

Decorator.cpp

#include <iostream>
class Component
{
public:
  virtual ~Component() {}
  
  virtual void operation() = 0;
  // ...
};

class ConcreteComponent : public Component
{
public:
  ~ConcreteComponent() {}
  
  void operation()
  {
    std::cout << "Concrete Component operation" << std::endl;
  }
  // ...
};

class Decorator : public Component
{
public:
  ~Decorator() {}
  
  Decorator( Component *c ) : component( c ) {}
  
  virtual void operation()
  {
    component->operation();
  }
  // ...

private:
  Component *component;
};

class ConcreteDecoratorA : public Decorator
{
public:
  ConcreteDecoratorA( Component *c ) : Decorator( c ) {}
  
  void operation()
  {
    Decorator::operation();
    std::cout << "Decorator A" << std::endl;
  }
  // ...
};

class ConcreteDecoratorB : public Decorator
{
public:
  ConcreteDecoratorB( Component *c ) : Decorator( c ) {}
  
  void operation()
  {
    Decorator::operation();
    std::cout << "Decorator B" << std::endl;
  }
  // ...
};


int main()
{
  //运行时装配
  ConcreteComponent  *cc = new ConcreteComponent();
  ConcreteDecoratorB *db = new ConcreteDecoratorB( cc );
  ConcreteDecoratorA *da = new ConcreteDecoratorA( db );
  
  Component *component = da;
  component->operation();
  
  delete da;
  delete db;
  delete cc;
  
  return 0;
}

模式定义

动态(组合)地给一个对象增加一些额外的职责。就增加功能而言,Decorator模式比生成子类(继承)更为灵活(消除重复代码 & 减少子类个数)。

——《设计模式》GoF

结构(Structure)

在这里插入图片描述

要点总结

 通过采用组合而非继承的手法, Decorator模式实现了在运行时 动态扩展对象功能的能力,而且可以根据需要扩展多个功能。避免 了使用继承带来的“灵活性差”和“多子类衍生问题” 。

 Decorator类在接口上表现为is-a Component的继承关系,即 Decorator类继承了Component类所具有的接口。但在实现上又 表现为has-a Component的组合关系,即Decorator类又使用了 另外一个Component类。

 Decorator模式的目的并非解决“多子类衍生的多继承”问题, Decorator模式应用的要点在于解决“主体类在多个方向上的扩展 功能”——是为“装饰”的含义。

【2】Bridge 桥模式

**动机(Motivation)**💡:

 由于某些类型的固有的实现逻辑,使得它们具有两个变化的维度乃至多个维度的变化

**问题思考(Consider)**🤔:

 如何**应对这种“多维度的变化”?**如何利用面向对象技术来使得类型可以轻松地沿着两个乃至多个方向变化,而不引入额外的复杂度?

业务需求:

①登录(Login),发送消息(SendMessage),发送图片(SendPicture),

②播放声音(PlaySound),画图(DrawShape),写文字(WriteText),连接(Connect)

等需求。假设我们要支持PC的平台设计,也要支持mobile的平台设计,需要在不同的平台上推荐出不同的功能。比如说我们要推出精简版(Lite),还有一个完美版(Perfect)

bridge1.cpp

#include<string>
using std::string;
class Image{

};

class Messager{
public:
    virtual void Login(string username, string password)=0;
    virtual void SendMessage(string message)=0;
    virtual void SendPicture(Image image)=0;

    virtual void PlaySound()=0;
    virtual void DrawShape()=0;
    virtual void WriteText()=0;
    virtual void Connect()=0;
    
    virtual ~Messager(){}
};


//平台实现 n
class PCMessagerBase : public Messager{
public:
    
    virtual void PlaySound(){
        //**********
    }
    virtual void DrawShape(){
        //**********
    }
    virtual void WriteText(){
        //**********
    }
    virtual void Connect(){
        //**********
    }
};

class MobileMessagerBase : public Messager{
public:
    
    virtual void PlaySound(){
        //==========
    }
    virtual void DrawShape(){
        //==========
    }
    virtual void WriteText(){
        //==========
    }
    virtual void Connect(){
        //==========
    }
};



//业务抽象 m
//类的数目:1+n+m*n
class PCMessagerLite : public PCMessagerBase {
public:
    
    virtual void Login(string username, string password){
        
        PCMessagerBase::Connect();
        //........
    }
    virtual void SendMessage(string message){
        
        PCMessagerBase::WriteText();
        //........
    }
    virtual void SendPicture(Image image){
        
        PCMessagerBase::DrawShape();
        //........
    }
};



class PCMessagerPerfect : public PCMessagerBase {
public:
    
    virtual void Login(string username, string password){
        
        PCMessagerBase::PlaySound();
        //********
        PCMessagerBase::Connect();
        //........
    }
    virtual void SendMessage(string message){
        
        PCMessagerBase::PlaySound();
        //********
        PCMessagerBase::WriteText();
        //........
    }
    virtual void SendPicture(Image image){
        
        PCMessagerBase::PlaySound();
        //********
        PCMessagerBase::DrawShape();
        //........
    }
};


class MobileMessagerLite : public MobileMessagerBase {
public:
    
    virtual void Login(string username, string password){
        
        MobileMessagerBase::Connect();
        //........
    }
    virtual void SendMessage(string message){
        
        MobileMessagerBase::WriteText();
        //........
    }
    virtual void SendPicture(Image image){
        
        MobileMessagerBase::DrawShape();
        //........
    }
};


class MobileMessagerPerfect : public MobileMessagerBase {
public:
    
    virtual void Login(string username, string password){
        
        MobileMessagerBase::PlaySound();
        //********
        MobileMessagerBase::Connect();
        //........
    }
    virtual void SendMessage(string message){
        
        MobileMessagerBase::PlaySound();
        //********
        MobileMessagerBase::WriteText();
        //........
    }
    virtual void SendPicture(Image image){
        
        MobileMessagerBase::PlaySound();
        //********
        MobileMessagerBase::DrawShape();
        //........
    }
};


void Process(){
    //编译时装配
    Messager *m = new MobileMessagerPerfect();
}
  • 平台实现 n
  • 业务抽象 m
  • 类的数目:1+n+m*n

bridge2.cpp

第一步:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
第二步:

我们发现PCMessagerLite和MobileMessagerLite的业务一样,可以整合成MessagerLite

PCMessagerPerfect和MobileMessagerPerfect的业务一样,可以整合成MessagerPerfect

在这里插入图片描述
在这里插入图片描述

第三步:

存在只override一些虚函数,有一些没有override.如果直接让PCMessagerBase和MobileMessagerBase去继承Messager是不合适的。所以我们需要把Messager做一个拆分

在这里插入图片描述

修改平台实现:

在这里插入图片描述

修改Lite版本和Perfect版本:

在这里插入图片描述

发现Messager的子类MessagerLite和MessagerPerfect都有MessagerImp* messagerImp;字段,可以把它往上提

在这里插入图片描述
在这里插入图片描述

#include<string>
using std::string;
class Image{

};

class Messager{
protected:
     MessagerImp* messagerImp;//...
public:
    virtual void Login(string username, string password)=0;
    virtual void SendMessage(string message)=0;
    virtual void SendPicture(Image image)=0;
    
    Messager(MessagerImp* p=nullptr){
        messagerImp=p;
    }
    virtual ~Messager(){}
};

// 不同的变化方向(业务和平台),所以分为两个类
class MessagerImp{
public:
    virtual void PlaySound()=0;
    virtual void DrawShape()=0;
    virtual void WriteText()=0;
    virtual void Connect()=0;
    
    MessagerImp();
};


//平台实现 n
class PCMessagerImp : public MessagerImp{
public:
    
    virtual void PlaySound(){
        //**********
    }
    virtual void DrawShape(){
        //**********
    }
    virtual void WriteText(){
        //**********
    }
    virtual void Connect(){
        //**********
    }
};

class MobileMessagerImp : public MessagerImp{
public:
    
    virtual void PlaySound(){
        //==========
    }
    virtual void DrawShape(){
        //==========
    }
    virtual void WriteText(){
        //==========
    }
    virtual void Connect(){
        //==========
    }
};


//业务抽象 m
//类的数目:1+n+m
class MessagerLite :public Messager {
public:
    virtual void Login(string username, string password){
        
        messagerImp->Connect();
        //........
    }
    virtual void SendMessage(string message){
        
        messagerImp->WriteText();
        //........
    }
    virtual void SendPicture(Image image){
        
        messagerImp->DrawShape();
        //........
    }

    MessagerLite(MessagerImp* p=nullptr){
        messagerImp=p;
    }
};

class MessagerPerfect :public Messager {
public:
    
    virtual void Login(string username, string password){
        
        messagerImp->PlaySound();
        //********
        messagerImp->Connect();
        //........
    }
    virtual void SendMessage(string message){
        
        messagerImp->PlaySound();
        //********
        messagerImp->WriteText();
        //........
    }
    virtual void SendPicture(Image image){
        
        messagerImp->PlaySound();
        //********
        messagerImp->DrawShape();
        //........
    }
    
    MessagerPerfect(MessagerImp* p = nullptr) {
        messagerImp = p;
    }
};

void Process(){
    //运行时装配
    MessagerImp* mImp=new PCMessagerImp();
    Messager *m = new MessagerLite(mImp);
}



网上代码(来子这个博主):https://github.com/chouxianyu/design-patterns-cpp

Bridge.cpp

#include <iostream>
class Implementor
{
public:
  virtual ~Implementor() {}
  
  virtual void action() = 0;
  // ...
};

class ConcreteImplementorA : public Implementor
{
public:
  ~ConcreteImplementorA() {}
  
  void action()
  {
    std::cout << "Concrete Implementor A" << std::endl;
  }
  // ...
};

class ConcreteImplementorB : public Implementor
{
public:
  ~ConcreteImplementorB() {}
  
  void action()
  {
    std::cout << "Concrete Implementor B" << std::endl;
  }
  // ...
};

class Abstraction
{
public:
  virtual ~Abstraction() {}
  
  virtual void operation() = 0;
  // ...
};

class RefinedAbstraction : public Abstraction
{
public:
  ~RefinedAbstraction() {}
  
  RefinedAbstraction(Implementor *impl) : implementor(impl) {}
  
  void operation()
  {
    implementor->action();
  }
  // ...

private:
  Implementor *implementor;
};


int main()
{
  Implementor *ia = new ConcreteImplementorA;
  Implementor *ib = new ConcreteImplementorB;
  
  Abstraction *abstract1 = new RefinedAbstraction(ia);
  abstract1->operation();
  
  Abstraction *abstract2 = new RefinedAbstraction(ib);
  abstract2->operation();
  
  delete abstract1;
  delete abstract2;
  
  delete ia;
  delete ib;
  
  return 0;
}

在这里插入图片描述

模式定义

将抽象部分(业务功能)与实现部分(平台实现)分离,使它们都可以独立地变化。

——《设计模式》GoF

结构(Structure)

在这里插入图片描述

要点总结

 Bridge模式使用“对象间的组合关系”解耦了抽象和实现之间固 有的绑定关系,使得抽象和实现可以沿着各自的维度来变化。所谓抽象和实现沿着各自纬度的变化,即“子类化”它们。

 Bridge模式有时候类似于多继承方案,但是多继承方案往往违背单一职责原则(即一个类只有一个变化的原因),复用性比较差。Bridge模式是比多继承方案更好的解决方法。

 Bridge模式的应用一般在“两个非常强的变化维度”,有时一个类也有多于两个的变化维度,这时可以使用Bridge的扩展模式。

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

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

相关文章

基于Jeecg-boot的flowable流程支持拒绝同意流程操作

更多功能看演示系统 gitee源代码地址 后端代码&#xff1a; https://gitee.com/nbacheng/nbcio-boot 前端代码&#xff1a;https://gitee.com/nbacheng/nbcio-vue.git 在线演示&#xff08;包括H5&#xff09; &#xff1a; http://122.227.135.243:9888 因为看很多朋友需要…

easyui01(基本布局)

一.概述 1.What&#xff1f; jQuery EasyUI是一组基于jQuery的UI插件集合体&#xff0c;能帮助web开发者更轻松的打造出功能丰富并且美观的UI界面 2.Why&#xff1f; ①.使用easyui 不需要写很多代码&#xff0c;只需要编写一些简单 HTML 标记&#xff0c;就可以定义用户界…

使用docker部署ELK实战

目录 什么是ELKELK简介ElasticsearchLogstashKibana docker安装ELK安装es安装 Kibana安装logstash 什么是ELK ELK是一个开源的数据分析平台&#xff0c;由三个开源项目Elasticsearch、Logstash和Kibana组成&#xff0c;因此被称为ELK Stack。ELK Stack主要用于处理和分析大量的…

pytorch实现图像分类器

pytorch实现图像分类器 一、定义LeNet网络模型1&#xff0c;卷积 Conv2d2&#xff0c;池化 MaxPool2d3&#xff0c;Tensor的展平&#xff1a;view()4&#xff0c;全连接 Linear5&#xff0c;代码&#xff1a;定义 LeNet 网络模型 二、训练并保存网络参数1&#xff0c;数据预处理…

Exception in thread “main“ java.lang.UnsupportedClassVersionError 50报错处理

之间正常走jenkinsdocker自动化部署的项目&#xff0c;今天改了一个文件&#xff0c;点了一下&#xff0c;竟然没有部署上去&#xff0c;提示如上&#xff0c;如下 Exception in thread "main" java.lang.UnsupportedClassVersionError: com/coocaa/tsp/sys/user/Use…

采用UWB定位技术开发的室内定位系统源码

UWB精准定位系统源码 UWB是什么&#xff1f; UWB(Ultra Wideband)超宽带技术是一种全新的、与传统通信技术有极大差异的通信新技术。它不需要使用传统通信体制中的载波&#xff0c;而是通过发送和接收具有纳秒或纳秒级以下的极窄脉冲来传输数据&#xff0c;实现精准定位。 技术…

机器鸟实现摆动尾巴功能

1. 功能说明 本文示例将实现R329样机机器鸟摆动尾巴的功能。 2. 电子硬件 在这个示例中&#xff0c;我们采用了以下硬件&#xff0c;请大家参考&#xff1a; 主控板 Basra主控板&#xff08;兼容Arduino Uno&#xff09;‍ 扩展板 Bigfish2.1扩展板‍ 电池7.4V锂电池 电路连接…

【头歌-Python】9.1 X射线衍射曲线绘制(project)第1~2关

第1关&#xff1a;X 射线衍射曲线 任务描述 本关任务&#xff1a;读文件中的数据绘制线图形。 相关知识 为了完成本关任务&#xff0c;你需要掌握&#xff1a; 1.python 读取文件 2.使用 matplotlib 绘制图形 python 读取文件 python读取文件可以用以下函数实现&#xff1…

chatgpt赋能python:Python收费介绍

Python收费介绍 什么是Python? Python是一种高级的、解释性、面向对象、纯粹的动态语言&#xff0c;多用于快速应用程序开发、脚本编写、系统管理任务等。它有一个简单直观优美的语法&#xff0c;非常容易学习。 Python的收费形式 Python语言本身是免费的&#xff0c;任何…

如何使用Jmeter进行http接口测试?

目录 前言&#xff1a; 一、开发接口测试案例的整体方案&#xff1a; 二、接口自动化适用场景&#xff1a; 三、接口测试环境准备 四、创建工程&#xff1a; 总结&#xff1a; 前言&#xff1a; 本文主要针对http接口进行测试&#xff0c;使用Jmeter工具实现。 Jmter工具设…

1.2 Scala变量与数据类型

一、变量声明 (一&#xff09;、利用val声明变量 案例演示 &#xff08;二&#xff09;利用var声明变量 案例演示 &#xff08;三&#xff09;换行输入语句&#xff08;续行&#xff09; &#xff08;四&#xff09;同时声明多个变量 Scala还可以将多个变量放在一起…

射频电路layout总结

射频电路板设计由于在理论上还有很多不确定性&#xff0c;因此常被形容为一种“黑色艺术”&#xff0c;但这个观点只有部分正确&#xff0c;RF电路板设计也有许多可以遵循的准则和不应该被忽视的法则。在实际设计时&#xff0c;真正实用的技巧是当这些准则和法则因各种设计约束…

RTU遥测终端机的应用场景有哪些?

遥测终端机又称智能RTU遥测终端机&#xff0c;是一种用于采集、传输和处理遥测数据的设备。在现代科技的发展中&#xff0c;遥测终端机扮演着重要的角色。它是一种能够实现远程监测和控制的关键设备&#xff0c;广泛应用于各个领域&#xff0c;包括水文水利、环境监测、工业自动…

创新案例|专注在线 协作平台 设计产品中国首家PLG独角兽企业蓝湖如何实现98%的头部企业渗透率

蓝湖起步于2015年&#xff0c;是一款服务于产品经理、设计师、工程师的产品设计研发在线协作工具&#xff0c; 2021年10月&#xff0c;蓝湖宣布完成C轮融资&#xff0c;融资额高达10亿人民币&#xff0c;称为中国2B市场中首家采用PLG发展的独角兽企业&#xff0c;并实现了从100…

Web 自动化测试Selenium 之PO 模型

目录 1. po 模型介绍 2. PageObject 设计模式 3. PO 的核心要素 4. 非PO 实现 5. PO 实现 6. 总结 7. PO 模式的特点 总结&#xff1a; 1. po 模型介绍 在自动化中&#xff0c;Selenium 自动化测试中有一个名字经常被提及 PageObject (思想与面向对象的特征相同)&#x…

MySQL数据库高级操作

目录 MySQL中6种常见的约束克隆表清空表的数据记录临时表创建外键约束&#xff0c;保证数据的完整性和一致性。 MySQL中6种常见的约束 主键约束&#xff08;primary key&#xff09;外键约束&#xff08;foreign key&#xff09;非空约束&#xff08;not null&#xff09;唯一…

安卓端Google隐私沙盒归因报告聚焦

自2022年2月Google首次提出将推出隐私沙盒至今已一年有余。现在&#xff0c;安卓端的隐私沙盒Beta测试已针对特定Android13设备正式开始。作为早期测试者&#xff0c;Adjust很高兴与 Google一同迈出增强用户隐私的第一步&#xff0c;并在接下来的旅程中继续携手同行。为帮助移动…

opencv初学记录

准备工作&#xff1a; 1.找一张图片 2.准备python运行环境&#xff0c;并导入库&#xff0c;pip install opencv-python 读取文件&#xff0c;并打印维度 import cv2 #为什么是cv2不是cv呢&#xff0c;这个2指的是c的api&#xff0c;是为了兼容老板&#xff0c;cv指的就是c&am…

今天面了一个来字节要求月薪23K,明显感觉他背了很多面试题...

最近有朋友去字节面试&#xff0c;面试前后进行了20天左右&#xff0c;包含4轮电话面试、1轮笔试、1轮主管视频面试、1轮hr视频面试。 据他所说&#xff0c;80%的人都会栽在第一轮面试&#xff0c;要不是他面试前做足准备&#xff0c;估计都坚持不完后面几轮面试。 其实&…

ChatGPT/InstructGPT详解

前言 GPT系列是OpenAI的一系列预训练文章&#xff0c;GPT的全称是Generative Pre-Trained Transformer&#xff0c;顾名思义&#xff0c;GPT的目的就是通过Transformer为基础模型&#xff0c;使用预训练技术得到通用的文本模型。目前已经公布论文的有文本预训练GPT-1&#xff…