|
Программирование >> Решение нетривиальных задач
char* на обработчиков сообщений типа метода самовывода (print() ). Я бы вывел строку при помощи: string s; s.print( cout ) или: cout << s; а не используя printf(). При этом совсем нет открытого доступа к внутреннему буферу. Функции окружения могут меньше беспокоиться о том, как хранятся символы, до тех пор, пока строковый объект правильно отвечает на сообщение о самовыводе. Вы можете менять свойства представления строки как хотите, не влияя на отправителя сообщения print() . Например, строковый объект мог бы содержать два буфера - один для строк Unicode и другой для строк char* - и обеспечивать перевод одной строки в другую. Вы могли бы даже добавить для перевода на французский язык сообщение translate to French() и получить многоязыкую строку. Такая степень изоляции и является целью объектно-ориентированного программирования, но вы ее не добьетесь, если не будете непреклонно следовать этим правилам. Здесь нет места ковбоям от программирования. 110.1. Не пользуйтесь функциями типа 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(). Проблема здесь в том, что проектирование выполнено выполнено наизнанку. Программист мыслил структурными категориями, а не объектно-ориентированными. При выполнении должным образом единственным видимым в других частях программы объектом был бы объект дата . Дата использовала бы объект календарь для реализации сообщения инициализируй себя (которое могло бы быть конструктором), но календарь бы содержался внутри даты . Определение класса календарь можно было бы даже вложить в определение класса дата . Объект дата также мог бы поддерживать другие инициализирующие сообщения, такие как инициализируй себя от редактируемого ввода или инициализируй себя из строки , но во всех случаях объект дата отвечает за нужное для инициализации взаимодействие с пользовательским интерфейсом. Остальная часть программы просто бы непосредственно использовала дату ; никто, кроме даты , даже бы не знал о существовании объекта календарь . То есть вы бы объявили дату и приказали ей себя инициализировать. Затем вы можете передавать объект дата всюду, куда необходимо. Конечно, дата должна также уметь себя вывести, переслать в файл или из файла, сравнить себя с другими датами и так далее. 111. Откажитесь от выражений языка Си, когда программируете на Си++ Многие из проблем, рассмотренных в предыдущих правилах, вызваны программистами на Си, не желающими отказаться от знакомых выражений Си при переходе на Си++. Та же самая проблема существует и в естественных языках: вам будет тяжело заставить себя понять по-французски, если вы просто переведете английские выражения в их буквальные эквиваленты. Хорошим примером этой проблемы в Си++ является char* . Большинство программистов на Си ни за что не соглашаются отказаться от использования строк в виде char* . Проблема заключается в том, что вы привыкли смотреть на char* и думать, что это строка. Это не строка. Это указатель. Убежденность в том, что указатель - это строка, обычно вызывает проблемы, некоторые из которых я уже рассматривал, а другие будут рассмотрены позднее. Симптомами этой проблемы является появление char* где-нибудь в программе, которая поддерживает класс string; вы должны делать все на языке string. Обобщим это: чтобы заставить объектно-ориентированную систему работать, все должно быть объектами. Основные типы Си не очень применимы, за исключением глубоких недр низкоуровневых функций-членов класса низкого уровня. Инкапсуляция вашего char* в классе string решит множество проблем, и потратите массу времени, пытаясь поддерживать char* , при том, что существует вполне хороший класс string, который может делать ту же работу.
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0.001
При копировании материалов приветствуются ссылки. |