Я реализую REST API-клиент в c ++, который должен уметь обнаруживать структуру API во время выполнения. Структура REST API также может измениться во время выполнения программы. Модель API основана на json массивах и объектах. Если массив возвращается, это означает, что извлеченный элемент является узлом, а если объект возвращается, это означает, что извлеченный элемент является листом.
Чтобы представить данные, которые я моделирую, я решил использовать составной шаблон, чтобы дать общий интерфейс для пользователей к древовидной структуре (узел или лист).
И для обеспечения привязок к моей модели некоторым GUI или CLI, я решил использовать шаблон Visitor для посещения узлы и листья.
Я попытался реализовать часть модели обнаружения с использованием шаблона Visitor, но он явно не подходит для этой задачи, поскольку мне нужно заменить ранее выделенные объекты производного типа на другой тип. (например: лист становится узлом). Это кажется мне невозможным при использовании шаблона Visitor, потому что объект не может заменить сам себя.
Я попытался добавить несколько контейнерных классов, чтобы обернуть свои объекты, но я оказался в аналогичной ситуации, когда мне нужен объект для замены сам по себе.
Я читал в Интернете о шаблонах проектирования, чтобы попытаться найти тот, который мог бы мне помочь, но я не нашел ни одного, который кажется подходящим.
Вот код, который я имею до сих пор - самая важная часть находится в конце (класс Discover):
#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()....
}
};
Есть ли какой-нибудь способ, которым я мог бы заменить ранее выделенные объекты во время выполнения без необходимости добавлять массив указателей на созданные объекты и изменять их когда нужно?
Спасибо!