Студенческий сайт КФУ - ex ТНУ » Учебный раздел » Учебные файлы »Информатика

Агрегация или наследование?

Тип: реферат
Категория: Информатика
Скачать
Купить
Агрегация или наследование?Евгений КаратаевИ снова о проектировании классов. Больная тема и место применения множества трюков. Большинство программистов используют трюки по-разному. Видимо, есть три способа их применения - 1) неосознанно, 2) осознанно, но с затруднениями при выборе способа и 3) осознанно и, более того, трюки вычисляются. Рассмотрим вопрос выбора пути при решении задачи типа "добавление новой функциональности". Имеется модуль в виде набора классов, который по функциональности частично подходит к тому, что надо получить. Имеется задача добавить в модуль некую функциональность. Имеется нежелание много работать и иметь в последующем с полученным кодом проблемы. При желании в эти условия задачи можно, полагаю, вписать практически любую программерскую задачу. Рассмотрим выбор между двумя вариантами действий. Первый вариант - взять имеющийся класс, максимально подходящий к требуемому и изменить его путем модификации без получения нового класса. Скажем, поправить несколько функций или добавить несколько членов класса. Второй вариант - составить новый класс, унаследованный от максимально подходящего к требуемой функциональности и дописать к наследнику что ему не хватает или переопределить часть виртуальных функций базового. Первый вариант договоримся называть агрегированием, а второй - наследованием. Рассмотрим подробнее оба варианта, абстрагируясь от выбора конкретного языка программирования и содержания классов. При агрегации мы не получаем нового класса и для обеих задач, старой и новой, используем один и тот же класс. Агрегацию мы можем получить не только как способ решить новую задачу, но и как способ исправить ошибки в старой задаче, поскольку исправления кода автоматически влияют на старую задачу. При агрегации к классу добавляется одно или два поля, благодаря которым и происходит различение старой и новой функциональности. А именно по значению этих полей. Например, добавленное поле имеет смысл номера версии, в зависимости от значения которой в модифицированном классе различается поведение нескольких функций. Этим способом мы можем избежать рутины с большим количеством модификаций задачи. Что является типичным признаком современного проекта. Добавляем поле, и при изменениях в спецификации корректируем поведение нескольких функций. Переопределять виртуальные функции по понятным причинам нет необходимости. При наследовании мы получаем новый класс. Возможно, несколько. Новая функциональность реализуется исключительно в новом классе и имеющийся код этого никак не замечает и продолжает работать (надеюсь, без ошибок ;). В наследнике переопределяем одну или несколько виртуальных функций и при необходимости того добавляем поля данных. Примеры, как это делать, программисты сами могут привести из своей практики. Сведем сравнительные различия в таблицу. Эти пункты следует учитывать, если проектирование классов производится не спонтанно и "по живому", а более-менее ответственно. Тем более, что всегда есть время обдумать свои шаги. Приведем реальные примеры, когда перед программистом может стоять выбор способа реализации задачи - использовать агрегирование или наследование и что будет удобнее. Вопрос в направлении действия слова "удобнее" рассматривается в основном в контексте удобства использования, поскольку качество используемого кода определяется именно удобством его использования, а не удобствами, которые испытывал программист при его написании. Рассмотрим окна в Windows. Окна в Windows все и независимо ни от чего имеют две основные характеристики - это определение класса и функция обработки сообщений. Это пункт номер раз, который известен всем. Кроме того, окна в Windows имеют четкую градацию на 4 вида, различающиеся как деталями функции обработки сообщений, так и отношением к нему системы управления окнами. Это различие определяется функциями по умолчанию, которые вызывает программист, если не обрабатывает какие-либо сообщения. Это функции DefWindowProc, DefDialogProc, DefMDIChildProc и DefMDIFrameProc. Классика объектно-ориентированного проектирования подсказывает, что следует, как минимум, в графе наследований предусмотреть класс, реализующий абстрактное окно. С тем, чтобы впоследствии этот класс можно было использовать для написания как этих 4 видов окон, так и произвольных контролов. В отношении уточнения поведения окна конкретного вида граф наследований класса окна в данном случае особой роли не играет. Стоит вопрос - что выбрать для реализации этих четырех видов окон - агрегацию или наследование с получением еще четырех или более классов? Реализация как библиотек OWL, так и MFC выбрала путь наследования. Получены дополнительные классы, реализующие соответственно просто несущее окно, контрол частного вида, диалоговое окно, MDI Frame и MDI Child (как сказал Пушкин, "... прости, не знаю как перевести."). В результате для использования каждого из этих классов требуется кроме общего кода, оперирующего базовым классом окна, частный код, который может быть вызван только для конкретных классов. Там, где используется специфика MDI Child, может быть использован код только для него. Более того - код вызова, специфичный для MDI Child, может пользоваться только этим классом либо его наследником. Программист, использующий библиотеку классов, обязывается при написании программы создавать наследника соответствующего класса. Таким образом, специфика окон в таком подходе проектирования вылилась не только в код специальных классов, но и в существование кода, применимого только к данному классу и существующему вне его. Этот факт нашел отражение в том, что встроенные в среды разработки Borland C++ и Visual C++ мастера классов при попытке создания новых классов, несущих прикладную нагрузку, обязательно требуют указания базового класса и последующую модификацию производят с учетом этих нескольких базовых классов. Унифицированный визуальный дизайнер окон в таком подходе представляет собой либо практически нереализуемую задачу, либо является объединением 4-х дизайнеров. В обеих этих средах более-менее полноценно реализовано визуальное редактирование лишь для диалоговых окон. Реализация библиотеки VCL пошла по пути агрегирования возможностей и написания базового класса (TForm) таким образом, что различие между 4-мя видами окон сводится к указанию значения поля данных. При этом код класса содержит код для всех 4-х видов окон. Становится невозможно построение программы, использующей только диалоговые окна и не содержащей кода поддержки MDI. При этом становится возможным использование единого визуального дизайнера для редактирования всех 4-х видов окон. Наличие же функций поддержки MDI у объекта, используемого в качестве диалогового окна мало кого смущает, поскольку эти функции для диалоговых окон просто не приходит в голову вызывать. Более того - у программиста есть уверенность, что то, что он проектирует в качестве обычного всплывающего окна в интерфейсе SDI, всегда можно будет использовать так же в качестве и MDI Frame, и MDI Child, и простого модального диалога. Более того, сохраняется возможность менять показ этого окна в зависимости от состояния программы. Например, в некоторых случаях MDI Child должен быть показан в модальном режиме. Плюсы и минусы выбора графа наследования видны уже на конкретном приведенном примере. В иных случаях следует определяться с выбором, исходя из конкретной задачи. Что интересно, на выбор может оказать влияние знание не только имеющейся задачи и ее целей, но и последующих перспектив работы. Как показала практика применения вышеупомянутых средств разработки, то есть Borland C++ / Visual C++ и Delphi / Borland C++ Builder, на первых двух успешно строились долго разрабатываемые проекты, сложные по своей организации и слабо зависящие от разнообразия интерфейса (либо получившиеся такими). Вторые два средства использовались для действительно быстрой и качественной разработки проектов, имеющих развитые интерфейсные средства. Сама возможность применения единого визуального дизайнера окон явилась своего рода катализатором возникновения сообщества компонентного подхода. Следует отметить, что компонентный подход, применяемый в Visual C++ (ActiveX), зарекомендовал себя в качестве более инертного. Наследование дельфийских компонентов и переопределение их виртуальных функций выполнить гораздо проще, чем наследование и переопределение виртуальных функций ActiveX контролов. Второе выполнить просто невозможно, поскольку следует наследовать не класс с частично реализованным поведением, а интерфейсы, вообще не содержащие реализации. Если эмулировать поведение похожего класса еще возможно, то слегка переопределить его виртуальные функции - уже большая спортивная проблема. Остается разве что полностью инкапсулировать исходный класс в...
Другие файлы:

Генетика пола человека. Наследование, сцепленное с полом
Генетические механизмы формирования пола. Понятия "наследственность" и "наследование". Сцепленное наследование признаков. Признаки, наследуемые через...

Наследование по закону
Понятие и открытие наследства. Проблемные вопросы наследования по закону. Наследование прав, связанных с участием в хозяйственных товариществах и обще...

Наследование по закону
Понятие наследования по закону в Российской Федерации; основные очереди наследников; наследование нетрудоспособными иждивенцами наследодателя, наследо...

Наследование земельных участков
Наследование на общих основаниях земельного участка или права пожизненного наследуемого владения земельным участком. Наследование по закону РФ или по...

Наследование и полиморфизм. Наследование классов
Иерархия и типы классов в программе, особенности обеспечения наследования. Наследование по принципу подчиненности. Включение в другие классы или делег...