Дж. Рамбо, М. Блаха - UML 2.0 - Объектно-ориентированное моделирование и разработка (1158633), страница 105
Текст из файла (страница 105)
В результате применения описанного правила программы получаются меньше по объему, а их отладка значительно ускоряется. Планирование повторного использования в будущем схоже с инвестированием и требует предвидения. Маловероятно, что отдельный класс будет использоваться в нескольких приложениях. Более вероятно использование тщательно продуманных подсистем, таких как абстрактные типы данных, графические пакеты ц библиотеки численного анализа. В этом отношении могут оказаться полезными образцы и каркасы (см. главу 14).
20.2.2. Правила хорошего стиля для повторного использования Собранные в этом разделе правила помогут вам повысить вероятность повторного использования в рамках одного приложения и между разными приложениями. ° Методы должны быть цельными. Метод считается цельным, если он выполняет одно действие или группу тесно связанных действий. Если он выполняет два несвязанных действия, разбейте его на два метода. ° Методы должны быть небольшими.
Если метод получается слишком большим, разбейте его на более простые. Большим можно считать метод, размер которого превышает одну-две страницы кода. Разбивая метод на части, вы можете добиться их повторного использования даже в том случае, если весь метод в целом вам больше никогда не пригодится. ° Методы должны быть согласованными. Схожие методы должны иметь схожие названия, порядок аргументов, типы данных, возвращаемые значения и ошибочные ситуации.
Везде, где это возможно, стремитесь обеспечивать параллельность. Методы одной операции должны иметь согласованную сигнатуру и семантику. 442 Глава 20 ° Стиль программирования В операционной системе 13Х1Х имеется множество примеров несогласованности функций. Например, в библиотеке С есть две несогласованные функции для вывода строк: ри«з и ~ригз. Первая записывает строку в стандартный поток вывода и заканчивает ее символом перевода строки. Вторая записывает строку в указанный файл, но не завершает ее символом перевода строки. Старайтесь избегать подобных несоответствий. ° Отделяйте политику от реализации. «Политические» методы принимают решения, обрабатывают аргументы и воплощают общий контекст.
Эти методы переключают управление между реализующими методами. Политические методы должны осуществлять проверку состояния и контроль ошибок, но они не должны непосредственно выполнять вычисления или реализовывать сложные алгоритмы. Политические методы часто зависят от приложения, но их легко писать и читать. Реализующие методы содержат конкретную детализированную логику, но не принимают решений о необходимости выполнения каких-либо действий. Если в таком методе возникает ошибочная ситуация, он должен только возвратить сообщение об ошибке, но не обрабатывать ее. Реализующие методы выполняют конкретные расчеты и часто содержат сложные алгоритмы.
Им не нужен доступ к глобальному контексту, они не должны принимать решения, содержать значения по умолчанию или переключать поток управления. Поскольку суть реализующих методов сводится к автономным алгоритмам, они часто оказываются полезными в различных контекстах. Не объединяйте политику и реализацию в одном методе.
Изолируйте ядро алгоритма в отдельный реализующий метод. Для этого требуется абстрагирование параметров пз политического метода и передача их в виде аргументов реализующему методу. Например, метод масштабирования окна в два раза — это политический метод. Он должен установить коэффициент масштабирования окна и вызвать реализующий метод, который масштабирует окно на произвольный коэффициент.
Если впоследствии вы решите изменить масштабный коэффициент по умолчанию на какой-либо другой, например на Еб, достаточно будет всего лишь изменить параметр в политическом методе, а реализующий метод, который осуществляет собственно масштабирование, изменять не потребуется. ° Учитывайте все возможности. Напишите методы для всех возможных ситуаций, а не только для тех, которые требуются в данный момент.
Например, если вы пишете метод для получения последнего элемента списка, напишите заодно метод для первого элемента. Это не только повысит вероятность повторного использования, но и рационализирует область применения родственных методов. ° Стремитесь к максимальному расширению применимости метода.
Старайтесь обобщать типы аргументов, предусловия и постусловия, предположения о действии метода и контекст, в котором он работает. Закодируйте осмысленные действия для пустых аргументов, граничных значений и недопустимых значений. Часто небольшое увеличение объема кода метода значительно расширяет область его применения. 20.2. Повторное использование 443 ° Не используйте глобальную информацию. Сокращайте внешние ссылки. Ссылка на глобальный объект лрсднолагает некоторый контекст использования метода.
Часто всю необходимую информацию можно передать в виде аргументов. Можно хранить глобальную информацию в составе целевого объекта, чтобы другие методы тоже могли обращаться к ней. ° Не используйте методов с состояниями. Если поведение метода су|цественно зависит от предшествующей истории, вероятность его повторного использования значительно снижается. Старайтесь заменять такие методы на методы, не использующие информацию о состояниях. Например, приложение для обработки текста требует реализации операций вставки и замены. Один из возможных подходов состоит в установке флага 1лзегГ (вставка) нли гер1асе 1замена) и вызове операции телтге, которая вставляет или заменяет текст в зависимости от состояния флага.
В другом подходе используются две операции 1лхегГ и гер1асе, которые осуществляют соответствуюшие действия без всякой информация о состоянии системы, Недостаток методов с информацией о состоянии заключается в том, что состояние объекта в одной из частей приложения может повлиять на метод, вызванный в другой части приложения. 20.2.3. Использование наследования Приведенные выше рекомендации ловышают вероятность повторного использования кода. В некоторых случаях методы различных классов кажутся похожими, но не достаточно похожи для того, чтобы заменить их одним унаследованным методом.
Существует несколько методик разбиения методов на части для упрошения наследования кода. ° Выделение общих частей. Самый простой подход заключается в вынесении общего кода в новый метод, который будет вызываться каждым из двух похожих методов. общий метод может быть перенесен в класс-предок. Фактически, это сводится к вызову подпрограммы (рис. 20.1). Общий метод Рис. 20.1.
Повторное использование с выделением общей части ° Выделение различий. В некоторых случаях удобнее вынести отличительные черты методов разных классов в отдельные методы, а оставшийся код оставить в общем методе. Этот подход работает в тех случаях, когда общего у методов больше, чем различий.
Как показано на рис. 20.2, новый метод вызывает операцию, которая реализуется по-разному методами разных классов. В некоторых случаях для общего метода приходится создавать абстрактный класс. Этот лодход упрощает добавление подклассов, поскольку методы подклассов содержат только отличительные черты. 444 Глава 20 ° Стиль программирования Общий метод — вызов операции М Метод АсМ Метод ВсМ Рис.
20.2. Повторное использование с выделением отличий Выделение общего и различий хорошо иллюстрирует пакет для построения графиков числовых данных. ВагаСгар!з — абстрактный класс, в котором содержатся общие для подклассов данные и операции. Один из методов ПагаСгарл называется йаге. Он состоит из следующих этапов: построение границы, масштабирование данных, построение осей, построение графика данных, построение заголовка н легенды.
Подклассы РагаСгар!з, к которым относятся !.теСгар!з, ВагСгар!з и 5саггегСгарл, строят границы, заголовки и легенды одинаково, но по-разному реализуют масштабирование данных, построение осей и данных. Каждый подкласс наследует методы ИгатоВоп!ег, г!гагаТ!г!е и г!гатайеяелг! от абстрактного класса ПагаСгар!з, по сам определяет собственные методы зса!еПага, г!гагаАхез и р!оШага.
Метод г!гав достаточно определить только один раз в классе ВагаСгар!з, Он наследуется всеми подклассами. Каждый раз при вызове метода г!гаге вызываются г!гагаВоЫег, г!гашТ!г!е и г!гашЕейепг! из суперкласса и зса!е0ага, а!гагалх!з и р!огйага нз подкласса. Чтобы добавить новый подкласс, достаточно написать три его специализированных метода. ° Делегируйте. Иногда наследование может повысить объем повторного использования кода в программе, однако классы по смыслу не находятся в отношениях предок-потомок.
Не поддавайтесь искушению воспользоваться наследованием только в целях облегчения реализации. Применяйте делегирование. Наследование нужно применять только тогда, когда оно семантически корректно. Наследование означает, что каждый экземпляр подкласса действительно является экземпляром суперкласса. Все операции и атрибуты суперкласса должны быть применимы и к подклассам. В результате неправильного применения наследования программы получаются сложными в обслуживании и труднорасширяемыми. Объектно-ориентированные языки сами по себе не накладывают ограничений на семантику наследования. В такой ситуации правильнее применять механизм делегирования. Метод одного класса вызывает метод другого класса для фактического выполнения каких-либо действий.
Поскольку передача управления осуществляется явным образом, вероятность возникновения каких-либо побочных эффектов существенно снижается. Названия методов в классах могут отличаться: в каждом классе они должны соответствовать истинному смыслу операций. ° Инкапсулируйте внешний код. Часто возникает желание использовать код, который был разработан для другого приложения в рамках иных соглашений об интерфейсах.
Вместо того чтобы вставлять в свою программу непосредственный вызов внешнего кода, лучше инкапсулировать его внутри метода или класса. Это позволит изменить или заменить внешний пакет без крупномасштабной модификации вашего кода. 20.3. Возможность расширения 445 Например, если вы занимаетесь разработкой приложения для численных расчетов и хотите использовать существующий надежный код для вычисления обратной матрицы, можно написать класс Матрица, который будет инкапсулировать функциональность, предоставляемую внешним пакетом.
Этот класс может содержать метод вычисления обратной матрицы, принимаюший в качестве аргумента допуск на сингулярность и возвращающий новую матрицу, обратную к данной. 20.3. Возможность расширения Чаше всего программное обеспечение приходится расширять в совершенно непредвпленных направлениях. Рекомендации по улучшению возможностей по- вторного использования повышают и возможность расширения.
Кроме них имеются дополнительные правила хорошего стиля, представленные в этом разделе. Инкапсулируйте классы. Класс считается инкапсулированным, если его внутренняя структура скрыта от других классов. К реализации класса должны обращаться только его собственные методы. Многие компиляторы самостоятельно оптимизируют методы внешних классов, обеспечивая прямой доступ к реализации данного класса, но программист не должен делать этого сам. Уважайте другие классы, никогда не лезьте за данными, которые находятся внутри нпх.