如何解决如何管理基类中不同类型的数据?
我的目标是将数据与各种实现分开。我不希望我的东西知道它们正在使用的实际子类,无论哪种方式。用最少的信息让事物只执行一项任务。
我会先往你的眼睛里扔一些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 问题,但您可以通过以下方式稍微美化您的代码。首先,我实现了两个接口:Getter
和 Setter
,例如:
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 原则的演讲。您的代码可能是应用这些原则的一个很好的例子。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。