如何管理基类中不同类型的数据?

问题描述

我的目标是将数据与各种实现分开。我不希望我的东西知道它们正在使用的实际子类,无论哪种方式。用最少的信息让事物只执行一项任务。

我会先往你的眼睛里扔一些code

// Example program
#include <iostream>
#include <string>
#include <memory>
#include <vector>
#include <functional>

class Model
{
    public:
    
    virtual bool set(int p_attrId,int p_value) {return false;}; 
    virtual bool get(int p_attrId,int & p_value) const {return false;};

};

class Derived: public Model
{
    static constexpr int c_classId = 1;
    int value = 1;
    public:
    
    enum EAttrs
    {
        eAttr1 = c_classId * 1000
    };
    
    virtual bool set(int p_attrId,int p_value) override
    {
        switch(p_attrId)
        {
            case eAttr1:
                value = p_value;
                return true;
            
            default:
                return Model::set(p_attrId,p_value);
        }
    }

    virtual bool get(int p_attrId,int & p_value) const override
    {
        switch(p_attrId)
        {
            case eAttr1:
                p_value = value;
                return true;
            
            default:
                return Model::get(p_attrId,p_value);
        }
    }
};

// GuiTextBoxComponent.h

// no includes to any class derived from model

class GuiTextBoxComponent
{
        std::weak_ptr<Model> m_model;
        int m_attrId;
    public:
        void draw()
        {
            auto locked = m_model.lock();
            if(locked)
            {
                int value;
                bool result = locked->get(m_attrId,value);
                if(!result)
                {
                    std::cout << "Failed to get attribute " << m_attrId << "\n";
                    return;
                }
            
                std::cout << "AttrID: " << m_attrId << " Value: " << value << "\n";
            }
            else
            {
                std::cout << "Model is dead\n";
            }
        }
        
        void setSource(std::weak_ptr<Model> p_model,int p_attrId)
        {
            m_model = p_model;
            m_attrId = p_attrId;
        }
};


int main()
{
    std::shared_ptr<Model> model (new Derived);
    GuiTextBoxComponent textBox;
    textBox.setSource(model,Derived::eAttr1);
    textBox.draw();
}

这背后的动机是从单个界面获取所有数据。 我需要能够添加像 GuiTextBoxComponent 这样的功能,而其标题中没有 #include "Derived1.h"

这种设计的挑战在于 Model 接口需要实现程序中任何地方所需的所有类型。

您将如何扩展所提供的类型?

是否有其他设计可以用来实现类似的结果?

解决方法

通常,我认为这是一个 XY 问题,但您可以通过以下方式稍微美化您的代码。首先,我实现了两个接口:GetterSetter,例如:

enum class EAttrs {
    eAttr1
};

template <typename GetterImpl>
struct Getter {
    bool get(EAttrs const attrId,int& value) {
        switch (attrId) {
        case EAttrs::eAttr1:
            return static_cast<GetterImpl*>(this)->get(value);
        default:
            return false;
        }
    }
};

template <typename SetterImpl>
struct Setter {
    bool set(EAttrs const attrId,int value) {
        switch (attrId) {
        case EAttrs::eAttr1:
            return static_cast<SetterImpl*>(this)->set(value);
        default:
            return false;
        }
    }
};

这里我使用了 CRTP,即静态多态性。那么你的派生类的实现就简单一点:

class Derived1 : public Getter<Derived1>,Setter<Derived1> {
    int value = 1;
public:    
    bool set(int p_value) {
        value = p_value;
        return true;
    }

    bool get(int & p_value) {
        p_value = value;
        return true;
    }
};

class Derived2 : public Getter<Derived1>,Setter<Derived1> {
    int value = 2;
public:
    bool set(int p_value) {
        value = p_value;
        return true;
    }

    bool get(int & p_value) {
        p_value = value;
        return true;
    }
};

最后,由于我们使用的是 CRTP,因此无需创建 std::unique_ptr。使用上述类的代码可能如下所示:

template <typename T>
void printInt(Getter<T>& model,EAttrs p_attrId) {
    int value;
    bool result = model.get(p_attrId,value);
    if (!result)
    {
        std::cout << "Failed to get attribute " << static_cast<int>(p_attrId) << "\n";
        return;
    }
    
    std::cout << "AttrID: " << static_cast<int>(p_attrId) << " Value: " << value << "\n";
}

int main()
{
    Derived1 derived1;
    Derived2 derived2;
    
    printInt(derived1,EAttrs::eAttr1);
    printInt(derived2,EAttrs::eAttr1);
}

查看DEMO

P.S. 请注意使用 enum class 而不是普通的 enum

看看 this CppCon 关于 Solid 原则的演讲。您的代码可能是应用这些原则的一个很好的例子。