c++ - 树结构中的运行时元素替换
问题描述
我正在用 C++ 实现一个 REST API 客户端,它需要能够在运行时发现 API 的结构。REST API 的结构也可能在程序执行期间发生变化。API 模型基于 json 数组和对象。如果返回一个数组,则意味着检索到的元素是一个节点,如果返回一个对象,则意味着检索到的元素是一个叶子。
为了表示我正在建模的数据,我决定使用复合模式为用户提供树结构(节点或叶)的通用接口。
为了给我的模型提供一些 GUI 或 CLI 的钩子,我决定使用访问者模式来访问节点和叶子。
我也尝试使用访问者模式来实现模型的发现部分,但它显然不适合该任务,因为我需要将先前分配的派生类型的对象替换为另一种类型(例如:叶子变成节点)。在我看来,使用访问者模式是不可能的,因为对象不能替换自己。
我试图添加一些容器类来包装我的对象,但我最终遇到了类似的情况,我需要一个对象来替换自己。
我在网上阅读了有关设计模式的信息,试图找到一种可以帮助我的方法,但我没有找到任何合适的方法。
这是我到目前为止的代码 - 最相关的部分在最后(发现类):
#include <string>
#include <deque>
#include <stack>
#include <iostream>
class Visitor;
class Element{
public:
Element(std::string name, Element* parent) : name{ name }, parent{ parent }{}
std::string get_name(){
return name;
}
std::string get_url(){
std::stack<std::string> url_stack;
Element* ancestor = parent;
url_stack.push(name);
while(ancestor != NULL){
url_stack.push(ancestor->name);
ancestor = ancestor->parent;
}
std::string url;
while(!url_stack.empty()){
url.append(url_stack.top());
url_stack.pop();
}
return url;
}
Element* get_parent(void){
return parent;
}
virtual ~Element(void){};
virtual void accept(Visitor& visitor) = 0;
private:
std::string name;
Element* parent;
};
class ElementEmpty : public Element{
public:
ElementEmpty(std::string name, Element* parent = NULL) : Element(name, parent){}
virtual void accept(Visitor& visitor);
};
class ElementLeaf : public Element{
public:
ElementLeaf(std::string name, std::string value, Element* parent = NULL) : Element(name, parent), value{ value }{}
virtual void accept(Visitor& visitor);
std::string value;
};
class ElementComposite : public Element{
public:
ElementComposite(std::string name, std::deque<std::string> value_names, Element* parent = NULL) : Element(name, parent){
for(auto& vn : value_names){
values.emplace_back(new ElementEmpty(vn, this));
}
}
~ElementComposite(){
while(!values.empty()){
delete values.back();
values.pop_back();
}
}
virtual void accept(Visitor& visitor);
std::deque<Element*> values;
};
class Visitor{
public:
virtual void visit(ElementEmpty& empty) = 0;
virtual void visit(ElementLeaf& leaf) = 0;
virtual void visit(ElementComposite& composite) = 0;
};
void ElementEmpty::accept(Visitor& visitor){
visitor.visit(*this);
}
void ElementLeaf::accept(Visitor& visitor){
visitor.visit(*this);
}
void ElementComposite::accept(Visitor& visitor){
visitor.visit(*this);
}
class Printer : public Visitor{
public:
virtual void visit(ElementEmpty& empty){
std::cout << empty.get_name() << " is empty" << std::endl;
}
virtual void visit(ElementLeaf& leaf){
std::cout << leaf.get_name() << " is a leaf with value" << leaf.value << std::endl;
}
virtual void visit(ElementComposite& composite){
std::cout << composite.get_name() << " is composite, visiting childrens" << std::endl;
for(auto& v : composite.values){
v->accept(*this);
}
}
};
class Discover : public Visitor{
public:
virtual void visit(ElementEmpty& empty){
discover(&empty);
}
virtual void visit(ElementLeaf& leaf){
discover(&leaf);
}
virtual void visit(ElementComposite& composite){
discover(&composite);
for(auto& v : composite.values){
v->accept(*this);
}
}
private:
void discover(Element* element){
std::string url = element->get_url();
//HTTP GET (pseudo-code here to avoid adding irelevant code)
json_val value = http_get(url);
Element* new_element;
if(value.is_array()){
new_element = new ElementComposite(element->get_name(), value.as_array().as_deque(), element->get_parent());
}
else if(value.is_object()){
new_element = new ElementLeaf(element->get_name(), value.as_string(), element->get_parent());
}
else{
new_element = new ElementEmpty(element->get_name(), element->get_parent());
}
//TODO: How to set element to new_element? it's impossible since we are currently in element->accept()....
}
};
有什么方法可以在运行时替换以前分配的对象,而不必添加指向已创建对象的指针数组并在需要时更改它们?
谢谢!
解决方案
对于您在提供的代码中分配的所有元素,您可以通过调用 new.
对于 discover() 可能被调用的所有元素,这是否也适用?
如果是这样,在某处调用的根源,将是该元素的真正所有者。例如,改变 discover() 的接口怎么样,让它返回一个指向新元素的指针,一直沿着调用链向下到达旧元素的真正所有者?然后该所有者可以删除旧元素,并开始将指向新分配的元素的指针存储在其位置。
当然,通常 - 不要使用原始指针,使用 std::unique_ptr 或 std::shared_ptr 或其他任何合适的东西,等等。但上述代码转换仍然可以使用这些。
推荐阅读
- c++ - 找到最高值并访问二维数组中的值后如何实现计数器?
- string - 字符串值需要在oracle中用引号替换
- ansible - 比较 ansible 中的挂载
- powershell - 使用PowerShell从Outlook下载时如何修复损坏的文件
- java - Java中带括号的字节码大小与不带括号的字节码大小
- typescript - TypeScript:如何使用 Object.entries 向此函数添加正确的类型
- c# - 如何使用 C# 在 PC 上模拟 PS4 控制器的操纵杆?
- python - 如何让我的函数打印我的代码?
- xml - 重写声明,使顺序不再重要
- matlab - 边缘函数如何在边缘函数中自动计算阈值?