• 应该用bind+function取代虚函数吗?


    用bind+function取代虚函数在好几年前就有人提出了,曾引起广泛的讨论,有支持的有反对的,可能赞成的人占大多数。这个话题挺有趣,本来是作为技术沙龙的开放性话题来讨论的,由于时间关系并没有讨论。今天就来具体探讨一下这个问题,我将做两个实验来验证一下这两种做法,具体是实现两个模式:策略模式和责任链模式。我将分别用经典的虚函数和bind+function来实现这两个模式。通过这两个实验来得出我的结论。

    实验一:策略模式的实现

    1.虚函数方式实现策略模式

    class Calculater
    {
    public:
        virtual int calculate(int x, int y) = 0;
    };
    
    class Minus : public Calculater
    {
    public:
        int calculate(int x, int y) 
        {
            return x - y;
        }
    };
    
    class Plus : public Calculater
    {
    public:
        int calculate(int x, int y) 
        {
            return x + y;
        }
    };
    
    class CalcuClient
    {
    private:
        Calculater* m_caculater;
    public:
        CalcuClient(Calculater* caculater) : m_caculater(caculater){}
    
        int calculate(int x, int y)
        {
            return m_caculater->calculate(x, y);
        }
    };
    View Code

    2.bind+function方式实现策略模式

    class NewCalcuClient
    {
    private:
        std::function<int(int, int)> m_function;
    
    public:
        NewCalcuClient(std::function<int(int, int)> function) : m_function(function){}
    
        int calculate(int x, int y)
        {
            return m_function(x, y);
        }
    };

    测试代码:

        Minus minus;
        CalcuClient client(&minus);
    
        Plus plus;
        CalcuClient client2(&plus);
    
        int r = client.calculate(7, 4);
        int r2 = client2.calculate(7, 4);
    
       //bind+function NewCalcuClient newclient(boost::bind(
    &Minus::calculate, &minus, _1, _2)); NewCalcuClient newclient2(boost::bind(&Plus::calculate, &plus, _1, _2)); int r3 = newclient.calculate(7, 4); int r4 = newclient2.calculate(7, 4);

      bind+function取代虚函数的一个重要理由是虚函数带来了效率损失,bind+function效率更高,我做了一个性能测试, 分别调用10000000次来看耗时,发现虚函数比bind+function方式要快一些,无论是用标准库的bind还是boost的bind,都比虚函数方式要慢,所以说bind+function比虚函数性能更好是想当然,站不住脚的。接下来看第二个实验。

    实验二:责任链模式的实现

    1.虚函数方式实现责任链模式

    struct Request
    {
        int RequestType;
    };
    
    class Handler
    {
    protected:
        std::shared_ptr<Handler> m_next;
    public:
        Handler(std::shared_ptr<Handler> next) : m_next(next){}
    
        virtual void HandleRequest(Request) = 0;
    };
    
    class ConcreteHandler1 : public Handler
    {
    public:
        ConcreteHandler1(std::shared_ptr<Handler> next) : Handler(next){}
    
        void HandleRequest(Request request)
        {
            if (request.RequestType == 1)
            {
                cout << "request handled in ConcreteHandler1" << endl;
            }
            else
            {
                if (m_next != nullptr)
                    m_next->HandleRequest(request);
            }
        }
    };
    
    class ConcreteHandler2 : public Handler
    {
    public:
        ConcreteHandler2(std::shared_ptr<Handler> next) : Handler(next){}
    
        void HandleRequest(Request request)
        {
            if (request.RequestType == 2)
            {
                cout << "request handled in ConcreteHandler2" << endl;
            }
            else
            {
                if (m_next != nullptr)
                    m_next->HandleRequest(request);
            }
        }
    };
    
    class ConcreteHandler3 : public Handler
    {
    public:
        ConcreteHandler3(std::shared_ptr<Handler> next) : Handler(next){}
    
        void HandleRequest(Request request)
        {
            if (request.RequestType == 3)
            {
                cout << "request handled in ConcreteHandler3" << endl;
            }
            else
            {
                if (m_next != nullptr)
                    m_next->HandleRequest(request);
            }
        }
    };

    2.bind+function方式实现责任链模式

    class ChainHandler
    {
        
    public:
        std::function<void(Request)> function;
    
        void HandleRequest(Request request)
        {
            function(request);
        }
    
        std::function<void(Request)>& getfunction()
        {
            return function;
        }
    };
    
    void assemble(std::function<void(Request)> call, std::function<void(Request)> next, Request request)
    {
        if (next != nullptr)
            next(request);
        else
            call(request);
    }

    测试代码:

    void Test()
    {
        auto thirdHandler = std::make_shared<ConcreteHandler3>(nullptr); 
        auto secondHandler = std::make_shared<ConcreteHandler2>(thirdHandler); 
        auto firstHandler = std::make_shared<ConcreteHandler1>(secondHandler); 
    
        Request request = { 2 };
        firstHandler->HandleRequest(request);
    
        ChainHandler chain;
        
        std::function<void(Request)> f1 = std::bind(&ConcreteHandler1::HandleRequest, firstHandler, std::placeholders::_1);
        std::function<void(Request)> f2 = std::bind(&ConcreteHandler2::HandleRequest, secondHandler, std::placeholders::_1);
        std::function<void(Request)> f3 = std::bind(&ConcreteHandler3::HandleRequest, thirdHandler, std::placeholders::_1);
        
        chain.function = std::bind(&assemble, f1, chain.function, std::placeholders::_1);
        chain.function = std::bind(&assemble, f2, chain.function, std::placeholders::_1);
        chain.function = std::bind(&assemble, f3, chain.function, std::placeholders::_1);
    
        chain.HandleRequest(request);
    }

    bind+function实现责任链模式的关键代码在这里:

    chain.function = std::bind(&assemble, f1, chain.function, std::placeholders::_1);
    chain.function = std::bind(&assemble, f2, chain.function, std::placeholders::_1);
    chain.function = std::bind(&assemble, f3, chain.function, std::placeholders::_1);
    
    chain.HandleRequest(request);

      这几行代码通过assemble不断地往function链条中加function,最后调用的时候会从链条的第一个function开始调用。

      bind+function取代虚函数的另外一个理由是松耦合,去除了继承的限制,方法的实现更加灵活,确实,低耦合确实是bind+function最大的优点,然而这个最大的优点也成了它最大的缺点,当需要替代的虚函数增多时,组装function的复杂度也在增加,太松散了导致代码也不够直观,代码的内聚性也变低了。比如上面责任链模式的实现,虚函数的实现明显比bind+function的实现要优雅。

    结论

      bind+function相比虚函数的实现在性能上并不占优,最大的优点是大大降低类之间的耦合度,缺点是太过于松散导致代码的内聚性和可读性降低。

      bind+function适用的场景:

      1.迫切需要接口和实现解耦;

      2.需要解耦的接口很少。

      满足这两种情况适合用bind+function,否则还是用虚函数更好。

  • 相关阅读:
    Autoit对win系统弹窗的操作
    Linux服务器测试网络连通性
    如何给linux配置两个不同网段的ip
    记下看过并觉得非常有用的文章
    使用python+selenium对12306车票数据读取
    windows系统mysql安装
    Python使用正则匹配re实现eval计算器
    css3[补1]
    Javascript[2]
    Javascript[1]
  • 原文地址:https://www.cnblogs.com/qicosmos/p/4527804.html
Copyright © 2020-2023  润新知