Б. Страуструп - Язык программирования С++ (1119446), страница 80
Текст из файла (страница 80)
Такие функции снижают уровеньабстракции от понятия до его конкретной реализации. К тому же добавление функций добавляетработы программисту и даже разработчику, когда он вернется к проектированию. Гораздо легчевключить в интерфейс еще одну функцию, как только установлена потребность в ней, чем удалить ееоттуда, когда уже она стала привычной.Причина, по которой мы требуем явного принятия решения о виртуальности данной функции, неоставляя его на стадию реализации, в том, что, объявив функцию виртуальной, мы существенно293Бьерн Страуструп.Язык программирования С++повлияем на использование ее класса и на взаимоотношения этого класса с другими. Объекты изкласса, имеющего хотя бы одну виртуальную функцию, требуют нетривиального распределенияпамяти, если сравнить их с объектами из таких языков как С или Фортран.
Класс с хотя бы однойвиртуальной функцией по сути выступает в роли интерфейса по отношению к классам, которые "ещемогут быть определены", а виртуальная функция предполагает зависимость от классов, которые "ещемогу быть определены" (см. $$12.2.3)Отметим, что стратегия минимализма требует, пожалуй, больших усилий со стороны разработчика.При определении набора операций больше внимания следует уделять тому, что надо сделать, а нетому, как это делать.Иногда полезно классифицировать операции класса по тому, как они работают с внутреннимсостоянием объектов:-Базовые операции: конструкторы, деструкторы, операции копирования.-Селекторы: операции, не изменяющие состояния объекта.-Модификаторы: операции, изменяющие состояние объекта.-Операции преобразований, т.е. операции порождающие объект другого типа, исходя иззначения (состояния) объекта, к которому они применяются.-Повторители: операции, которые открывают доступ к объектам класса или используютпоследовательность объектов.Это не есть разбиение на ортогональные группы операций.
Например, повторитель может бытьспроектирован как селектор или модификатор. Выделение этих групп просто предназначено помочь впроцессе проектирования интерфейса класса. Конечно, допустима и другая классификация.Проведение такой классификации особенно полезно для поддержания непротиворечивости междуклассами в рамках одного компонента.В языке С++ есть конструкция, помогающая заданию селекторов и модификаторов в виде функциичлена со спецификацией const и без нее.
Кроме того, есть средства, позволяющие явно задатьконструкторы, деструкторы и функции преобразования. Операция копирования реализуется с помощьюопераций присваивания и конструкторов копирования.11.3.3.3 Шаг 3: указание зависимостейУточните определение классов, указав их зависимости от других классов. Различные видызависимостей обсуждаются в $$12.2. Основными по отношению к проектированию следует считатьотношения наследования и использования. Оба предполагают понимание того, что значит для классаотвечать за определенное свойство системы. Отвечать за что-либо не означает, что класс долженсодержать в себе всю информацию, или, что его функции-члены должны сами проводить всенеобходимые операции.
Как раз наоборот, каждый класс, имеющий определенный уровеньответственности, организует работу, перепоручая ее в виде подзадач другим классам, которые имеютменьший уровень ответственности. Но надо предостеречь, что злоупотребление этим приемомприводит к неэффективным и плохо понимаемым проектам, поскольку происходит размножениеклассов и объектов до такой степени, что вместо реальной работы производится только серия запросовна ее выполнение.
То, что можно сделать в данном месте, следует сделать.Необходимость учесть отношения наследования и использования на этапе проектирования (а не тольков процессе реализации) прямо вытекает из того, что классы представляют определенные понятия.Отсюда также следует, что именно компонент (т.е. множество связанных классов), а не отдельныйкласс, являются единицей проектирования.11.3.3.4 Шаг 4: определение интерфейсовОпределите интерфейсы классов.
На этой стадии проектирования не нужно рассматривать приватныефункции. Вопросы реализации, возникающие на стадии проектирования, лучше всего обсуждать нашаге 3 при рассмотрении различных зависимостей. Более того, существует золотое правило: есликласс не допускает по крайней мере двух существенно отличающихся реализаций, то что-то явно не в294Бьерн Страуструп.Язык программирования С++порядке с этим классом, это просто замаскированная реализация, а не представление абстрактногопонятия.
Во многих случаях для ответа на вопрос: "Достаточно ли интерфейс класса независим отреализации?"- надо указать, возможна ли для класса схема ленивых вычислений.Отметим, что общие базовые классы и друзья (friend) являются частью общего интерфейса класса (см.$$5.4.1 и $$12.4). Полезным упражнением может быть определение раздельного интерфейса дляклассов-наследников и всех остальных классов с помощью разбиения интерфейса на общую изакрытые части.Именно на этом шаге следует продумать и описать точные определения типов аргументов. В идеалежелательно иметь максимальное число интерфейсов со статическими типами, относящимися к областиприложения (см.
$$12.1.3 и $$12.4).При определении интерфейсов следует обратить внимание на те классы, где набор операцийпредставлен более, чем на одном уровне абстракции. Например, в классе file у некоторых функцийчленов аргументы имеют тип file_descriptor (дескриптор_файла), а у других аргументы - строкасимволов, которая обозначает имя файла. Операции с file_descriptor работают на другом уровне(меньшем) абстракции, чем операции с именем файла, так что даже странно, что они относятся кодному классу.
Возможно, было бы лучше иметь два класса: один представляет понятие дескрипторафайла, а другой - понятие имени файла. Обычно все операции класса должны представлять понятияодного уровня абстракции. Если это не так, то стоит подумать о реорганизации и его, и связанных с нимклассов.11.3.3.5 Перестройка иерархии классовШаги 1 и 3 требуют исследования классов и их иерархии, чтобы убедиться, что они адекватно отвечаютнашим требованиям. Обычно это не так, и приходится проводить перестройку для улучшенияструктуры, проекта или реализации.Самая типичная перестройка иерархии классов состоит в выделении общей части двух классов в новыйкласс или в разбиении класса на два новых.
В обоих случаях в результате получится три класса:базовый класс и два производных. Когда следует проводить такую перестройку? Каковы общиепоказания, что такая перестройка будет полезной?К сожалению нет простого и универсального ответа на эти вопросы. Это и не удивительно, посколькуто, что предлагается, не является мелочью при реализации, а изменяет основные понятия системы.Важной и нетривиальной задачей является поиск общности среди классов и выделение общей части.Нет точного определения общности, но следует обращать внимание на общность для понятий системы,а не просто для удобства реализации.
Указаниями, что два класса имеют нечто общее, что возможновыделить в общий базовый класс, служат схожие способы использования, сходство наборов операций,сходство реализаций и просто тот факт, что часто в процессе обсуждения проекта обаклассапоявляются одновременно. С другой стороны, если есть несколько наборов операций класса сразличными способами использования, если эти наборы обеспечивают доступ к раздельнымподмножествам объектов реализации, и, если класс возникает в процессе обсуждения несвязанныхтем, то этот класс является явным кандидатом для разбиения на части.В силу тесной связи между понятиями и классами проблемы перестройки иерархии классоввысвечиваются на поверхности проблем именования классов и использования имен классов в процессеобсуждения проекта.
Если имена классов и их упорядоченность, задаваемая иерархией классов,кажутся неудобными при обсуждении проекта, значит, по всей видимости, есть возможность улучшенияиерархии. Заметим, что подразумевается, что анализ иерархии классов лучше проводить не в одиночку.Если вы оказались в таком положении, когда не с кем обсудить проект, хорошим выходом будетпопытаться составить учебное описание системы, используя имена классов.11.3.3.6 Использование моделейКогда пишешь статью, пытаешься найти подходящую для темы модель.