Дж. Рамбо, М. Блаха - UML 2.0 - Объектно-ориентированное моделирование и разработка (1158633), страница 82
Текст из файла (страница 82)
° Операции, являющиеся частными случаями. Некоторые операции могут иметь меньше аргументов, потому что они являются частными случаями более общих операций. Реализуйте такие операции, вызывая более общие операции с подходящими значениями параметров. Например, добавление элемента в конец списка — частный случай операции вставки элемента в список с точкой вставки, находящейся после конечного элемента списка. ° Несогласованные названия. Схожие атрибуты в разных классах могут имети разные названия. Дайте атрибутам одинаковые названия и переместите их в общий класс-предок.
Это обеспечит лучшую согласованность операций, работающих с этими атрибутами. Ищите похожие операции 342 Глава 15 ° Проектирование классов с разными названиями. Очень важно иметь единую стратегию именования всех элементов модели. ° Несогласованные операции. Если операция определена в отдельных классах некоторой группы, нужно определить операцию у общего предка группы, а для тех классов, в которых она не используется, объявить ее как заглушку. Например, операция поворота имеет смысл для всех геометрических фигур, но для окружности она тождественна отсутствию операции. Пример с банкоматом.
Банкомат может печатать удаленные транзакции на чеке. Логично было бы добавить возможность печатать чеки и для транзакций, осуществляемых с помощью кассира. Однако печать чека для класса йетогеТтатасгююп (УдаленнаяТранзакция) требует наличия СазяСагп' (БанковскойКарты), тогда как чек для СазЫегТгапзасгюп (КассоваяТранзакция) печатается непосредственно для данного экземпляра Сизготег (Клиент). Кроме того, программное обеспечение кассы никак не связано с системой банкоматов.
Поэтому у нас должно быть два вида чеков: йетогейесе~рг (ЧекУдаленныхОпераций) н Сайгетйесе~рг (ЧекКассовыхОпераций). 15.92. Абстрагирование общего поведения На этапе анализа вы вряд ли сможете выявить все возможности использования наследования, поэтому на этапе проектирования стоит снова уделить время проверке модели классов с целью поиска схожих элементов. Кроме того, в процессе проектирования модель обязательно расширяется новыми классами и операциями.
Если в каких-либо классах имеются одинаковые операции и атрибуты, может статься, что эти классы на самом деле являются конкретизацией чего-то более общего, что может быть добавлено в модель на более высоком уровне абстрагирования. Везде, где имеется общее поведение, вы можете создавать общие суперклассы для тех черт поведения, которые являются одинаковыми у всех потомков, оставляя им реализацию всех прочих черт.
Такая трансформация модели классов называется абстрагированием общего суперкласса или общего поведения из модели. Мы рекомендуем вам создавать только абстрактные суперклассы, не имеющие непосредственных экземпляров. Поведение, определяемое таким суперклассом, принадлежит всем экземплярам его подклассов. (Для этого в модель классов всегда можно добавить подкласс Огяег — Прочие.) Например, операция огам для геометрической фигуры на дисплее требует настройки и прорисовки геометрии. Прорисовка для разных фигур, таких как окружности, прямые н сплайны, осуществляется по-разному, но для всех фигур можно унаследовать общую настройку (установку цвета, ширины линии и прочих параметров) от абстрактного класса Лянге (Фигура).
Иногда оказывается полезно создать суперкласс даже в том случае, если в вашем приложении у него будет только один подкласс. Хотя никакой немедленной пользы это действие не принесет, абстрактный суперкласс может быть повторно использован в будущих проектах. Он даже может оказаться достойным включения в вашу собственную библиотеку классов. Когда вы закончите проект, вам стоит посмотреть, какие классы могут повторно использоваться в будущих приложениях.
15.9. Корректировка иерархии наследования 343 Абстрактные суперклассы обладают и другими достоинствами, помимо общего и повторного использования. Деление класса на два, позволяющее отделить более конкретные аспекты от более общих, является одним из проявлений модульного принципа организации.
Каждый класс представляет собой отдельный компонент системы с хорошо документированным интерфейсом. Создание абстрактных суперклассов повышает возможность расширения программного продукта. Представьте, что вы разрабатываете модуль контроля температуры для большой автоматизированной системы управления. Вы должны использовать конкретный датчик (модель )55) с конкретным интерфейсом считывания температуры и преобразовывать численные данные в градусы Цельсия по специальной формуле.
Все это поведение можно поместить в отдельный класс, каждый экземпляр которого будет соответствовать отдельному датчику в составе системы. Однако если вы примете во внимание, что )55 — не единственная модель датчика, то вы создадите абстрактный суперкласс Яеизог (Датчик), определяющий поведение, общее для всех датчиков. Конкретный подкласс уепзок)55 будет осуществлять считывание и преобразование для датчика данной модели. Когда вам потребуется перевести систему управления на новую модель датчика, достаточно будет всего лишь подготовить подкласс со специализированным поведением, учитывающим особенности новой модели датчика. Общее для любых датчиков поведение уже имеется в суперклассе.
Но самое главное, что вам не придется изменить ни единой строчки кода во всей системе управления, использующей эти датчики, потому что интерфейс, определенный в суперклассе уеизог, останется тем же самым. Абстрактные суперклассы облегчают управление конфигурациями в процессе поставки и поддержки программного обеспечения. Представьте, что вы должны поставить свою систему управления на множество заводов, разбросанных по всей стране, и на каждом конфигурация системы будет своей, причем, в частности, будут использоваться разные типы датчиков. Некоторые заводы все еще работают со старой моделью )55, другие уже перешли на новую К99. Третьи работают с датчиками обоих типов.
Настройка системы в соответствии с требованиями множества клиентов может потребовать больших усилий. Вместо этого вы можете поставить всем клиентам одну и ту же версию системы, в которой для каждой модели датчика будет предусмотрен свой подкласс. Когда система запускается, она считывает клиентский файл конфигурации, в котором написано, какие модели датчиков он использует, после чего система создает экземпляры конкретных подклассов соответствующих моделей.
В остальном система работает со всеми датчиками одинаково, так как их интерфейс определяется суперклассом Беихок Вы можете даже реализовать переключение с одного типа датчика на другой в процессе работы (без выключения системы), если добавите возможность создания нового обьекта для нового типа датчика. Пример с банкоматом. Мы уделили особое внимание наследованию в процессе конструирования модели классов. На данном этапе новых возможностей расширения иерархии наследования не появилось.
В реальном приложении этап проектирования внесет в систему гораздо больше деталей, а потому и возможностей улучшения наследования будет больше. 344 Глава 15 ° Проектирование классов 15.9.3. Делегирование Наследование — это механизм реализации обобщения, согласно которому поведение суперкласса используется совместно всеми его подклассами. Совместное использование поведения допустимо только тогда, когда классы действительно находятся в отношении обобщения, то есть когда действительно можно сказать, что подкласс является частным случаем суперкласса. Операции подкласса, перекрывающие соответствующие операции суперкласса, должны предоставлять те же сервисы, что и в суперклассе. Когда класс В наследует спецификацию класса А, вы можете предполагать, что каждый экземпляр класса В является экземпляром класса А, потому что эти экземпляры ведут себя соответствующим образом.
Некоторые программисты используют наследование как технологию реализации, вовсе не задумываясь о необходимости поддерживать согласованность поведения. Часто бывает так, что существующий класс реализует часть поведения, которое вы хотите добавить в новый класс, а в остальном эти классы отличаются друг от друга. У программиста возникает соблазн унаследовать поведение от сушествующего класса, чтобы избежать необходимости реализовывать новый класс полностью. В результате могут возникнуть проблемы, связанные с тем, что остальные унаследованные операции реализуют нежелательное поведение. Мы не рекомендуем наследовать поведение, потому что это приводит к ошибкам.
Представьте, что вам нужен класс 5гас( (Стек), а у вас уже есть класс бмг (Список). У вас может возникнуть желание унаследовать 5гасй от бмг. Помещение элемента на стек можно реализовать как добавление элемента к концу списка, а удаление элемента из стека можно реализовать как удаление элемента из конца списка. Но вы унаследуете и операции, добавляющие и удаляющие элементы из произвольных позиций списка. Если они когда-либо будут использованы (по ошибке или для упрощения задачи), класс 5гасй начнет вести себя не так, как ему полагается. Вместо того чтобы использовать наследование как технологию реализации, вы можете достичь той же цели, связав два класса ассоциацией.