Как реализовать принцип разделения интерфейсов, используя умные указатели в C ++? - PullRequest
4 голосов
/ 03 июля 2019

Я пришел из Delphi и C #, поэтому я понимаю интерфейсы с их точки зрения.Я занимаюсь C ++ в течение нескольких лет и до сих пор изучаю интерфейсы с его точки зрения.

В моем приложении возникает ситуация, когда мне нужны классы, которые реализуют несколько интерфейсов (то есть наследуют несколько чистых абстрактных классов), чтобы указывать различные варианты поведения.которые поддерживаются каждым классом.Это не совсем провайдер, но он достаточно близок к тому, что это та же проблема.

Интерфейсы поведения не наследуются друг от друга.Иерархии нет.

Delphi и C # делают это, не дыша тяжело, но я пытаюсь выяснить, как это делается в C ++.(Кроме того, на данный момент я ограничен C ++ 11.)

Я исследовал dynamic_pointer_cast, static_pointer_cast, но им требуется иерархия наследования.

Я посмотрел на reinterpret_pointer_cast, но он недоступенв C ++ 11.

Я пытался использовать корневой интерфейс, который наследуют мои интерфейсы поведения (аналогично IUnknown в COM или IInterface в Delphi), создавая иерархию наследования, но затем я столкнулся с проблемой алмаза.

Я видел несколько предложений по добавлению методов в базовый интерфейс, чтобы возвращать ссылки на каждый интерфейс поведения, но это такая связь, которую я действительно не хочу, поскольку мне может понадобиться добавить другие поведения и классы, которыереализовать их позже.

Вот код, который я не скомпилировал, показывающий простой пример того, что я пытаюсь сделать.

class IBase
{
public:
    virtual void DoBaseStuff() = 0;
}

class IBehaviorA
{
public:
    virtual void DoBehaviorA() = 0;
}

class IBehaviorB
{
public:
    virtual void DoBehaviorB() = 0;
}

class Base : public IBase
{
public:
    Base()  {}
    virtual ~Base() {}
    virtual void DoBaseStuff() { /* ... */ }
}

class JustA : public IBase, public IBehaviorA
{
public:
    JustA() {}
    virtual ~JustA() {}
    virtual void DoBaseStuff() { /* ... */ }
    virtual void DoBehaviorA() { /* ... */ }
}

class JustB : public IBase, public IBehaviorB
{
public:
    JustB() {}
    virtual ~JustB() {}
    virtual void DoBaseStuff() { /* ... */ }
    virtual void DoBehaviorB() { /* ... */ }
}

class AandB : public IBase, public IBehaviorA, public IBehaviorB
{
public:
    AandB() {}
    virtual ~AandB() {}
    virtual void DoBaseStuff() { /* ... */ }
    virtual void DoBehaviorA() { /* ... */ }
    virtual void DoBehaviorB() { /* ... */ }
}

void ProcessAllBehaviorA(std::vector<std::shared_ptr<IBase>> bases)
{
    for (auto& base : bases)
    {
        std::shared_ptr<IBehaviorA> behaviorA
                    = SOME_KIND_OF_CAST<IBehaviorA>(base);

        if (behaviorA != nullptr)
        {
            behaviorA->DoBehaviorA();
        }
    }
}

void main()
{
    std::vector<std::shared_ptr<IBase>> bases;

    bases.push_back(std::shared_ptr<IBase>(new Base()));
    bases.push_back(std::shared_ptr<IBase>(new JustA()));
    bases.push_back(std::shared_ptr<IBase>(new JustB()));
    bases.push_back(std::shared_ptr<IBase>(new AandB()));

    ProcessAllBehaviorA(bases);
}

Я пытаюсь выяснить, что делатьделать там, где в методе ProcessAllBehaviorA находится местозаполнитель SOME_KIND_OF_CAST.

Я уверен, что не могу быть первымПервый человек, чтобы попытаться сделать это.

Как другие люди реализуют принцип разделения интерфейса (или аналогичные шаблоны, как у меня), используя умные указатели в C ++?

1 Ответ

4 голосов
/ 03 июля 2019

Я исследовал dynamic_pointer_cast, static_pointer_cast, но им требуется иерархия наследования.

Ваш код имеет иерархию наследования, поэтому std::dynamic_pointer_cast будет работать просто отлично!Я добавил некоторое поведение в ваш код, и он работает, как и ожидалось.

#include <memory>
#include <vector>
#include <iostream>

class IBase
{
public:
    virtual void DoBaseStuff() = 0;
};

class IBehaviorA
{
public:
    virtual void DoBehaviorA() = 0;
};

class IBehaviorB
{
public:
    virtual void DoBehaviorB() = 0;
};

class Base : public IBase
{
public:
    Base() {}
    virtual ~Base() {}
    virtual void DoBaseStuff() { /* ... */ }
};

class JustA : public IBase, public IBehaviorA
{
public:
    JustA() {}
    virtual ~JustA() {}
    virtual void DoBaseStuff() { /* ... */ }
    virtual void DoBehaviorA() { std::cout << "I am just A!" << std::endl; }
};

class JustB : public IBase, public IBehaviorB
{
public:
    JustB() {}
    virtual ~JustB() {}
    virtual void DoBaseStuff() { /* ... */ }
    virtual void DoBehaviorB() { /* ... */ }
};

class AandB : public IBase, public IBehaviorA, public IBehaviorB
{
public:
    AandB() {}
    virtual ~AandB() {}
    virtual void DoBaseStuff() { /* ... */ }
    virtual void DoBehaviorA() { std::cout << "I am A and B" << std::endl; }
    virtual void DoBehaviorB() { /* ... */ }
};

void ProcessAllBehaviorA(std::vector<std::shared_ptr<IBase>> bases)
{
    for (auto& base : bases)
    {
        std::shared_ptr<IBehaviorA> behaviorA
            = std::dynamic_pointer_cast<IBehaviorA>(base);

        if (behaviorA != nullptr)
        {
            behaviorA->DoBehaviorA();
        }
        else {
            std::cout << "Requested pointer does not implement A." << std::endl;
        }
    }
}

void main()
{
    std::vector<std::shared_ptr<IBase>> bases;

    bases.push_back(std::shared_ptr<IBase>(new Base()));
    bases.push_back(std::shared_ptr<IBase>(new JustA()));
    bases.push_back(std::shared_ptr<IBase>(new JustB()));
    bases.push_back(std::shared_ptr<IBase>(new AandB()));

    ProcessAllBehaviorA(bases);
}

Вывод:

Requested pointer does not implement A.
I am just A!
Requested pointer does not implement A.
I am A and B

Кстати, вы пропустили точку с запятой в конце определения класса.

...