Структурирование иерархий классов C ++ для удобства обслуживания и инкапсуляции

У меня есть несколько общих вопросов об инкапсуляции, связанной с ремонтопригодностью. Вот пример класса, который я использовал для помощи в построении дерева синтаксического анализа. (Я избегал STL ради образования.)

Класс Node описывает узел в дереве. Управляющий класс ParseTree (не показан) отвечает за создание и поддержание коллекции Node объектов осмысленным древовидным способом.

// 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_;
};

Следующие четыре темы относятся к инкапсуляции.

  1. Статические методы в классе Node объявлены статическими, потому что их можно сформулировать без использования каких-либо закрытых членов. Мне интересно, должны ли они жить вне Node в общем пространстве имен или, может быть, как статические члены в ParseTree. Должно ли мое решение зависеть от того факта, что ParseTree отвечает за деревья, и по этой логике функции должны находиться в ParseTree?

  2. В связи с этим, причина, по которой статические методы находятся в Node вместо ParseTree, заключалась в том, что ParseTree заполнялся большим количеством членов. Я читал, что небольшой размер класса и маневренность лучше для ремонтопригодности. Должен ли я изо всех сил искать методы, которые не полагаются на доступ к частным членам, и вытаскивать их из определения моего класса и помещать их в функции, сгруппированные в том же пространстве имен, что и класс?

  3. Я также читал несколько советов по тому, как избегать мутаторов для частных членов, поскольку они имеют тенденцию нарушать инкапсуляцию, поэтому в итоге у меня остались только аксессоры, и я позволил ParseTree обрабатывать любые модификации, используя его дружбу с Node. Неужели это лучше, чем иметь мутаторы и просто прекратить дружбу с ParseTree? Если я добавлю мутаторы, то Node можно будет повторно использовать в других контекстах без добавления еще одной дружбы.

  4. Если я добавлю мутаторы и удалю статические функции из Node, я чувствую, что могу просто сделать члены данных общедоступными и удалить все объявления аксессоров / мутаторов / друзей. У меня сложилось впечатление, что такой подход был бы дурным тоном. Следует ли мне скептически относиться к своему дизайну, если у меня есть пары аксессуаров / мутаторов для каждого закрытого члена?

  5. Если в моем подходе есть еще что-то очевидное и неправильное, о чем я не подумал бы спросить, я был бы признателен, если бы об этом услышал.


person Jordan Gray    schedule 10.08.2011    source источник
comment
Ваши аргументы ходят по кругу, что и происходит со мной, когда я думаю о такого рода проблемах. Я бы не стал слишком об этом беспокоиться, ты выглядишь так, будто знаешь, что делаешь. Но очень быстро 1) Я не думаю, что они должны быть в Node. 2) Да, глобальные функции. 3) Я не вижу большого преимущества в повторном использовании Node, в этом нет ничего особенного, держите его привязанным к ParseTree. 4) Нет, потому что для чего-то столь же простого, как Node, все может быть общедоступным.   -  person john    schedule 11.08.2011
comment
«Мутатор», господи, что не так с сеттерами и геттерами?   -  person James    schedule 11.08.2011
comment
Я провел большую часть последних нескольких лет, выполняя низкоуровневую работу (дизайн IC, hdl, часть встроенного C). Мой близкий друг всегда использовал для обозначения аксессоров и мутаторов, когда описывал мне объектно-ориентированный дизайн, и это то, что у меня в голове, когда я думаю о публичных функциях-членах, которые взаимодействуют с частными членами.   -  person Jordan Gray    schedule 11.08.2011


Ответы (2)


Спросите себя, что такое узел? Ясно, что это что-то, что может иметь родителя, левого и правого дочерних элементов. Он также содержит указатель на некоторые данные. У узла есть высота? Это зависит от контекста, возможно ли, что ваши узлы в какой-то момент могут стать частью цикла? ParseTree имеет понятие высоты, но, похоже, узел не имеет.

Честно говоря, я предлагаю вам сначала исправить логику вашей программы, а затем вы можете беспокоиться о наворотах и ​​свистках объектно-ориентированного программирования. Вопросы, которые вы задаете, вероятно, дадут ответы сами по себе, когда вы продолжите.

person James    schedule 11.08.2011
comment
Спасибо, вопросы, которые вы задаете, привели меня к полезному мышлению. Тем не менее, я закончил свою программу, но я не чувствовал, что моя организация подходит. И у меня еще нет хорошо развитой интуиции или набора вопросов, которые я мог бы задать себе для организации своего кода. Вдобавок я не взялся за достаточно большую проблему, чтобы увидеть, как мой наивный дизайн повредит моей реализации. - person Jordan Gray; 11.08.2011

Я думаю, что Node слишком переполнен этими аксессуарами, которые, по-видимому, являются просто косвенным способом раскрытия ваших частных членов. Я думаю, что удаление этих статических членов в пространство имен приложения было бы немного чище. Например:

namespace mycompiler {
    class Node {
        ...
    };

    class ParseTree {
        ...
    };

    const Node* FindParent(...);
    int Height(...);
    void Print(...);
}

Таким образом вы по-прежнему можете избежать загрязнения глобального пространства имен, но в то же время сохраните ваши классы Node и ParseTree меньшего размера. Вы также можете перегрузить некоторые mycompiler:: функции (например, Print()), чтобы принять любой объект из вашего пространства имен, если вы не хотите вставлять их в свои классы. Это сделало бы Node и ParseTree более интеллектуальными контейнерами, в то время как некоторая внешняя логика (для соответствующих классов) могла бы быть изолирована в mycompiler::.

person Kenji    schedule 11.08.2011