Подстановка элементов времени выполнения в древовидной структуре - PullRequest
0 голосов
/ 16 февраля 2020

Я реализую 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()....
  }

};

Есть ли какой-нибудь способ, которым я мог бы заменить ранее выделенные объекты во время выполнения без необходимости добавлять массив указателей на созданные объекты и изменять их когда нужно?

Спасибо!

1 Ответ

1 голос
/ 16 февраля 2020

Для всех Элементов, которые вы выделяете в предоставленном коде, вы делаете это динамически, вызывая new.

Будет ли это также верно для ВСЕХ элементов, которые могут вызываться для Discover ()?

Если это так, то при root звонке где-то будет истинный владелец этого Элемента. Как насчет изменения интерфейса Discover (), например, чтобы он возвращал указатель на новый элемент, по всей цепочке вызовов истинному владельцу старого элемента? Затем этот владелец может просто удалить старый элемент и начать хранить указатель на недавно выделенный элемент на его месте.

Конечно, обычно - не используйте необработанные указатели, используйте std :: unique_ptr или std :: shared_ptr, или что еще подходит, et c. Но вышеупомянутое преобразование кода все еще возможно с теми же.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...