How to simplify the adapter scheme?

折月煮酒 提交于 2020-05-15 19:37:06

问题


To make it clear, I post the code of "how to use" first:

AB.h:

#include <vector>
#include <list>

// execpt they both have children, A & B share nothing in common,
// even children use different containers
struct A {
  int num;
  std::vector<A> children;
};

struct B {
  std::string s;
  float n;
  std::list<B> children;
};

main.cpp:

#include "Node.h"

// handle A & B with the common interface Node
// do not use template, since in practice, print() may be a large library
void print(Node& n) {
  printf("%s\n", n.tell().c_str());
  for (auto& c : n.children()) {
    print(c);
  }
}

////////////////////////////////////////////////////////////////////////////////
int main() {
  A a { .num = 3, .children = { { .num = 7 }, { .num = 8 } } };
  NodeA na(&a); // wrap A with NodeA adapter
  print(na);

  B b {
    .s = "Will", .n = 1,
    .children = { { .s = "Ryu", .n = 5 }, { .s = "Ken", .n = 8 } }
  };
  NodeB nb(&b); // wrap B with NodeB adapter
  print(nb);
}

Compile & run:

WilldeMacBook-Pro:testNodes Will$ cc -std=c++11 -lc++ main.cpp Node.cpp
WilldeMacBook-Pro:testNodes Will$ ./a.out
3
7
8
Will1.000000
Ryu5.000000
Ken8.000000

It really worked, handle distinct A & B in a universal way, little runtime overhead, and... straightforward & simple... until you see the Node.h:

#include <memory>
#include <sstream>
#include "AB.h"

////////////////////////////////////////////////////////////////////////////////
struct Node;
struct Nodes;
struct NodeIter;
struct NodeIterPtr;

struct Node { // the only interface i care about, other three are "have-to" ones
  virtual std::string tell() = 0;
  virtual Nodes& children() = 0;
};

struct Nodes {
  virtual NodeIterPtr begin() = 0;
  virtual NodeIterPtr end() = 0;
};

struct NodeIter {
  virtual ~NodeIter() {}
  virtual Node& operator*() = 0;
  virtual NodeIter& operator++() = 0;
  virtual bool operator!=(const NodeIter& other) = 0;
};

struct NodeIterPtr {
  NodeIterPtr(NodeIter* p) : ptr(p) {}

  Node& operator*() { return (*ptr).operator*(); }
  NodeIter& operator++() { return (*ptr).operator++(); }
  bool operator!=(const NodeIterPtr& other) {
    return (*ptr).operator!=(*other.ptr);
  }

private:
  std::shared_ptr<NodeIter> ptr;
};

////////////////////////////////////////////////////////////////////////////////
// adapter for A
struct NodeA : public Node {
  NodeA(A* p) : ptr(p) {}
  std::string tell() { return std::to_string(ptr->num); }
  Nodes& children();

private:
  A* ptr;
};

struct NodesA : public Nodes {
  NodesA(std::vector<A>* ns) : nodes(ns) {}

  NodeIterPtr begin();
  NodeIterPtr end();

private:
  std::vector<A>* nodes;
};

struct NodeIterA : public NodeIter {
  NodeIterA(A* p) : ptr(p) {}

  Node& operator*() { static NodeA a(nullptr); a = NodeA(ptr); return a; }
  NodeIter& operator++() { ptr++; return *this; }
  bool operator!=(const NodeIter& other) {
    auto realOther = (const NodeIterA&)(other);
    return ptr != realOther.ptr;
  }

private:
  A* ptr;
};

////////////////////////////////////////////////////////////////////////////////
// adapter for B
struct NodeB : public Node {
  NodeB(B* p) : ptr(p) {}
  std::string tell() { return ptr->s + std::to_string(ptr->n); }
  Nodes& children();

private:
  B* ptr;
};

struct NodesB : public Nodes {
  NodesB(std::list<B>* ns) : nodes(ns) {}

  NodeIterPtr begin();
  NodeIterPtr end();

private:
  std::list<B>* nodes;
};

struct NodeIterB : public NodeIter {
  NodeIterB(std::list<B>::iterator i) : iter(i) {}

  Node& operator*() { static NodeB b(nullptr); b = NodeB(&(*iter)); return b; }
  NodeIter& operator++() { iter++; return *this; }
  bool operator!=(const NodeIter& other) {
    auto realOther = (const NodeIterB&)(other);
    return iter != realOther.iter;
  }

private:
  std::list<B>::iterator iter;
};

And the Node.cpp:

#include "Node.h"

Nodes& NodeA::children() {
  static NodesA c(nullptr);
  c = NodesA(&ptr->children);
  return c;
}

NodeIterPtr NodesA::begin() {
  auto iter = new NodeIterA(&(*nodes->begin()));
  return NodeIterPtr(iter);
}

NodeIterPtr NodesA::end() {
  auto iter = new NodeIterA(&(*nodes->end()));
  return NodeIterPtr(iter);
}

////////////////////////////////////////////////////////////////////////////////
Nodes& NodeB::children() {
  static NodesB c(nullptr);
  c = NodesB(&ptr->children);
  return c;
}

NodeIterPtr NodesB::begin() {
  auto iter = new NodeIterB(nodes->begin());
  return NodeIterPtr(iter);
}

NodeIterPtr NodesB::end() {
  auto iter = new NodeIterB(nodes->end());
  return NodeIterPtr(iter);
}

I really think it's too complicated, to achieve such a simple goal. (If A & B have more than one container member, such as students/employees/tickets, That would be like hell.) Were C++ designed to do so? I even come to some bizarre ideas of returning a base class reference instead of a base class pointer. Anyway, what I want to ask is, is there a way to simplify above code? As the saying goes, "talk is cheap, show me the code", before talking any philosophy, I prefer to see code that can be directly compiled & run, same output, same simple way of usage, but with reduced complexity of Node.h/cpp. Thanks.

来源:https://stackoverflow.com/questions/61812074/how-to-simplify-the-adapter-scheme

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!