Информатика наследование

Инкапсуляция, наследование, полиморфизм

Объектно-ориентированное программирование основано на «трех китах» — трех важнейших принципах, придающих объектам новые свойства. Этими принципами являются инкапсуляция, наследование и полиморфизм.

Инкапсуляция есть объединение в единое целое данных и алгоритмов обработки этих данных. В рамках ООП данные называются полями объекта, а алгоритмы — объектными методами.

Инкапсуляция позволяет в максимальной степени изолировать объект от внешнего окружения. Она существенно повышает надежность разрабатываемых программ, т.к. локализованные в объекте алгоритмы обмениваются с программой сравнительно небольшими объемами данных, причем количество и тип этих данных обычно тщательно контролируются. В результате замена или модификация алгоритмов и данных, инкапсулированных в объект, как правило, не влечет за собой плохо прослеживаемых последствий для программы в целом (в целях повышения защищенности программ в ООП почти не используются глобальные переменные).

Другим немаловажным следствием инкапсуляции является легкость обмена объектами, переноса их из одной программы в другую. Можно сказать, что ООП «провоцирует» разработку библиотек объектов, таких как Turbo Vision.

Наследование есть свойство объектов порождать своих потомков. Объект-потомок автоматически наследует от родителя все поля и методы, может дополнять объекты новыми полями и заменять (перекрывать) методы родителя или дополнять их.

Принцип наследования решает проблему модификации свойств объекта и придает ООП в целом исключительную гибкость. При работе с объектами программист обычно подбирает объект, наиболее близкий по своим свойствам для решения конкретной задачи, и создает одного или нескольких потомков от него, которые «умеют» делать то, что не реализовано в родителе.

Последовательное проведение в жизнь принципа «наследуй и изменяй» хорошо согласуется с поэтапным подходом к разработке крупных программных проектов и во многом стимулирует такой подход.

Полиморфизм — это свойство родственных объектов (т.е. объектов, имеющих одного общего родителя) решать схожие по смыслу проблемы разными способами. В рамках ООП поведенческие свойства объекта определяются набором входящих в него методов. Изменяя алгоритм того или иного метода в потомках объекта, программист может придавать этим потомкам отсутствующие у родителя специфические свойства. Для изменения метода необходимо перекрыть его в потомке, т.е. объявить в потомке одноименный метод и реализовать в нем нужные действия. В результате в объекте-родителе и объекте-потомке будут действовать два одноименных метода, имеющие разную алгоритмическую основу и, следовательно, придающие объектам разные свойства. Это и называется полиморфизмом объектов.

Юридическая консультация. Телефон: +7 920-985-9888.

26. Наследование.

Тип-потомок, при этом, называется наследником или порожденным (дочерним) типом. А тип, которому наследует дочерний тип, называется порождающим (родительским) типом.

Наследуемые поля и методы можно использовать в неизменном виде или переопределять (модифицировать).

Н. Вирт в своем языке Паскаль стремился к максимальной простоте, поэтому он не стал его усложнять введением отношения наследования. Поэтому типы в Паскале не могут наследовать.

Однако Тurbо Раsсаl 7.0 расширяет этот язык для поддержки наследования. Одним из таких расширений является новая категория структуры данных, связанная с записями, но значительно более мощная. Типы данных в этой новой категории определяются с помощью нового зарезервированного слова Оbjесt. Синтаксис при этом очень похож на синтаксис определения записей:

Область действия есть одно из следующих ключевых слов:

Подробнее об областях действия компонент рассказано в вопросе № 28.

Наследование – это мощный инструмент, используемый при разработке программ. Оно позволяет реализовать на практике объектно-ориентированную декомпозицию задачи, средствами языка выражать отношения между объектами типов, образующих иерархию, а также способствует повторному использованию программного кода.

26. Наследование

Наследование – это процесс порождения новых типов-потомков от существующих типов-родителей, при этом потомок получает (наследует) от родителя все его поля и методы.

Однако Turbo Pascal 7.0 расширяет этот язык для поддержки наследования. Одним из таких расширений является новая категория структуры данных, связанная с записями, но значительно более мощная. Типы данных в этой новой категории определяются с помощью нового зарезервированного слова Object. Синтаксис при этом очень похож на синтаксис определения записей:

Знак «+» после синтаксической конструкции в круглых скобках означает, что эта конструкция должна встречаться один или более раз в данном описании.

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

IT1205: Программирование Java SE

Один из основополагающих механизмов концепции повторного использования кода в ООП — это наследование. Оно позволяет получить новые классы из существующих. Новый класс (также называемый подкласс, подтип, производный класс, дочерний класс) может унаследовать члены от старого класса (так называемого суперкласса супертипа, базового класса, родительского класса). Подкласс может добавить новое поведение и свойства и при определенных обстоятельствах изменить наследуемое им поведение.

В Java реализация наследования достигается с помощью расширения классов (т.е. добавления новых полей и методов) и изменения унаследованных членов. Наследование членов достаточно тесно связано с их доступностью. Если член суперкласса доступен по своему простому имени в подклассе (без использования какого-то дополнительного синтаксиса, например такого, как super ), то член рассматривается как унаследованный. Это значит, что закрытые ( private ), переопределенные и скрытые члены суперкласса не наследуются. Наследование не следует путать с существованием таких членов в состоянии объекта подкласса (см. Пример 6.1).

Суперкласс задается с помощью ключевого слова extends в заголовке объявления подкласса. В теле класса производного класса определяются только новые и измененные члены. Остальные его объявления составляются из унаследованных методов. Если оператор extends не дан в заголовке объявления класса, то класс неявно расширяет класс java.lang.Object . Такое неявное наследование предполагается в объявлении класса Light в строке (1) в примере 6.1. Также в примере 6.1 подкласс TubeLight в строке (2) явно использует оператор extends и определяет дополнительные члены к уже унаследованным из суперкласса Light (который, в свою очередь, наследует от класса Object ). Члены суперкласса Light , которые доступны по своим простым именам в подклассе TubeLight , наследуются подклассом.

Закрытые ( private ) члены суперкласса не наследуются подклассом, и обратиться к ним можно только опосредованно. Закрытое поле indicator суперкласса Light не наследуется, но существует в объекте подкласса и косвенно доступно.

С помощью соответствующих модификаторов доступа суперкласс может ограничить доступ к своим членам — например, к каким из них можно обратиться напрямую — и таким образом определить, какие члены могут быть унаследованы его подклассами. Как показано в примере 6.1, подкласс может использовать унаследованные члены, как если бы они были объявлены в теле его класса. Но это не распространяется на члены, объявленные как private в суперклассе. Члены, которые имеют пакетную видимость в суперклассе, также не наследуются подклассами в других пакетах, так как эти члены только доступны по своему простому имени в подклассах внутри того же самого пакета, где находится суперкласс.

Смотрите так же:  Офисы осаго ресо в спб

Поскольку конструкторы и блоки инициализации не являются членами класса, они не наследуются подклассом.

Пример 6.1 Расширяющие классы: наследование и доступность

Класс в Java может расширять только один другой класс; т.е. он может иметь только одного непосредственного родителя. Такой тип наследования иногда называют одиночным или линейным наследованием реализации. Такой выбор имени уместен, потому что подкласс наследует реализации членов своего суперкласса. Отношение наследования может быть изображено как иерархия наследования (также известная как иерархия классов). Верхние классы в иерархии являются более обобщенными, так как они абстрагируются от поведения класса. Классы внизу иерархии более специализированы, потому что они настраивают унаследованное поведение, используя дополнительные свойства и поведение. На рис. 6.1 показано отношение наследования между классом Light , который представляет абстракцию, и его более специализированными подклассами. Класс java.lang.Object всегда находится на вершине любой иерархии наследования Java, так как все классы, за исключением самого класса Object , наследуют (или прямо или косвенно) от этого класса.

Наследование определяет отношение «есть» (is-a) (также известное как суперкласс — подкласс) между суперклассом и его подклассом. Это означает, что объект подкласса может использоваться независимо от того, используется ли суперкласс. Это часто применяется в качестве лакмусовой бумажки для решения вопроса об использовании наследования. В наследовании имеются тонкости в работе с объектами. Объект класса TubeLight может использоваться независимо от объекта суперкласса Light . Объект класса TubeLight есть также объект (is-a) суперкласса Light . Отношение наследования транзитивно: если класс B расширяет класс A , то класс C , который расширяет класс B , будет также наследовать от класса A через класс B . Объект класса SpotLightBulb является также объекте (is-a) iкласса Light . Отношение «есть» (is-a) не поддерживается между одноранговыми классами: объект класса LightBulb не является объектом класса TubeLight .

Рис. 6.1. Иерархия наследования

В то время как наследование определяет отношение «есть» (is-a) между суперклассом и его подклассами, агрегация характеризует отношение «имеет» (has-a) между экземпляром класса и его компонентами (или частями). Экземпляр класса Light имеет следующие части: поле для хранения значения мощности ( noOfWatts ), поле для хранения данных о том, включена лампочка или нет ( indicator ), и строковый объект для хранения данных о местоположении (обозначенное полем, хранящим значение ссылки, location ). В Java составной объект не может содержать другие объекты. Он может только содержать ссылки на составляющие его объекты. Это отношение определяет иерархию агрегации, которая воплощает отношение «имеет» (has-a). Объекты-компоненты могут совместно использоваться объектами, и их жизненные циклы могут быть -зависимы от жизненного цикла объекта, их содержащего.

Инкапсуляция, полиморфизм, наследование

Все языки OOP, включая С++, основаны на трёх основополагающих концепциях, называемых инкапсуляцией, полиморфизмом и наследованием. Рассмотрим эти концепции.

1. Инкапсуляция

Инкапсуляция (encapsulation) — это механизм, который объединяет данные и код, манипулирующий зтими данными, а также защищает и то, и другое от внешнего вмешательства или неправильного использования. В объектно-ориентированном программировании код и данные могут быть объединены вместе; в этом случае говорят, что создаётся так называемый «чёрный ящик». Когда коды и данные объединяются таким способом, создаётся объект (object). Другими словами, объект — это то, что поддерживает инкапсуляцию.

Внутри объекта коды и данные могут быть закрытыми (private). Закрытые коды или данные доступны только для других частей этого объекта. Таким образом, закрытые коды и данные недоступны для тех частей программы, которые существуют вне объекта. Если коды и данные являются открытыми, то, несмотря на то, что они заданы внутри объекта, они доступны и для других частей программы. Характерной является ситуация, когда открытая часть объекта используется для того, чтобы обеспечить контролируемый интерфейс закрытых элементов объекта.

На самом деле объект является переменной определённого пользователем типа. Может показаться странным, что объект, который объединяет коды и данные, можно рассматривать как переменную. Однако применительно к объектно-ориентированному программированию это именно так. Каждый элемент данных такого типа является составной переменной.

2. Полиморфизм

Полиморфизм (polymorphism) (от греческого polymorphos) — это свойство, которое позволяет одно и то же имя использовать для решения двух или более схожих, но технически разных задач. Целью полиморфизма, применительно к объектно-ориентированному программированию, является использование одного имени для задания общих для класса действий. Выполнение каждого конкретного действия будет определяться типом данных. Например для языка Си, в котором полиморфизм поддерживается недостаточно, нахождение абсолютной величины числа требует трёх различных функций: abs(), labs() и fabs(). Эти функции подсчитывают и возвращают абсолютную величину целых, длинных целых и чисел с плавающей точкой соответственно. В С++ каждая из этих функций может быть названа abs(). Тип данных, который используется при вызове функции, определяет, какая конкретная версия функции действительно выполняется. В С++ можно использовать одно имя функции для множества различных действий. Это называется перегрузкой функций (function overloading).

В более общем смысле, концепцией полиморфизма является идея «один интерфейс, множество методов». Это означает, что можно создать общий интерфейс для группы близких по смыслу действий. Преимуществом полиморфизма является то, что он помогает мнижать сложность программ, разрешая использование того же интерфейса для задания единого класса действий. Выбор же конкретного действия, в зависимости от ситуации, возлагается на компилятор. Вам, как программисту, не нужно делать этот выбор самому. Нужно только помнить и использовать общий интерфейс. Пример из предыдущего абзаца показывает, как, имея три имени для функции определения абсолютной величины числа вместо одного, обычная задача становится более сложной, чем это действительно необходимо.

Полиморфизм может применяться также и к операторам. Фактически во всех языках программирования ограниченно применяется полиморфизм, например, в арифметических операторах. Так, в Си, символ + используется для складывания целых, длинных целых, символьных переменных и чисел с плавающей точкой. В этом случае компилятор автоматически определяет, какой тип арифметики требуется. В С++ вы можете применить эту концепцию и к другим, заданным вами, типам данных. Такой тип полиморфизма называется перегрузкой операторов (operator overloading).

Ключевым в понимании полиморфизма является то, что он позволяет вам манипулировать объектами различной степени сложности путём создания общего для них стандартного интерфейса для реализации похожих действий.

3. Наследовние

Наследование (inheritance) — это процесс, посредством которого один объект может приобретать свойства другого. Точнее, объект может наследовать основные свойства другого объекта и добавлять к ним черты, характерные только для него. Наследование является важным, поскольку оно позволяет поддерживать концепцию иерархии классов (hierarchical classification). Применение иерархии классов делает управляемыми большие потоки информации. Например, подумайте об описании жилого дома. Дом — это часть общего класса, называемого строением. С другой стороны, строение — это часть более общего класса — конструкции, который является частью ещё более общего класса объектов, который можно назвать созданием рук человека. В каждом случае порождённый класс наследует все, связанные с родителем, качества и добавляет к ним свои собственные определяющие характеристики. Без использования иерархии классов, для каждого объекта пришлось бы задать все характеристики, которые бы исчерпывающи его определяли. Однако при использовании наследования можно описать объект путём определения того общего класса (или классов), к которому он относится, с теми специальными чертами, которые делают объект уникальным. Наследование играет очень важную роль в OOP.

Смотрите так же:  Ордер на проживание в общежитии

Наследование, полиморфизм, инкапсуляция

Объектно-ориентированное программирование ( ООП ) – подход к созданию программ, основанный на использовании классов и объектов, взаимодействующих между собой.

Класс (java class) описывает устройство и поведение объектов. Устройство описывается через набор характеристик (свойств), а поведение – через набор доступных для объектов операций (методов). Классы можно создавать на основе уже имеющихся, добавляя или переопределяя свойства и методы.

Классы представляют шаблоны, по которым строятся объекты. Объекты – это элементы программы, обладающие схожим набором характеристик и поведением (т.е это элементы, построенные на основе одного класса). Каждый объект имеет некоторое состояние, оно определяется значением всех его свойств. В одной программе могут существовать несколько классов, а объекты разных классов могут взаимодействовать между собой (через методы).

Наследование, extends

Наследование является неотъемлемой частью Java. При использовании наследования принимается во внимание, что новый класс, наследующий свойства базового (родительского) класса имеет все те свойства, которым обладает родитель. В коде используется операнд extends, после которого указывается имя базового класса. Тем самым открывается доступ ко всем полям и методам базового класса.

Используя наследование, можно создать общий «java class», который определяет характеристики, общие для набора связанных элементов. Затем можно наследоваться от него и создать дополнительные классы, для которых определить дополнительные уникальные для них характеристики.

Главный наследуемый класс в Java называют суперклассом super. Наследующий класс называют подклассом. Таким образом подкласс — это специализированная версия суперкласса, которая наследует все свойства суперкласса и добавляет свои собственные уникальные элементы.

Рассмотрим пример описания java class’a студента Student, который имеет имя, фамилию, возраст, и номер группы. Класс студента будем создавать на основе super класса пользователя User, у которого уже определены имя, фамилия и возраст:

Теперь создаем отдельный класс Student, наследующего свойства super класса. При наследовании класса необходимо также переопределить и конструкторы родительского класса :

Ключевое слово extends показывает, что мы наследуемся от класса User.

Ключевое слово super

В конструкторе класса Student мы вызываем конструктор родительского класса через оператор super, передавая ему весь необходимой набор параметров. В Java ключевое слово super обозначает суперкласс, т.е. класс, производным от которого является текущий класс. Ключевое слово super можно использовать для вызова конструктора суперкласса и для обращения к члену суперкласса, скрытому членом подкласса.

Рассмотрим как происходит наследование с точки зрения создания объекта :

Сначала открывается конструктор класса Student, после этого вызывается конструктор суперкласса User, а затем выполняются оставшиеся операции в конструкторе Student. Такая последовательность действий вполне логична и позволяет создавать более сложные объекты на основе более простых.

У суперкласса могут быть несколько перегруженных версий конструкторов, поэтому можно вызывать метод super() с разными параметрами. Программа выполнит тот конструктор, который соответствует указанным аргументам.

Вторая форма ключевого слова super действует подобно ключевому слову this, только при этом мы всегда ссылаемся на суперкласс подкласса, в котором она использована. Общая форма имеет следующий вид:

Здесь член может быть методом либо переменной экземпляра. Подобная форма подходит в тех случаях, когда имена членов подкласса скрывают члены суперкласса с такими же именами.

В результате в консоли мы должны увидеть :

Переопределение методов, Override

Если в иерархии классов имя и сигнатура типа метода подкласса совпадает с атрибутами метода суперкласса, то метод подкласса переопределяет метод суперкласса. Когда переопределённый метод вызывается из своего подкласса, он всегда будет ссылаться на версию этого метода, определённую подклассом. А версия метода из суперкласса будет скрыта.

Если нужно получить доступ к версии переопределённого метода, определённого в суперклассе, то необходимо использовать ключевое слово super.

Не путайте переопределение с перегрузкой. Переопределение метода выполняется только в том случае, если имена и сигнатуры типов двух методов идентичны. В противном случае два метода являются просто перегруженными.

В Java SE5 появилась анотация @Override;. Если необходимо переопределить метод, то используйте @Override, и компилятор выдаст сообщение об ошибке, если вместо переопределения будет случайно выполнена перегрузка.

В Java можно наследоваться только от одного класса.

Инкапсуляция

В информатике инкапсуляцией (лат. en capsula) называется упаковка данных и/или функций в единый объект.

Основой инкапсуляции в Java является класс. Инкапсуляция означает, что поля объекта недоступны его клиентам непосредственно — они скрываются от прямого доступа извне. Инкапсуляция предохраняет данные объекта от нежелательного доступа, позволяя объекту самому управлять доступом к своим данным.

Модификаторы доступа

При описании класса используются модификаторы доступа. Модификаторы доступа можно рассматривать как с позиции инкапсуляции так и наследования. Если рассматривать с позиции инкапсуляции, то модификаторы доступа позволяют ограничить нежелательный доступ к членам класса извне.

Модификатор доступа Область действия
public Без ограничений
private Только из данного класса
protected Из данного класса и его потомков
Без модификатора Для всех классов данного пакета

Открытые члены класса составляют внешнюю функциональность, которая доступна другим классам. Закрытыми (private) обычно объявляются независимые от внешнего функционала члены, а также вспомогательные методы, которые являются лишь деталями реализации и неуниверсальны по своей сути. Благодаря сокрытию реализации класса можно менять внутреннюю логику отдельного класса, не меняя код остальных компонентов системы.

Желательно использовать доступ к свойствам класса только через его методы (принцип bean классов, «POJO»), который позволяет валидировать значения полей, так как прямое обращение к свойствам отслеживать крайне сложно, а значит им могут присваиваться некорректные значения на этапе выполнения программы. Такой принцип относится к управлению инкапсулированными данными и позволяет быстро изменить способ хранения данных. Если данные станут храниться не в памяти, а в файлах или базе данных, то потребуется изменить лишь ряд методов одного класса, а не вводить эту функциональность во все части системы.

Программный код, написанный с использованием принципа инкапсуляции легче отлаживать. Для того чтобы узнать, в какой момент времени и кто изменил свойство интересующего нас объекта, достаточно добавить вывод отладочной информации в тот метод объекта, посредством которого осуществляется доступ к свойству этого объекта. При использовании прямого доступа к свойствам объектов программисту бы пришлось добавлять вывод отладочной информации во все участки кода, где используется интересующий нас объект.

Смотрите так же:  Срочный трудовой договор документ

Пример простого описания робота

В представленном примере робота используются наборы методов, начинающие с set и get. Эту пару методов часто называют сеттер/геттер. Данные методы используются для доступа к полям объекта. Наименования метода заканчиваются наименованием поля, начинающееся с ПРОПИСНОЙ буквы.

В методах set мы передаем значение через формальный параметр во внутрь процедуры. В коде процедуры мы присваиваем значение переменной объекта/класса с использованием ключевого слова this.

Использование ключевого слова this необходимо, т.к. наименование формального параметра совпадает с наименованием переменной объекта. Если бы наименования отличались бы, то можно было бы this не использавать.

Полиморфизм, polymorphism

Полиморфизм является одним из фундаментальных понятий в объектно-ориентированном программировании наряду с наследованием и инкапсуляцией. Слово полиморфизм греческого происхождения и означает «имеющий много форм». Чтобы понять, что означает полиморфизм применительно к объектно-ориентированному программированию, рассмотрим пример создания векторного графического редактора, в котором необходимо использовать ряд классов в виде набора графических примитивов — Square, Line, Circle, Triangle, и т.д. У каждого из этих классов необходимо определить метод draw для отображения соответствующего примитива на экране.

Очевидно, придется написать некоторый код, который для изображения рисунка будет последовательно перебирать все примитивы, которые необходимо вывести на экран, и вызывать метод draw у каждого из них.

Человек, незнакомый с полиморфизмом, вероятнее всего создаст несколько массивов: отдельный массив для каждого типа примитивов и напишет код, который последовательно переберет элементы из каждого массива и вызовет у каждого элемента метод draw. В результате получится примерно следующий код:

Недостатком написанного выше кода является дублирование практически идентичного кода для отображения каждого типа примитивов. Также неудобно то, что при дальнейшей модернизации нашего графического редактора и добавлении возможности рисовать новые типы графических примитивов, например Text, Star и т.д., при таком подходе придется менять уже существующий код и добавлять в него определения новых массивов, а также обработку элементов, содержащихся в них.

Используя полиморфизм, можно значительно упростить реализацию подобной функциональности. Прежде всего, создадим общий родительский класс Shape для всех наших классов.

После этого мы создаем различные классы-наследники: Square (Квадрат), Line (Линия), Сircle (круг) и Triangle (Треугольник):

В наследниках у нас переопределен метод draw. В результате получили иерархию классов, которая изображена на рисунке.

Теперь проверим удивительную возможность полиморфизма:

В консоль будут выведены следующие строки:

Таким образом каждый класс-наследник вызвал именно свой метод draw, вместо того, чтобы вызвать метод draw из родительского класса Shape.

Полиморфизм — положение теории типов, согласно которому имена (например, переменных) могут обозначать объекты разных, но имеющих общего родителя, классов. Следовательно, любой объект, обозначаемый полиморфным именем, может посвоему реагировать на некий общий набор операций.

Перегрузка метода, overload

В процедурном программировании тоже существует понятие полиморфизма, которое отличается от рассмотренного механизма в ООП. Процедурный полиморфизм предполагает возможность создания нескольких процедур или функций с одинаковым именем, но разными количеством или типами передаваемых параметров. Такие одноименные функции называются перегруженными, а само явление — перегрузкой (overload). Перегрузка функций существует и в ООП и называется перегрузкой методов. Примером использования перегрузки методов в языке Java может служить класс PrintWriter, который используется в частности для вывода сообщений на консоль. Этот класс имеет множество методов println, которые различаются типами и/или количеством входных параметров. Вот лишь несколько из них:

Объект имеет свойства и поведение, которые инкапсулированы (скрыты) у него внутри. Сервисы, которые он предоставляет своим клиентам, составляют его контракт. Только контракт, определенный объектом, доступен клиентам. Реализация свойств и поведения объекта клиентов не касается. Инкапсуляция помогает провести различие между контрактом объекта и реализацией. Это имеет большое значение для разработки программ. Реализацию объекта можно изменить, не затрагивая клиентов. Инкапсуляция также уменьшает сложность, так как внутреннее содержание объекта скрыто от клиентов, которые не могут оказать влияние на реализацию.

Выбор между наследованием и агрегацией

На рис.6.6 приведена диаграмма классов UML, демонстрирующая несколько агрегативных связей и одно отношение наследования. Диаграмма классов показывает структуру определяемую при помощи агрегации, и стек, определяемый при помощи наследования. И то и другое основывается на связных списках. Цель примера заключается в том, чтобы продемонстрировать наследование и агрегацию, а не рабочую реализацию очередей и стеков. В классе Node в строке (1) определены два поля: одно, обозначающее данные, и другое, обозначающее следующий узел из списка. Класс LinkedList в строке (2) управляет списком с помощью ссылок head и tail . Узлы можно добавить как в начало, так и в конец списка, но удалить можно только с начала списка.

Рис. 6.6. Реализация структуры данных с помощью наследования и агрегации

Пример 6.15. Реализация структуры данных с помощью наследования и агрегации

Очень важно на этапе проектирования при моделировании взаимосвязей сделать выбор между наследованием и агрегацией. Вообще, рекомендуется использовать наследование, если отношение есть (is-a) явно поддерживается всеми вовлеченными объектами на протяжении их жизненного цикла; в других случаях лучшим выбором является агрегация. Роль часто путают с отношением есть. Например, пусть дан класс Employee , лучшей идеей является применение наследования от этого класса для моделирования, которые могут выполнять сотрудники (например, менеджер или кассир), если эти периодически изменяются. Изменяющиеся роли следует рассмотреть как новый объект, представляющий новую роль каждый раз, когда это происходит.

Повторное использование кода также лучше достигается с помощью агрегации, когда нет связи есть. Поддержка искусственно созданного отношения есть обычно не лучший вариант. Это показано в примере 6.15 в строке (6). Поскольку класс StackByInheritance из строки (4) является подклассом класса LinkedList из строки (2), то любой унаследованный метод суперкласса можно вызвать у экземпляра подкласса. Также можно вызвать методы, которые противоречат абстракции, поливаемой подклассом, что показано в строке (6). Применение агрегации в такой ситуации приводит к лучшему решению, как показано в классе QueueByAggregation в строке (3). Класс описывает операции очереди при помощи делегирования запросов нижележащему классу LinkedList . Клиенты, реализующие очередь таким способом, не могут получить доступ к нижележащим классам, поэтому не могут нарушить абстракцию.

И наследование, и агрегация способствуют инкапсуляции, так как изменения реализации локализованы в классе. Изменение контракта суперкласса может отразится на подклассах (называется волновым эффектом) и также на клиентах, которые зависят определенного поведения подклассов.

Полиморфизм достигается через наследование и реализацию интерфейса. Код, построенный на полиморфном поведении, будет по-прежнему работать без изменения, если будут добавлены новые подклассы или новые классы, реализующие интерфейс. Если отношение общее-частное не очевидно, то полиморфизм лучше достигается через агрегацию и интерфейсную реализацию.

Оставьте комментарий