Лучшая реализация для класса, который может иметь различные реализации базовых членов - PullRequest
1 голос
/ 17 января 2020

Я хотел бы иметь дочерний класс Handler, который обрабатывает несколько обратных вызовов и передает данные из одного класса в другой. Однако базовые классы B1 и B2 могут иметь разные реализации для своих членов.

Ниже приведен способ реализации желаемого поведения. Я думаю, что должен быть лучший способ, но не могу понять.

// Example program
#include <iostream>
#include <string>

template <class T>
class IBase
{
    public:
        IBase()
        {
            object = new T(*this);    
        };

        ~IBase()
        {
            delete object;
        }

        virtual void ValidateCallback()
        {
        };

        void RxCallback()
        {
           object->RxCallback();
        };

        void Send()
        {
            object->Send();
        };

       T* object;
};

class C1
{
    public:
        virtual void RxCompleteCallback() = 0;

        void RxParse()
        {
            std::cout << "Parse C1" << std::endl;
            RxCompleteCallback();
        };
};

class C2
{
    public:
        virtual void RxCompleteCallback() = 0;

        void RxParse()
        {
            std::cout << "Parse C2" << std::endl;
            RxCompleteCallback();
        };
};

class B1 : public C1
{
    public:
        B1(IBase<B1> &handler )
        {
           ihandler = &handler;
        };

        void DoSomething()
        {
            std::cout << "DoSomething B1" << std::endl;
            ihandler->ValidateCallback();
        };

        void RxCompleteCallback() override
        {
            std::cout << "DoSomething other than B2" << std::endl;
            std::cout << "RxCompleteCallback" << std::endl;
        };

        void RxCallback()
        {
            RxParse();
        };

        void Send()
        {
            DoSomething();
        };

        IBase<B1> * ihandler;
};

class B2 : public C2
{
    public:
        B2(IBase<B2> &handler )
        {
           ihandler = &handler;
        };

        void DoSomething()
        {
            std::cout << "DoSomething B2" << std::endl;
            ihandler->ValidateCallback();
        };

        void RxCompleteCallback() override
        {
            std::cout << "DoSomething other than B1" << std::endl;
            std::cout << "RxCompleteCallback" << std::endl;
        };

        void RxCallback()
        {
            RxParse();
        };

        void Send()
        {
            DoSomething();
        };

        IBase<B2> * ihandler;
};

class Validate
{
    public:
        void CalculateValidation()
        {
            std::cout << "Calculate validation" << std::endl;
        };
};

template <class T>
class Handler : public IBase<T>, public Validate
{
    public:
        void ValidateCallback() override
        {
            std::cout << "ValidateCallback" << std::endl;
            CalculateValidation();
        };

        void Receive()
        {
            IBase<T>::RxCallback();
        };

        void Send()
        {
           IBase<T>::Send();
        }

};

int main()
{
    Handler<B1> handler1;
    handler1.Receive();
    handler1.Send();
    std::cout << std::endl;
    Handler<B2> handler2;
    handler2.Receive();
    handler2.Send();
}

Вывод:

Parse C1
DoSomething other than B2
RxCompleteCallback
DoSomething B1
ValidateCallback
Calculate validation

Parse C2
DoSomething other than B1
RxCompleteCallback
DoSomething B2
ValidateCallback
Calculate validation

1 Ответ

0 голосов
/ 17 января 2020

Есть несколько способов сделать это в C ++. Трудно сказать, каков наилучший способ, это зависит от того, как вы будете его использовать, и приведенный вами пример слишком прост, чтобы рекомендовать конкретный способ c. Как правило, я бы сказал, что вы хотите получить c классы, определенные для протокола, из Handler, а не наоборот, поэтому вы должны написать:

class Handler {
public:
    virtual void Receive() {};
    virtual void Send() {};
};

class B1: public Handler {
    virtual void Receive() {
        ...
    }

    virtual void Send() {
        ...
    }
};

int main() {
    B1 handler1;
    handler1.Receive();
    ...
}

Основная проблема здесь что вам нужно использовать virtual функции-члены здесь, иначе базовый класс не знает, какую реализацию производного класса вызывать. Но это позволяет вам передавать Handler * в качестве аргумента другой функции, которая затем будет работать с любым производным классом без использования шаблонов.

Другой вариант - использовать шаблон любопытно повторяющегося шаблона , который будет выглядеть так:

template <typename T>
class Handler {
    void Receive() {
        static_cast<T*>(this)->Receive();
    }

    void Send() {
        static_cast<T*>(this)->Send();
    }
};

class B1: public Handler<B1>
{
    void Receive() {
        ...
    }

    void Send() {
        ...
    }
};

int main() {
    B1 handler1;
    handler1.Receive();
    ...
}

Это позволяет избежать виртуальных методов. Он также очень похож на ваш class Handler, но имеет то преимущество, что ему не нужна переменная-член T *object.

...