I'm implementing a REST API client in c++ that needs to be able to discover the structure of the API at runtime. The structure of the REST API could change during the program execution as well. The API model is based on json arrays and objects. If an array is returned it means the retrieved element is a node and if an object is returned it means the retrieved element is a leaf.
In order to represent the data I'm modeling I decided to use the composite pattern to give a common interface to users to the tree structure (node or leaf).
And to provide hooks to my model to some GUI or CLI I decided to use the Visitor pattern in order to visit the nodes and leaves.
I tried to implement the discovery part of the model using the Visitor pattern as well but it is clearly not suited for the task because I need to replace previously allocated objects of a derived type to another type (eg: a leaf becomes a node). Which seems to me impossible using the Visitor pattern because an object can't replace itself.
I tried to add some container classes to wrap my objects but I ended up in a similar situation where I need an object to replace itself.
I read online about design patterns to try and find one that could help me out but I haven't found any that seems suitable.
Here's the code I have so far - the most relevant part is at the end (class 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()....
}
};
Is there any way I could manage to replace previously allocated objects at runtime without having to add an array of pointers to the created objects and changing them when needed?
Thanks!
Aucun commentaire:
Enregistrer un commentaire