精华内容
下载资源
问答
  • 常见的接口调用方式三种(设计接口的时候要考虑选用哪种接口) 1、http接口:http是一种网络传输协议,基于TCP。(等价于:http+json) 现在浏览器客户端与服务器端通信基本都是采用http协议。 SpringCloud框架,...

    常见的接口调用方式有三种(设计接口的时候要考虑选用哪种接口)

    1、http接口:http是一种网络传输协议,基于TCP。(等价于:http+json)
    现在浏览器客户端与服务器端通信基本都是采用http协议。
    SpringCloud框架,各个服务间也是通过http方式来实现的。
    http api接口是走http协议,通过路径来区分调用的方法,请求报文都是key-value形式的,返回报文一般都是json串。
    http有几个特点:
    (1)规定了数据格式
    (2)对服务没有任何技术限定,自由灵活,更符合为服务理念。
    (3)现在热门的REST风格 / RESTful 风格,就可通过Http协议来实现。

    请求方式:post/get/put/delete 等。
    传输的数据格式(一般而言):JSON

    2、rpc接口:远程过程调用(类似的还有RMI),基于TCP。
    自定义数据式,基于原生TCP通信,速度快,效率高。
    现在热门的dubbo框架,就是RPC的典型代表。
    rpc有几个特点:
    (1)数据的格式可以自定义(一般是XML)。
    (2)速度快,效率高。
    (3)现在比较热门的dubbo就是RPC的典型代表。

    传输的数据格式:XML

    3、webservice接口:Webservice是系统对外的接口。(等价于:http+xml)
    webService接口是走soap协议通过http传输,请求报文和返回报文都是xml格式的。
    webService接口提供的服务是基于web容器的,底层使用http协议,类似一个远程的服务提供者,比如天气预报服务,对各地客户端提供天气预报,是一种请求应答的机制,是跨系统跨平台的。就是通过一个servlet,提供服务出去。

    首先客户端从服务器的到WebService的WSDL,同时在客户端声称一个代理类(Proxy Class) 这个代理类负责与WebService服务器进行Request 和Response 当一个数据(XML格式的)被封装成SOAP格式的数据流发送到服务器端的时候,就会生成一个进程对象并且把接收到这个Request的SOAP包进行解 析,然后对事物进行处理,处理结束以后再对这个计算结果进行SOAP包装,然后把这个包作为一个Response发送给客户端的代理类(Proxy Class),同样地,这个代理类也对这个SOAP包进行解析处理,继而进行后续操作。这就是WebService的一个运行过程。

    webservice大体上分为5个层次:

    1. Http传输信道
    2. XML的数据格式
    3. SOAP封装格式
    4. WSDL的描述方式
    5. UDDI UDDI是一种目录服务,企业可以使用它对Webservices进行注册和搜索

    应用协议是SOAP(简单对象访问协议)
    传输的数据格式:XML

    4、总结1(接口的选择)
    现在很多第三方接口,都改成了基于http,直接传递json数据的方式来代替webservice。
    但是webservice接口能传输数据量比较大的数据,而且可以跟语言无关,也可以跟操作系统无关。
    在某些业务复杂,稳定性和正确性要求高的领域(如ERP、电商、支付),WebService还有是用武之地的。

    5、总结2(传输数据格式的选择)
    为什么JSON比XML流行
    还是易用性,JSON的可读性比XML强,解析规则也简单许多。
    XML 解析的时候规则太多了,动不动就非法字符,动不动就抛异常。这对追求高开发速度和低开发门槛的企业来说,是个致命伤。

    JSON的缺点是数据类型支持较少,且不精确。比方说:

    {"price":12580}
    

    在json里,你无法知道这个价格是int, float还是double。

    所以,如上面第二条所述,在一些业务要求较高的领域,还是XML更合适。

    最后说一下性能, JSON 的性能高于XML,除此之外,基于 XML 和 HTTP 的 WebService , 基于 JSON 的RESTful API ,并没有性能差异。

    6、学习链接
    链接1:点我
    链接2:点我
    链接3:点我

    展开全文
  • C++接口工程实践:有哪些实现方法?

    千次阅读 多人点赞 2020-09-03 13:47:28
    那么C++有哪些实现接口的方法呢,不同的方法又适用于哪些场景呢?本文分享在C++接口工程实践上的一些探索心得。一 接口的分类接口按照功能划分可以分为调用接口与回调接口:调用接口一段代码、一个模块、一个程序库...
    简介:程序开发的时候经常会使用到接口。众所周知,C++语言层面并没有接口的概念,但并不意味着C++不能实现接口的功能。相反,正是由于C++语言没有提供标准的接口,导致实际实现接口的方法多种多样。那么C++有哪些实现接口的方法呢,不同的方法又适用于哪些场景呢?本文分享在C++接口工程实践上的一些探索心得。

    image.png

    一 接口的分类

    接口按照功能划分可以分为调用接口与回调接口:

    调用接口

    一段代码、一个模块、一个程序库、一个服务等(后面都称为系统),对外提供什么功能,以接口的形式暴露出来,用户只需要关心接口怎么调用,不用关心具体的实现,即可使用这些功能。这类被用户调用的接口,称为调用接口。

    调用接口的主要作用是解耦,对用户隐藏实现,用户只需要关心接口的形式,不用关心具体的实现,只要保持接口的兼容性,实现上的修改或者升级对用户无感知。解耦之后也方便多人合作开发,设计好接口之后,各模块只通过接口进行交互,各自完成各自的模块即可。

    回调接口

    系统定义接口,由用户实现,注册到系统中,系统有异步事件需要通知用户时,回调用户注册的接口实现。系统定义接口的形式,但无需关心接口的实现,而是接受用户的注册,并在适当的时机调用。这类由系统定义,用户实现,被系统调用的接口,称为回调接口。

    回调接口的主要作用是异步通知,系统定义好通知的接口,并在适当的时机发出通知,用户接收通知,并执行相应的动作,用户动作执行完后控制权交还给系统,用户动作可以给系统返回一些数据,以决定系统后续的行为。

    二 调用接口

    我们以一个Network接口为例,说明C++中的调用接口的定义及实现,示例如下:

    class Network
    {
    public:
        bool send(const char* host, 
                  uint16_t port, 
                  const std::string& message);
    }
    

    Network接口现在只需要一个send接口,可以向指定地址发送消息。下面我们用不同的方法来定义Network接口。

    虚函数

    虚函数是定义C++接口最直接的方式,使用虚函数定义Network接口类如下:

    class Network
    {
    public:
        virtual bool send(const char* host, 
                          uint16_t port, 
                          const std::string& message) = 0;
    
        static Network* New();
    
        static void Delete(Network* network);
    }

    将send定义为纯虚函数,让子类去实现,子类不对外暴露,提供静态方法New来创建子类对象,并以父类Network的指针形式返回。接口的设计一般遵循对象在哪创建就在哪销毁的原则,因此提供静态的Delete方法来销毁对象。因为对象的销毁封装在接口内部,因此Network接口类可以不用虚析构函数。

    使用虚函数定义接口简单直接,但是有很多弊端:

    • 虚函数开销:虚函数调用需要使用虚函数表指针间接调用,运行时才能决定调用哪个函数,无法在编译链接期间内联优化。实际上调用接口在编译期间就能确定调用哪个函数,无需虚函数的动态特性。
    • 二进制兼容:由于虚函数是按照索引查询虚函数表来调用,增加虚函数会造成索引变化,新接口不能在二进制层面兼容老接口,而且由于用户可能继承了Network接口类,在末尾增加虚函数也有风险,因此虚函数接口一经发布,难以修改。

    指向实现的指针

    指向实现的指针是C++比较推荐的定义接口的方式,使用指向实现的指针定义Network接口类如下:

    class NetworkImpl;
    class Network
    {
    public:
        bool send(const char* host, 
                  uint16_t port, 
                  const std::string& message);
    
        Network();
    
        ~Network();
    
    private:
        NetworkImpl* impl;
    }

    Network的实现通过impl指针转发给NetworkImpl,NetworkImpl使用前置声明,实现对用户隐藏。使用指向实现的指针的方式定义接口,接口类对象的创建和销毁可以由用户负责,因此用户可以选择将Network类的对象创建在栈上,生命周期自动管理。

    使用指向实现的指针定义接口具有良好的通用性,用户能够直接创建和销毁接口对象,并且增加新的接口函数不影响二进制兼容性,便于系统的演进。

    指向实现的指针增加了一层调用,尽管对性能的影响几乎可以忽略不计,但不太符合C++的零开销原则,那么问题来了,C++能否实现零开销的接口呢?当然可以,即下面要介绍的隐藏的子类。

    隐藏的子类

    隐藏的子类可以实现零开销的接口,思想非常简单。调用接口要实现的目标是解耦,主要就是隐藏实现,也即隐藏接口类的成员变量,如果能将接口类的成员变量都移到另一个隐藏的实现类中,接口类就不需要任何成员变量,也就实现了隐藏实现的目的。隐藏的子类就是这个隐藏的实现类,使用隐藏的子类定义Network接口类如下:

    class Network
    {
    public:
        bool send(const char* host, 
                  uint16_t port, 
                  const std::string& message);
    
        static Network* New();
    
        static void Delete(Network* network);
    
    protected:
        Network();
    
        ~Network();
    }

    Network接口类只有成员函数(非虚函数),没有成员变量,并且构造函数和析构函数都申明为protected。提供静态方法New创建对象,静态方法Delete销毁对象。New方法的实现中创建隐藏的子类NetworkImpl的对象,并以父类Network指针的形式返回。NetworkImpl类中存放Network类的成员变量,并将Network类声明为friend:

    class NetworkImpl : public Network
    {
        friend class Network;
    
    private:
        //Network类的成员变量
    }

    Network的实现中,创建隐藏的子类NetworkImpl的对象,并以父类Network指针的形式返回,通过将this强制转换为NetworkImpl的指针,访问成员变量:

    bool Network::send(const char* host, 
                       uint16_t port, 
                       const std::string& message)
    {
        NetworkImpl* impl = (NetworkImpl*)this;
        //通过impl访问成员变量,实现Network
    }
    
    static Network* New()
    {
        return new NetworkImpl();
    }
    
    static void Delete(Network* network)
    {
        delete (NetworkImpl*)network;
    }
    

    使用隐藏的子类定义接口同样具有良好的通用性和二进制兼容性,同时没有增加任何开销,符合C++的零开销原则。

    三 回调接口

    同样以Network接口为例,说明C++中的回调接口的定义及实现,示例如下:

    class Network
    {
    public:
        class Listener
        {
        public:
            void onReceive(const std::string& message);
        }
    
        bool send(const char* host, 
                  uint16_t port, 
                  const std::string& message);
    
        void registerListener(Listener* listener);
    }
    

    现在Network需要增加接收消息的功能,增加Listener接口类,由用户实现,并注册其对象到Network中后,当有消息到达时,回调Listener的onReceive方法。

    虚函数

    使用虚函数定义Network接口类如下:

    class Network
    {
    public:
        class Listener
        {
        public:
            virtual void onReceive(const std::string& message) = 0;
        }
    
        bool send(const char* host, 
                  uint16_t port, 
                  const std::string& message);
    
        void registerListener(Listener* listener);
    }
    

    将onReceive定义为纯虚函数,由用户继承实现,由于多态的存在,回调的是实现类的方法。

    使用虚函数定义回调接口简单直接,但同样存在和调用接口中使用虚函数同样的弊端:虚函数调用开销,二进制兼容性差。

    函数指针

    函数指针是C语言的方式,使用函数指针定义Network接口类如下:

    class Network
    {
    public:
        typedef void (*OnReceive)(const std::string& message, void* arg);
    
        bool send(const char* host, 
                  uint16_t port, 
                  const std::string& message);
    
        void registerListener(OnReceive listener, void* arg);
    }
    

    使用函数指针定义C++回调接口简单高效,但只适用于回调接口中只有一个回调函数的情形,如果Listener接口类中要增加onConnect,onDisconnect等回调方法,单个函数指针无法实现。另外函数指针不太符合面向对象的思想,可以换成下面要介绍的std::function。

    std::function

    std::function提供对可调用对象的抽象,可封装签名相符的任意的可调用对象。使用std::function定义Network接口类如下:

    class Network
    {
    public:
        typedef std::function<void(const std::string& message)> OnReceive;
    
        bool send(const char* host, 
                  uint16_t port, 
                  const std::string& message);
    
        void registerListener(const OnReceive& listener);
    }
    

    std::function可以很好的取代函数指针,配合std::bind,具有很好的通用性,因而被广受推崇。但std::function同样只适用于回调接口中只有一个回调方法的情形。另外,std::function比较重量级,使用上面的便利却会带来了性能上的损失,有人做过性能对比测试,std::function大概比普通函数慢6倍以上,比虚函数还慢。

    类成员函数指针

    类成员函数指针的使用比较灵活,使用类成员函数指针定义Network接口类如下:

    class Network
    {
    public:
        class Listener
        {
        public:
            void onReceive(const std::string& message);
        }
    
        typedef void (Listener::* OnReceive)(const std::string& message);
    
        bool send(const char* host, 
                  uint16_t port, 
                  const std::string& message);
    
        void registerListener(Listener* listener, OnReceive method);
    
        template<typename Class>
        void registerListener(Class* listener, 
             void (Class::* method)(const std::string& message)
        {
            registerListener((Listener*)listener, (OnReceive)method);
        }
    }
    

    因为类成员函数指针必须和类对象一起使用,所以Network的注册接口需要同时提供对象指针和成员函数指针,registerListener模板函数可注册任意类的对象和相应符合签名的方法,无需继承Listener,与接口类解耦。

    使用类成员函数指针定义C++回调接口灵活高效,可实现与接口类解耦,并且不破坏面向对象特性,可很好的取代传统的函数指针的方式。

    类成员函数指针同样只适用于回调接口中只有一个回调方法的情形,如果有多个回调方法,需要针对每一个回调方法提供一个类成员函数指针。那么有没有方法既能实现与接口类解耦,又能适用于多个回调方法的场景呢?参考下面介绍的非侵入式接口。

    四 非侵入式接口

    Rust中的Trait功能非常强大,可以在类外面,不修改类代码,实现一个Trait,那么C++能否实现Rust的Trait的功能呢?还是以Network接口为例,假设现在Network发送需要考虑序列化,重新设计Network接口,示例如下:

    定义Serializable接口:

    class Serializable
    {
    public:
        virtual void serialize(std::string& buffer) const = 0;
    };
    

    Network接口示例:

    class Network
    {
    public:
        bool send(const char* host, 
                  uint16_t port, 
                  const Serializable& s);
    }
    

    Serializable接口相当于Rust中的Trait,现在一切实现了Serializable接口的类的对象均可以通过Network接口发送。那么问题来了,能否在不修改类的定义的同时,实现Serializable接口呢?假如我们要通过Network发送int类型的数据,能否做到呢?答案是肯定的:

    1. class IntSerializable : public Serializable
    {
    public:
        IntSerializable(const int* i) :
            intThis(i)
        {
    
        }
    
        IntSerializable(const int& i) :
            intThis(&i)
        {
    
        }
    
        virtual void serialize(std::string& buffer) const override 
        {
            buffer += std::to_string(*intThis);
        }
    
    private:
        const int* const intThis;
    };
    

    有了实现了Serializable接口的IntSerializable,就可以实现通过Network发送int类型的数据了:

    Network* network = Network::New();
    int i = 1;
    network->send(ip, port, IntSerializable(i));
    

    Rust编译器通过impl关键字记录了每个类实现了哪些Trait,因此在赋值时编译器可以自动实现将对象转换为相应的Trait类型,但C++编译器并没有记录这些转换信息,需要手动转换类型。

    非侵入式接口让类和接口区分开来,类中的数据只有成员变量,不包含虚函数表指针,类不会因为实现了N个接口而引入N个虚函数表指针;而接口中只有虚函数表指针,不包含数据成员,类和接口之间通过实现类进行类型转换,实现类充当了类与接口之间的桥梁。类只有在充当接口用的时候才会引入虚函数表指针,不充当接口用的时候没有虚函数表指针,更符合C++的零开销原则。

    原文链接:https://developer.aliyun.com/article/771482?

    版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。
    展开全文
  • 我们的接口服务之前是web形式的,是通过servlet形式访问,会传一下参数。现在为了安全考虑要修改成https的形式,我们准备申请一个数字证书。将网站改成https//:的访问形式。 请教大神们几个问题: 1、这么修改的话...
  • 那么C++有哪些实现接口的方法呢,不同的方法又适用于哪些场景呢?本文分享在C++接口工程实践上的一些探索心得。 一、 接口的分类 接口按照功能划分可以分为调用接口与回调接口: 调用接口 一段代码、一个模块...

    程序开发的时候经常会使用到接口。众所周知,C++语言层面并没有接口的概念,但并不意味着C++不能实现接口的功能。相反,正是由于C++语言没有提供标准的接口,导致实际实现接口的方法多种多样。那么C++有哪些实现接口的方法呢,不同的方法又适用于哪些场景呢?本文分享在C++接口工程实践上的一些探索心得。

    一、 接口的分类

    接口按照功能划分可以分为调用接口与回调接口:

    调用接口

    一段代码、一个模块、一个程序库、一个服务等(后面都称为系统),对外提供什么功能,以接口的形式暴露出来,用户只需要关心接口怎么调用,不用关心具体的实现,即可使用这些功能。这类被用户调用的接口,称为调用接口。

    调用接口的主要作用是解耦,对用户隐藏实现,用户只需要关心接口的形式,不用关心具体的实现,只要保持接口的兼容性,实现上的修改或者升级对用户无感知。解耦之后也方便多人合作开发,设计好接口之后,各模块只通过接口进行交互,各自完成各自的模块即可。

    回调接口

    系统定义接口,由用户实现,注册到系统中,系统有异步事件需要通知用户时,回调用户注册的接口实现。系统定义接口的形式,但无需关心接口的实现,而是接受用户的注册,并在适当的时机调用。这类由系统定义,用户实现,被系统调用的接口,称为回调接口。

    回调接口的主要作用是异步通知,系统定义好通知的接口,并在适当的时机发出通知,用户接收通知,并执行相应的动作,用户动作执行完后控制权交还给系统,用户动作可以给系统返回一些数据,以决定系统后续的行为。

    二 、调用接口

    我们以一个Network接口为例,说明C++中的调用接口的定义及实现,示例如下:

    class Network

    {

    public:

        bool send(const char* host,

                  uint16_t port,

                  const std::string& message);

    }

    Network接口现在只需要一个send接口,可以向指定地址发送消息。下面我们用不同的方法来定义Network接口。

    虚函数

    虚函数是定义C++接口最直接的方式,使用虚函数定义Network接口类如下:

    class Network

    {

    public:

        virtual bool send(const char* host,

                          uint16_t port,

                          const std::string& message) = 0;

        static Network* New();

        static void Delete(Network* network);

    }

    将send定义为纯虚函数,让子类去实现,子类不对外暴露,提供静态方法New来创建子类对象,并以父类Network的指针形式返回。接口的设计一般遵循对象在哪创建就在哪销毁的原则,因此提供静态的Delete方法来销毁对象。因为对象的销毁封装在接口内部,因此Network接口类可以不用虚析构函数。

    使用虚函数定义接口简单直接,但是有很多弊端:

    虚函数开销:虚函数调用需要使用虚函数表指针间接调用,运行时才能决定调用哪个函数,无法在编译链接期间内联优化。实际上调用接口在编译期间就能确定调用哪个函数,无需虚函数的动态特性。

    二进制兼容:由于虚函数是按照索引查询虚函数表来调用,增加虚函数会造成索引变化,新接口不能在二进制层面兼容老接口,而且由于用户可能继承了Network接口类,在末尾增加虚函数也有风险,因此虚函数接口一经发布,难以修改。

    指向实现的指针

    指向实现的指针是C++比较推荐的定义接口的方式,使用指向实现的指针定义Network接口类如下:

    class NetworkImpl;

    class Network

    {

    public:

        bool send(const char* host,

                  uint16_t port,

                  const std::string& message);

        Network();

        ~Network();

    private:

        NetworkImpl* impl;

    }

    Network的实现通过impl指针转发给NetworkImpl,NetworkImpl使用前置声明,实现对用户隐藏。使用指向实现的指针的方式定义接口,接口类对象的创建和销毁可以由用户负责,因此用户可以选择将Network类的对象创建在栈上,生命周期自动管理。

    使用指向实现的指针定义接口具有良好的通用性,用户能够直接创建和销毁接口对象,并且增加新的接口函数不影响二进制兼容性,便于系统的演进。

    指向实现的指针增加了一层调用,尽管对性能的影响几乎可以忽略不计,但不太符合C++的零开销原则,那么问题来了,C++能否实现零开销的接口呢?当然可以,即下面要介绍的隐藏的子类。

    隐藏的子类

    隐藏的子类可以实现零开销的接口,思想非常简单。调用接口要实现的目标是解耦,主要就是隐藏实现,也即隐藏接口类的成员变量,如果能将接口类的成员变量都移到另一个隐藏的实现类中,接口类就不需要任何成员变量,也就实现了隐藏实现的目的。隐藏的子类就是这个隐藏的实现类,使用隐藏的子类定义Network接口类如下:

    class Network

    {

    public:

        bool send(const char* host,

                  uint16_t port,

                  const std::string& message);

        static Network* New();

        static void Delete(Network* network);

    protected:

        Network();

        ~Network();

    }

    Network接口类只有成员函数(非虚函数),没有成员变量,并且构造函数和析构函数都申明为protected。提供静态方法New创建对象,静态方法Delete销毁对象。New方法的实现中创建隐藏的子类NetworkImpl的对象,并以父类Network指针的形式返回。NetworkImpl类中存放Network类的成员变量,并将Network类声明为friend:

    class NetworkImpl : public Network

    {

        friend class Network;

    private:

        //Network类的成员变量

    }

    Network的实现中,创建隐藏的子类NetworkImpl的对象,并以父类Network指针的形式返回,通过将this强制转换为NetworkImpl的指针,访问成员变量:

    bool Network::send(const char* host,

                      uint16_t port,

                      const std::string& message)

    {

        NetworkImpl* impl = (NetworkImpl*)this;

        //通过impl访问成员变量,实现Network

    }

    static Network* New()

    {

        return new NetworkImpl();

    }

    static void Delete(Network* network)

    {

        delete (NetworkImpl*)network;

    }

    使用隐藏的子类定义接口同样具有良好的通用性和二进制兼容性,同时没有增加任何开销,符合C++的零开销原则。

    三 、回调接口

    同样以Network接口为例,说明C++中的回调接口的定义及实现,示例如下:

    class Network

    {

    public:

        class Listener

        {

        public:

            void onReceive(const std::string& message);

        }

        bool send(const char* host,

                  uint16_t port,

                  const std::string& message);

        void registerListener(Listener* listener);

    }

    现在Network需要增加接收消息的功能,增加Listener接口类,由用户实现,并注册其对象到Network中后,当有消息到达时,回调Listener的onReceive方法。

    虚函数

    使用虚函数定义Network接口类如下:

    class Network

    {

    public:

        class Listener

        {

        public:

            virtual void onReceive(const std::string& message) = 0;

        }

        bool send(const char* host,

                  uint16_t port,

                  const std::string& message);

        void registerListener(Listener* listener);

    }

    将onReceive定义为纯虚函数,由用户继承实现,由于多态的存在,回调的是实现类的方法。

    使用虚函数定义回调接口简单直接,但同样存在和调用接口中使用虚函数同样的弊端:虚函数调用开销,二进制兼容性差。

    函数指针

    函数指针是C语言的方式,使用函数指针定义Network接口类如下:

    class Network

    {

    public:

        typedef void (*OnReceive)(const std::string& message, void* arg);

        bool send(const char* host,

                  uint16_t port,

                  const std::string& message);

        void registerListener(OnReceive listener, void* arg);

    }

    使用函数指针定义C++回调接口简单高效,但只适用于回调接口中只有一个回调函数的情形,如果Listener接口类中要增加onConnect,onDisconnect等回调方法,单个函数指针无法实现。另外函数指针不太符合面向对象的思想,可以换成下面要介绍的std::function。

    std::function

    std::function提供对可调用对象的抽象,可封装签名相符的任意的可调用对象。使用std::function定义Network接口类如下:

    class Network

    {

    public:

        typedef std::function<void(const std::string& message)> OnReceive;

        bool send(const char* host,

                  uint16_t port,

                  const std::string& message);

        void registerListener(const OnReceive& listener);

    }

    std::function可以很好的取代函数指针,配合std::bind,具有很好的通用性,因而被广受推崇。但std::function同样只适用于回调接口中只有一个回调方法的情形。另外,std::function比较重量级,使用上面的便利却会带来了性能上的损失,有人做过性能对比测试,std::function大概比普通函数慢6倍以上,比虚函数还慢。

    类成员函数指针

    类成员函数指针的使用比较灵活,使用类成员函数指针定义Network接口类如下:

    class Network

    {

    public:

        class Listener

        {

        public:

            void onReceive(const std::string& message);

        }

        typedef void (Listener::* OnReceive)(const std::string& message);

        bool send(const char* host,

                  uint16_t port,

                  const std::string& message);

        void registerListener(Listener* listener, OnReceive method);

        template<typename Class>

        void registerListener(Class* listener,

            void (Class::* method)(const std::string& message)

        {

            registerListener((Listener*)listener, (OnReceive)method);

        }

    }

    因为类成员函数指针必须和类对象一起使用,所以Network的注册接口需要同时提供对象指针和成员函数指针,registerListener模板函数可注册任意类的对象和相应符合签名的方法,无需继承Listener,与接口类解耦。

    使用类成员函数指针定义C++回调接口灵活高效,可实现与接口类解耦,并且不破坏面向对象特性,可很好的取代传统的函数指针的方式。

    类成员函数指针同样只适用于回调接口中只有一个回调方法的情形,如果有多个回调方法,需要针对每一个回调方法提供一个类成员函数指针。那么有没有方法既能实现与接口类解耦,又能适用于多个回调方法的场景呢?参考下面介绍的非侵入式接口。

    四 、非侵入式接口

    Rust中的Trait功能非常强大,可以在类外面,不修改类代码,实现一个Trait,那么C++能否实现Rust的Trait的功能呢?还是以Network接口为例,假设现在Network发送需要考虑序列化,重新设计Network接口,示例如下:

    定义Serializable接口:

    class Serializable

    {

    public:

        virtual void serialize(std::string& buffer) const = 0;

    };

    Network接口示例:

    class Network

    {

    public:

        bool send(const char* host,

                  uint16_t port,

                  const Serializable& s);

    }

    Serializable接口相当于Rust中的Trait,现在一切实现了Serializable接口的类的对象均可以通过Network接口发送。那么问题来了,能否在不修改类的定义的同时,实现Serializable接口呢?假如我们要通过Network发送int类型的数据,能否做到呢?答案是肯定的:

    1. class IntSerializable : public Serializable

    {

    public:

        IntSerializable(const int* i) :

            intThis(i)

        {

        }

        IntSerializable(const int& i) :

            intThis(&i)

        {

        }

        virtual void serialize(std::string& buffer) const override

        {

            buffer += std::to_string(*intThis);

        }

    private:

        const int* const intThis;

    };

    有了实现了Serializable接口的IntSerializable,就可以实现通过Network发送int类型的数据了:

    Network* network = Network::New();

    int i = 1;

    network->send(ip, port, IntSerializable(i));

    Rust编译器通过impl关键字记录了每个类实现了哪些Trait,因此在赋值时编译器可以自动实现将对象转换为相应的Trait类型,但C++编译器并没有记录这些转换信息,需要手动转换类型。

    非侵入式接口让类和接口区分开来,类中的数据只有成员变量,不包含虚函数表指针,类不会因为实现了N个接口而引入N个虚函数表指针;而接口中只有虚函数表指针,不 包含数据成员,类和接口之间通过实现类进行类型转换,实现类充当了类与接口之间的桥梁。类只有在充当接口用的时候才会引入虚函数表指针,不充当接口用的时候没有虚函数表指针,更符合C++的零开销原则。

    C++编程语言的应用对于开发人员来说是一个非常有用的应用语言。不过其中还有许多比较高深的内容值得我们去花大量的时间去学习。今天就讲到这里啦,大家记得点赞收藏,分享转发,加关注哦!

    展开全文
  • 接口有哪些类型? 接口的分类:1.webservice接口 2.http api接口 webService接口是走soap协议通过http传输,请求报文和返回报文都是xml格式的,我们在测试的时候都用通过工具才能进行调用,测试。 http api接口是...

    什么是接口?
    接口测试主要用于外部系统与系统之间以及内部各个子系统之间的交互点,定义特定的交互点,然后通过这些交互点来,通过一些特殊的规则也就是协议,来进行数据之间的交互。

    接口都有哪些类型?
    接口的分类:1.webservice接口 2.http api接口
    webService接口是走soap协议通过http传输,请求报文和返回报文都是xml格式的,我们在测试的时候都用通过工具才能进行调用,测试。
    http api接口是走http协议,通过路径来区分调用的方法,请求报文都是key-value形式的,返回报文一般都是json串,有get和post等方法,这也是最常用的两种请求方式。
    json是一种通用的数据类型,所有的语言都认识它。(json的本质是字符串,他与其他语言无关,只是可以经过稍稍加工可以转换成其他语言的数据类型,比如可以转换成Python中的字典,key-value的形式,可以转换成JavaScript中的原生对象,可以转换成java中的类对象等。)
    B/S和C/S的区别?
    CS响应速度快,安全性强,用户体验好,一般应用于局域网中,但是开发维护成本高,;
    BS可以实现跨平台,客户端零维护,但是个性化能力低,响应速度较慢
    url地址有哪些组成?
    协议 http/https
    端口号:http/https 80 443
    请求方式 get
    参数name pwd
    参数值 admin 123
    域名部分
    虚拟目录部分
    文件名
    http/https 的区别?
    1.安全性不同。HTTP是超文本传输协议,信息是明文传输的。HTTPS是具有安全性的ssl证书加密的传输协议。所以HTTPS比HTTP更安全
    2.默认端口不同。HTTP的默认端口是80,HTTPS的默认端口是443
    3.协议不同。HTTP是无状态的协议,而HTTPS是由ssl+HTTP构建的可进行加密传输、身份认证的网络协议。
    4.部署的成本不同。HTTP是免费的,HTTPS是需要证书的,一般免费证书很少,需要交费。所以HTTPS的成本相对会更高。
    get请求,post请求的区别:
    1、GET使用URL或Cookie传参。而POST将数据放在BODY中。
    2、GET的URL会有长度上的限制,则POST的数据则可以非常大。
    3、POST比GET安全,因为数据在地址栏上不可见。
    4、一般get请求用来获取数据,post请求用来发送数据。
    其实上面这几点,只有最后一点说的是比较靠谱的,第一点post请求也可以把数据放到url里面,get请求其实也没长度限制,post请求看起来参数是隐式的,稍微安全那么一些些,就算post请求,你通过抓包也是可以抓到参数的。
    常见状态码和含义
    1,100 - 继续。客户端应继续其请求
    2、200 2开头的都表示这个请求发送成功,最常见的就是200,就代表这个请求是ok的,服务器也返回了。
    1).202 - 已接受。已经接受请求,但未处理完成
    2).206 - 部分内容。服务器成功处理了部分GET请求
    3、300 3开头的代表重定向,最常见的是302,把这个请求重定向到别的地方了。
    1).301 - 请求永久重定向,转移到其它URL
    2).302 - 请求临时重定向
    3).304 - 请求被重定向到客户端本地缓存
    4、400 400代表客户端发送的请求有语法错误,401代表访问的页面没有授权,403表示没有权限访问这个页面,404代表没有这个页面。
    1)401 - 客户端请求没有经过授权
    2)403 - 客户端的请求被服务器拒绝,一般为客户端没有访问权限
    3)404 - 客户端请求的URL在服务端不存在
    4)406 - 服务器无法根据客户端请求的内容特性完成请求
    5)408 - 服务器等待客户端发送的请求时间过长,超时
    5、500 5开头的代表服务器有异常,500代表服务器内部异常,504代表服务器端超时,没返回结果。
    501 - 服务器不支持请求的功能,无法完成请求
    505 - 服务器不支持请求的HTTP协议的版本,无法完成处理

    cookie与session的区别:

    1、cookie数据存放在客户的浏览器上,session数据放在服务器上。

    2、cookie不是很安全,别人可以分析存放在本地的cookie并进行cookie欺骗

    考虑到安全应当使用session。

    3、session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能

    考虑到减轻服务器性能方面,应当使用cookie。

    4、单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。

    服务器的响应状态码
    详情:https://blog.csdn.net/alice_tl/article/details/87186772

    HTTP响应
    服务器收到了客户端发来的HTTP请求后,根据HTTP请求中的动作要求,服务端做出具体的动作,将结果回应给客户端,称为HTTP响应。

    展开全文
  • 三、常见的API形式有哪些? 1、HTTP/HTTPS类型接口 2、RPC接口 3、Web Service 接口 四、API接口使用案例 1.纯请求API接口 2.开发中的API接口请求 五、常用API接口工具 1.接口测试工具 2.接口文档管理工具 六、本地...
  • 本篇是本人学习Java比较,如雷同,纯属巧合。Java中接口与抽象类比较相同点1)接口和抽象类都不能被实例化,它们都位于继承树的...对于接口的实现者而言,接口规定了实现者必须向外提供哪些服务(以方法的形式提供)
  • 2. Api有哪些安全问题?http接口—前后端分离mvvm 3. Token授权机制 用户使用用户名密码登录后服务器给客户端返回一个Token(通常是UUID),并将Token-UserId以键值对的形式存放在缓存服务器中。服务端接收到请求后...
  • 一般服务的安全包括认证(Authentication)与授权(Authorization)两部分,认证即证明一个用户是合法的用户,比如通过用户名密码的形式,授权则是控制某个用户可以访问哪些资源。比较成熟的框架Shiro、Spring ...
  • 它是一种服务模式,一个特殊的产品,目前较大规模的互联网企业都自己的开放平台。 如果把自己局限为一个功能产品经理,工作当中只是研究研究产品交互形式、操作流程、表层架构,那确定不太需要知道开放平台相关...
  • 的直播带货系统是以小程序的形式搭建起来的,那么微信开放平台的接入不可或缺,微信开放平台作为第三方移动程序提供接口,使用户将第三方程序的内容发布给好友或者分享至朋友圈。从而达成互动营销的目的。同
  • 公众号中前边的文章QGIS连接Arcgis Server发布数据,是QGIS通过调用Arcgis Server的数据服务接口进行数据和样式的获取,这些具体的数据接口有哪些呢,下边文章中简单的进行一下介绍。 在Arcgis Server的数据界面中,...
  • 还是通过阅读soa接口文档,在软件开发行业,总会文档落后于代码的情况?这些手法都太拙劣了。其实我们可以通过一些更快捷高效的手段达到目的。 萌生这个想法的背景是,我们公司部署了几个soa服务。然后我们测试...
  • 1 面试信息 面试形式:视频面试 ...2.JSP有哪些内置对象 3.cookie和session的不同之处 4.Spring中的IoC是什么 5.介绍MySQL的事务以及索引 6.Java的抽象类和接口有什么区别 6.JC(垃圾回收)原理 7.JC算...
  •  Collection是集合类的上级接口,继承与他的接口主要Set 和List. Collections是针对集合类的一个帮助类,他提供一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作。 13、&和&&的区别。 &是位运算符...
  • 17、JSP和Servlet有哪些相同点和不同点,他们之间的联系是什么? 90 18、MVC的各个部分都有那些技术来实现?如何实现? 90 19、我们在web应用开发过程中经常遇到输出某种编码的字符,如iso8859-1等,如何输出一个某种...
  • 而开源界里各种著名的传输协议,如 raknet/enet 之类,一发布都是整套协议栈一起发布,这种形式是不利于多样化的,我的项目只能选择用或者不用你,很难选择 “部分用你”,然而你一套协议栈设计的再好,是非常难以...
  • 坐标地址互找

    2015-03-05 17:10:12
       Geocoding API 1.什么是Geocoding? 2.Geocoding API有哪些功能? 3.使用限制 ...Geocoding API是一个供程序员调用的、http形式的地图服务接口。主要服务那些非网页程序的

空空如也

空空如也

1 2 3 4 5 ... 9
收藏数 167
精华内容 66
关键字:

服务接口形式有哪些