Правила программирования на Си и Си++

       

Не пользуйтесь функциями типа get/set (чтения и присваивания значений)


Это правило в действительности то же, что и предыдущее "все данные должны быть закрытыми". Я выделил его, потому что есть такая распространенная ошибка среди начинающих программистов на Си++. Нет разницы между:

struct xxx

{

   int x;

};

и:

class xxx {

private:

   int x;

public:

   void setx ( int ix ){ x = ix;      }

   int  getx ( void )  { return

x; }

}

за исключением той, что второй вариант труднее читать. Просто сделать данные закрытыми недостаточно: вам нужно изменить образ мыслей. Подведем итог по нескольким упомянутым ранее пунктам:

· Сообщение реализует свойство. Открытая (public) функция реализует обработчик сообщения. Поля данных —

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

Заметьте, что вы будете изредка видеть обработчик сообщений, который ничего не делает, кроме возврата содержимого поля или помещает в поле значение, переданное в виде аргумента. Этот обработчик тем не менее не является функцией типа get/set. Вопрос в том, как возникает такая ситуация. Нет абсолютно ничего плохого в том, если вы начинаете с ряда сообщений и затем решаете, что самым простым способом реализации сообщения является помещение специального поля в определение класса. Другими словами, этот обработчик сообщений не является усложненным способом доступа к полю; скорее, это поле является простым способом реализовать сообщение. Хотя вы попали в то же место, вы попали туда совершенно другим путем.

Конечно, эта организация означает, что Си++ не может быть эффективно использован в гибридной среде Си/Си++, потому что интерфейс между двумя половинами программы уничтожает инкапсуляцию, которой вы так сильно старались добиться. В известном смысле жаль, что Си++ создан на основе Си, потому что это просто подстрекает нас к ошибкам.

Закончу этот раздел более реальным примером. Как-то раз я видел интерфейс, в котором объект "календарь" позволял пользователю интерактивно выбирать дату, щелкая мышью на каком-либо из дней, показанных на изображении календаря. "Календарь" затем экспортирует эту дату в другие части программы, помещая ее в объект "дата",


который возвращается из сообщения get_date(). Проблема здесь в том, что проектирование выполнено выполнено наизнанку. Программист мыслил структурными категориями, а не объектно-ориентированными.
При выполнении должным образом единственным видимым в других частях программы объектом был бы объект "дата". "Дата" использовала бы объект "календарь" для реализации сообщения "инициализируй_себя"
(которое могло бы быть конструктором), но "календарь" бы содержался внутри "даты". Определение класса "календарь" можно было бы даже вложить в определение класса "дата". Объект "дата" также мог бы поддерживать другие инициализирующие сообщения, такие как "инициализируй_себя_от_редактируемого_ввода" или "инициализируй_себя_из_строки", но во всех случаях объект "дата" отвечает за нужное для инициализации взаимодействие с пользовательским интерфейсом. Остальная часть программы просто бы непосредственно использовала "дату"; никто, кроме "даты", даже бы не знал о существовании объекта "календарь". То есть вы бы объявили "дату" и приказали ей себя инициализировать. Затем вы можете передавать объект "дата" всюду, куда необходимо. Конечно, "дата" должна также уметь себя вывести, переслать в файл или из файла, сравнить себя с другими датами и так далее.

Содержание раздела