C++ 结构C++;用于可维护性和封装的类层次结构

C++ 结构C++;用于可维护性和封装的类层次结构,c++,data-structures,coding-style,encapsulation,C++,Data Structures,Coding Style,Encapsulation,我有一些关于封装的一般性问题,因为它与可维护性有关。下面是一个示例类,我用来帮助构建解析树。(为了教育,我避免了STL。) 节点类描述树中的节点。管理类ParseTree(未显示)负责以有意义的树状方式构建和维护节点对象的集合 // contents of node.h, not including header guard or namespace class Token; class Node { public: static const Node* FindParent(const N

我有一些关于封装的一般性问题,因为它与可维护性有关。下面是一个示例类,我用来帮助构建解析树。(为了教育,我避免了STL。)

节点
类描述树中的节点。管理类
ParseTree
(未显示)负责以有意义的树状方式构建和维护
节点
对象的集合

// contents of node.h, not including header guard or namespace
class Token;
class Node {
public:
  static const Node* FindParent(const Node* p_root, const Node* p_node);
  static int Height(const Node* p_root);
  static void Print(const Node* p_root);
  Node(const Token * p_tok=0) : p_left_(0), p_right_(0), p_tok_(p_tok) {}
  ~Node() { delete p_left_; delete p_right_; }
  const Node* p_left(void) const { return p_left_; }
  const Node* p_right(void) const { return p_right_; }
  const Token* p_tok(void) const { return p_tok_; }
private:
  friend class ParseTree;
  Node* p_left_;
  Node* p_right_;
  Token* p_tok_;
};
以下四个主题与封装相关

  • 节点
    类中的静态方法被声明为静态的,因为它们的措辞可以不使用任何私有成员。我想知道它们是否应该位于
    节点
    之外的公共名称空间中,或者作为
    解析树
    中的静态成员。我的决定是否应该由
    ParseTree
    负责树这一事实决定,并且根据这一逻辑,函数应该存在于
    ParseTree

  • 另一方面,静态方法位于
    Node
    而不是
    ParseTree
    中的原因是
    ParseTree
    中充满了大量成员。我已经读到,保持类的小型化和敏捷性对于可维护性更好。我是否应该不厌其烦地寻找不依赖私有成员访问的方法,并将它们从类定义中拉出来,放入与类在同一命名空间中分组的函数中

  • 我还阅读了一些关于避免私有成员上的变异的建议,因为它会破坏封装,所以我最终只拥有访问器,并让
    ParseTree
    使用它与
    节点的友好关系来处理任何修改。这真的比有了变异子就结束了与
    ParseTree
    的友谊要好吗?如果我添加了变异体,那么
    节点
    可以在其他上下文中重用,而无需添加另一个好友

  • 如果我在
    节点
    中添加了mutator并删除了静态函数,我觉得我可以将数据成员公开并删除所有访问器/mutator/友元声明。我的印象是,这种做法形式不好。如果每个私有成员都有访问器/变异器对,我是否应该怀疑我的设计

  • 如果我的方法还有什么明显的错误,我不想问,我很乐意听到

  • 我认为Node中的这些访问器有点太多了,这显然只是一种间接地公开私有成员的方式。我认为将这些静态成员删除到应用程序名称空间会更干净一些。例如:

    namespace mycompiler {
        class Node {
            ...
        };
    
        class ParseTree {
            ...
        };
    
        const Node* FindParent(...);
        int Height(...);
        void Print(...);
    }
    

    这样,您仍然可以避免污染全局名称空间,但同时保持节点和ParseTree类更小。您还可以重载一些
    mycompiler::
    函数(例如
    Print()
    )来接受命名空间中的任何对象,如果您不想将它们粘贴到类中的话。这将使Node和ParseTree更加智能化,而一些外部逻辑(到相关类)可以在
    mycompiler::

    中隔离,问问自己,什么是节点?显然,它可能有一个父母,一个左撇子和一个右撇子。它还保存指向某些数据的指针。节点是否具有高度?这取决于上下文,您的节点是否可能在某个点上成为循环的一部分?ParseTree有高度的概念,而节点似乎没有

    老实说,我建议您先纠正程序逻辑,然后再考虑OO的问题。
    你所问的问题可能会在你继续的过程中自动得到答案。

    你的论点在循环,这就是我在思考这些问题时所遇到的情况。我不会太担心的,你看起来知道你在做什么。但很快1)我认为他们不应该在节点中。2) 是的,全局函数。3) 我认为重用节点并没有什么好处,若将其绑定到ParseTree,那个么它就并没有什么价值了。4) 不,因为对于像节点这样简单的东西,一切公开都是可能的。“Mutator”,耶稣,setter和getter怎么了?过去几年的大部分时间我都在做底层工作(IC设计,hdl,一点嵌入式C)。我的一位密友在向我描述面向对象设计时,总是提到访问器和变异器,所以当我想到与私有成员交互的公共成员函数时,这就是我脑海中的想法。谢谢,你提出的问题引导了我一条有用的思路。也就是说,我完成了我的计划,但我觉得我的组织不合适。我还没有一个完善的直觉,也没有一套组织代码的问题。此外,我还没有承担一个足够大的问题,看到我天真的设计破坏了我的实现。