В чем отличие полиморфизма от наследования

Обновлено: 28.06.2024

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

Задачи

Оглавление

Базовые принципы объектно-ориентированного программирования

  • Инкапсуляция: как объекты прячут свое внутреннее устройство;
  • Наследование: как в этом языке поддерживается повторное использование кода;
  • Полиморфизм: как в этом языке реализована поддержка выполнения нужного действия в зависимости от типа передаваемого объекта?

ИНКАПСУЛЯЦИЯ

Класс DBReader скрывает за счет инкапсуляции подробности открытия и закрытия баз данных

Dim f As New DBReader
f.Open("C:\foo.mdb")

Выполняем с базой данных нужные нам действия

Наш класс DBReader инкапсулирует внутренние подробности того, как именно он обнаруживает, загружает, выполняет операции и, в конце концов, закрывает файл данных.

С философией инкапсуляции тесно связан еще один принцип – сокрытие всех внутренних данных (то есть переменных-членов) класса. В идеале все внутренние переменные-члены класса должны быть определены как Private. В результате обращение к ним из внешнего мира будет возможно только при помощи открытых функций-членов. Такое решение, помимо всего прочего, защищает вас от возможных ошибок, поскольку открытые данные очень просто повредить.

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

ПОЛИМОРФИЗМ

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

Для примера мы вновь обратимся к нашей иерархии геометрических фигур. Предположим, что в классе Shape (геометрическая фигура) определена функция Draw () – рисование, которая не принимает параметров и ничего не возвращает. Поскольку геометрические фигуры бывают разными, и каждый тип фигуры потребует изображать своим собственным уникальным способом, скорее всего, нам потребуется в производных классах (таких как Hexagon – шестиугольник и Circle – окружность) создать свой собственный метод Draw (), заместив им метод Draw () базового класса (рис. 7.2).

Классический полиморфизм позволяет определять возможности всех производных классов при создании базового класса. Например, в нашем случае вы можете быть уверены, что метод Draw () в том или ином варианте присутствует в любом классе, производном от Shape. К достоинствам классического полиморфизма можно отнести также и то, что во многих ситуациях вы сможете избежать создания повторяющихся методов для выполнения схожих операций (типа DrawCircle (), DrawRectangle (), DrawHexagon () и так далее).

Рис. 7.2. Полиморфизм

Мы используем ключевое слово Class для определения класса. Класс может содержать данные и методы, выполняющие действия. В следующей таблице представлены виды-членов, которые мы можем определить в классе:

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

Свойство похоже на переменную, за исключением того, что его значение должно возвращаться и устанавливаться с помощью методов Get и Set класса

Константа – это значение, доступное только для чтения и используемое всеми объектами класса

Метод – это функция или процедура, которая предоставляет некоторую функциональность объектам класса. Мы можем также использовать ключевое слово Shared для определения методов, которые относятся к самому классу, а не к конкретным его объектам

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

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

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

Class MyClass 1
End Class
Класс MyClass 1 неявным образом объявлен доступным для всех сборок; это означает, что к нему можно осуществлять доступ из любого другого кода и из любой сборки.

Public Class MyClass 2
End Class
Класс MyClass 2 явным образом объявлен доступным для всех сборок.

Friend Class MyClass 3
End Class
Класс MyClass 3 виден в текущей сборке, но не виден извне.

Private Class MyClass 4
End Class
Класс MyClass 4 виден только в области его объявления. Классы, объявленные с помощью ключевого слова Private, используются для реализации логики внутри класса, при этом, не предоставляя этой логики другим частям приложения.

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

Свойства являются важной частью разработки нашего класса. Например, следующая диаграмма показывает небольшой фрагмент модели объекта, представляющего человека, работающего в компании. Используется нотация UML (Unified Modeling Language) (рис. 7.3).

Рис. 7.3. Диаграмма классов

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

Мы можем опустить процедуру Get или Set для свойства:

Скалярные свойства. Скалярное свойство представляет одно значение. Значение может быть простым, таким как Integer или String, или сложным, таким как объект типа DateTime или Color.

Индексированные свойства. Индексированное свойство представляет коллекцию (набор) значений. Клиентский код использует синтаксис массива для доступа к конкретным значениям из этой коллекции.

Public Class Person
Private MName As String
Public Property Name() As String
Get
Return MName
End Get
Set(ByVal Value As String)
MName = Value
End Set
End Property
End Class

Следующий фрагмент кода показывает, как использовать свойство Name, определенное в классе Person

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim APerson As New Person
APerson.Name = TextBox1.Text
MsgBox(APerson.Name)
End Sub

Рассмотрим описание свойств для чтения/записи, только для чтения и только для записи. Свойство Name в предыдущем примере являлось свойством для чтения/записи. Определим свойство только для чтения на примере информации о дне рождения человека, а свойство только для записи на примере свойства EmailAlias для установки имени e-mail-адреса человека. Имя e-mail-адреса человека представляет первую часть e-mail - адреса сотрудника фирмы, стоящую перед именем домена. Это имя может быть изменено, но к нему никогда нельзя осуществить доступ (вместо этого, клиентская программа получает полный e-mail-адрес). Свойство EmailAddress для получения полного e-mail адреса. E-mail адрес формируется каждый раз при обращении к нему путем добавления имени домена компании к имени адреса человека.

Public Class Person
Private MName As String
Private MDob As DateTime
Private MEmailAlias As String
Public Sub New(ByVal name As String, ByVal Dob As DateTime)
MName = name
MDob = Dob
End Sub
Public Property Name() As String
Get
Return MName
End Get
Set(ByVal Value As String)
MName = Value
End Set
End Property
Public ReadOnly Property DOB() As DateTime
Get
Return MDob
End Get
End Property
Public WriteOnly Property EmailAlias() As String
Set(ByVal Value As String)
MEmailAlias = Value
End Set
End Property
Public ReadOnly Property EmailAddress() As String
Get
Return MEmailAlias & "@MyCompany.com"
End Get
End Property
End Class

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

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim APerson As New Person(" Петров В. Н.", New DateTime(1978, 7, 2))
TextBox1.Text = APerson.Name
TextBox2.Text = APerson.DOB.ToLongDateString
APerson.EmailAlias = "Petrov"
TextBox3.Text = APerson.EmailAddress
End Sub

Разделяемое свойство (Shared-свойство) представляет собой часть информации, общей для всего класса, которую мы хотим представить как часть класса, а не как часть конкретного экземпляра класса. Клиентский код получает доступ к разделяемым свойствам через класс, а не через экземпляр этого класса. Следующий пример показывает, как определить разделяемое свойство Domain для представления имени домена, которое используется как часть e-mail-адреса человека.

В коде класса появится еще один фрагмент кода:

Public Class Person
Private Shared MDomain As String
Public Shared Property Domain() As String
Get
Return MDomain
End Get
Set(ByVal Value As String)
MDomain = Value
End Set
End Property
Public ReadOnly Property EmailAddress() As String
Get
Return MEmailAlias & "@" & MDomain
End Get
End Property

Воспользоваться этим разделяемым свойством можно, например, таким образом:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim APerson As New Person(" Петров В. Н.", New DateTime(1978, 7, 2))
Person.Domain = "MyCoolSite.com"
APerson.EmailAlias = "Petrov"
TextBox1.Text = APerson.EmailAddress
End Sub

Обратите внимание на следующие моменты в этом примере:

  • Свойство Domain разделяемое.
  • Свойство EmailAddress использует имя домена для генерации полного E-mail - адреса человека.
  • Процедура Button1_Click устанавливает свойство Domain, используя имя класса Person, а не имя экземпляра класса APerson.

Выводы

Вопросы для самопроверки

  1. Как вы считаете, не находятся ли в противоречии процессы инкапсуляции, как процесса сокрытия кода реализации свойств и методов класса, и полиморфизма, как процесса изменения, расширения кода свойств и методов классов наследников?
  2. Если при описании класса не было объявлено области его видимости, то какую область видимости такой класс будет иметь по умолчанию?
  3. Чем отличаются атрибуты класса от свойств класса?
  4. Для чего применяются свойства с ключевым словом ReadOnly?
  5. Для чего применяются свойства с ключевым словом WriteOnly?
  6. В чем отличие скалярных свойств класса от индексированных свойств класса?
  7. Для чего используются разделяемые Shared-свойства класса? В чем состоит особенность их использования?

Литература

Описание свойств класса, как открытых или закрытых переменных. Использование конструкторов класса. Ограничение доступа к свойствам. Разделяемые переменные в классах. Тестирование классов. Синхронизация элементов управления формы. Классы работы с векторами и простыми дробями

В чем разница между наследованием и полиморфизмом в Java

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

Объектно-ориентированное программирование (ООП) является распространенной парадигмой программирования в разработке программного обеспечения. Это помогает моделировать реальные сценарии, используя объекты. Java - это язык программирования, который поддерживает ООП. Класс - это проект, и он помогает создавать объекты. У класса есть атрибуты и поведение. Атрибуты также называются свойствами, а поведение - методами. Двумя основными столпами ООП являются наследование и полиморфизм.

Ключевые области покрыты

1. Что такое наследование в Java
- определение, функциональность
2. Что такое полиморфизм в Java
- определение, функциональность
3. В чем разница между наследованием и полиморфизмом в Java
- Сравнение основных различий

Основные условия

Класс, Наследование, Перегрузка, Переопределение, Полиморфизм, Java


Что такое наследование в Java

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


Рисунок 1: Класс А


Рисунок 2: Класс B


Рисунок 3: Классовый тест

Класс A имеет метод sum и добавляет два значения. Класс B расширяет A. У него есть метод с именем sub. Вычитает два значения. Тестовый класс имеет основной метод. Объект obj является объектом типа B. Поскольку класс B наследует класс A, объект может использовать свойства и методы класса A. Следовательно, объект может вызывать как sum, так и sub-методы.

Что такое полиморфизм в Java

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

перегрузка


Рисунок 4: Java-программа с переопределением

Класс перегрузки имеет два метода с тем же именем, что и сумма. Метод суммы в строке 5 не принимает никаких параметров. Он добавляет два значения 10 и 20 и возвращает результат, равный 30. Более того, метод суммы в строке 11 получает два параметра. Он добавляет эти два значения и возвращает результат 50. Объект является объектом в методе main. Obj.sum () вызывает метод sum в строке 5, а obj.sum (20,30) вызывает метод sum в строке 11. Тот же объект вызывает соответствующий метод соответствующим образом. Следовательно, один и тот же объект имеет различное поведение в зависимости от ситуации.

Переопределение


Рисунок 5: Класс транспортного средства


Рисунок 6: Класс автомобиля


Рисунок 7: Тестовый класс

Разница между наследованием и полиморфизмом в Java

Определение

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

Реализация

Более того, реализация наследования происходит на уровне класса, тогда как реализация полиморфизма происходит на уровне метода.

использование

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

Заключение

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

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

Такой подход является основой объектно-ориентированного программирования. Это позволяет работать с объектами, не вдаваясь в особенности их реализации. В каждом конкретном случае применяется тот или иной подход: инкапсуляция, полиморфизм или наследование. Например, при необходимости обратиться к скрытым данным объекта, следует воспользоваться инкапсуляцией, создав, так называемую, функцию доступа или свойство.

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

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

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

Пользователь может взаимодействовать с объектом только через этот интерфейс. Реализуется с помощью ключевого слова: public.

Пользователь не может использовать закрытые данные и методы. Реализуется с помощью ключевых слов: private, protected, internal.))

Инкапсуляция — один из четырёх важнейших механизмов объектно-ориентированного программирования (наряду с абстракцией, полиморфизмом и наследованием).

Сокрытие реализации целесообразно применять в следующих случаях:

предельная локализация изменений при необходимости таких изменений,

прогнозируемость изменений (какие изменения в коде надо сделать для заданного изменения функциональности) и прогнозируемость последствий изменений.


Наследование [ ]

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

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

Простое наследование: [ ]

Класс, от которого произошло наследование, называется базовым или родительским (англ. base class). Классы, которые произошли от базового, называются потомками, наследниками или производными классами (англ. derived class).

Множественное наследование [ ]

При множественном наследовании у класса может быть более одного предка. В этом случае класс наследует методы всех предков. Достоинства такого подхода в большей гибкости. Множественное наследование реализовано в C++. Из других языков, предоставляющих эту возможность, можно отметить Python и Эйфель. Множественное наследование поддерживается в языке UML.

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

Полиморфизм [ ]

Полиморфи́зм — возможность объектов с одинаковой спецификацией иметь различную реализацию.

Язык программирования поддерживает полиморфизм, если классы с одинаковой спецификацией могут иметь различную реализацию — например, реализация класса может быть изменена в процессе наследования[1].

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

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

  • внешняя общность проявляется как одинаковый набор методов с одинаковыми именами и сигнатурами (именем методов и типами аргументов и их количеством);
  • внутренняя общность — одинаковая функциональность методов. Её можно описать интуитивно или выразить в виде строгих законов, правил, которым должны подчиняться методы. Возможность приписывать разную функциональность одному методу (функции, операции) называется перегрузкой метода (перегрузкой функций, перегрузкой операций).

Формы полиморфизма [ ]

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

Параметрические метод [ ]

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

Параметрические типы. [ ]

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

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

Мир (готовы ли вы уже в самом начале лекции погрузиться в водовороты жизни?) полон беспорядка. Возможно, Природа ненавидит беспорядок, а может быть — и нет, я в этом не уверен. Ваша точка зрения во многом зависит от того, кого вы читали — Платона, Аристотеля, Канта. Наука определенно борется с беспорядком. Здравые рассуждения о мире требуют порядка.

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

Что касается нас, то мы займемся пониманием наследования — наукой, инженерией, поиском систематических, хорошо структурированных описаний. Одним из важнейших инструментов, помогающих в этом поиске, является иерархическая классификация, известная также как таксономия .

Объекты в этом случае естественные, а классификация искусственная.

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

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

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

Подобно математикам, мы, программисты, имеем дело с абстрактными объектами — творениями нашего воображения — и не можем обвинять никого, кроме себя, при обнаружении беспорядка в мире наших объектов. И нам, как и всем, нужно преобразовать беспорядок, создавая подобие порядка. Возможно, нам это нужно больше, чем кому-либо, поскольку мы являемся чемпионами в создании энтропии, ибо наши программы способны создать самую немыслимую путаницу, которая только возможна. Говорят, что "человеку свойственно ошибаться, но путаницу несомненно создает компьютер" ; добавим к этому: "или компьютерный программист".

Мы уже встречались с наследованием и связанным с ним ключевым словом inherit — фактически уже в самом первом примере — как со способом получения в нашем классе преимуществ от работы, выполненной в другом классе. Но это только один из аспектов наследования, который применим к классу, рассматриваемому как модуль — собрание под одной обложкой полезных компонентов. Наследование становится куда более интересным — за счет новых приемов, таких как полиморфизм и динамическое связывание — для приложений, где используется другая роль классов: роль типов данных. В этой роли класс описывает коллекцию объектов периода выполнения, таких как линии метро или такси.

Такси и транспортные средства

Да, такси. В лекциях предыдущих курсов мы отдали дань нашим простонародным привычкам и путешествовали в метро, где кого только не встретишь. Сейчас же поднимемся на следующую ступеньку и будем распоряжаться собственным транспортным средством.

Наследуемые компоненты

Класс TAXI из TRAFFIC обеспечивает — вы можете это проверить — такой компонент, как

Другим компонентом класса является office , представляющий диспетчерский офис службы такси.

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

  • такси имеет пассажиров (в противном случае комментарий для компонента take не имел бы смысла: кого следует доставить из одной точки в другую?). Класс должен иметь команду для посадки пассажиров и запрос, позволяющий выяснить текущее число пассажиров;
  • в любой момент такси имеет текущую позицию.

Где же находятся соответствующие свойства? Ответ можно найти, взглянув в начало объявления класса:

Класс TAXI наследует от VEHICLE ; и в самом деле, если посмотреть на класс VEHICLE , то можно найти команды load , для посадки пассажиров в транспортное средство, так же как и unload — для высадки, и запрос count , дающий текущее число пассажиров. Теперь, обратившись к началу объявления класса VEHICLE , вы увидите:

Класс VEHICLE наследует от класса MOVING , который описывает движущиеся объекты и имеет запрос position .

Классы VEHICLE и MOVING объявляются не просто как class , а как deferred class . Мы вскоре познакомимся детально с концепцией отложенного класса, указывающего на то, что не все его компоненты полностью заданы; реализация некоторых из них оставлена его потомкам — классам, наследующим от него.

Глядя, как эти три класса описывают типы объектов периода выполнения — такси, транспортные средства, движущиеся объекты, — мы понимаем, о чем говорит нам наследование. Оно устанавливает, что в системе Traffic любое такси может рассматриваться как транспортное средство, которое, в свою очередь, является движущимся объектом. В частности, все свойства класса MOVING применимы к целям типа VEHICLE и TAXI , и все свойства класса VEHICLE применимы к целям типа TAXI .

Термины наследования

Как обычно, помогает точная терминология.

Определения: наследник, родитель, (правильный) потомок и предок

Если B наследует от A ( В Eiffel A перечислено в предложении inherit класса B), то B наследник A, а A - родитель B. Потомками класса является сам класс и (рекурсивно) потомки его наследников. Сам класс не включается в число правильных потомков. Предок и правильный предок являются обращенными понятиями по отношению к потомкам.

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

В литературе иногда встречается термин "подкласс", означающий иногда наследника, иногда правильного потомка. Аналогично встречается и термин "суперкласс".

На рисунке ниже, иллюстрирующем наш пример, все классы являются потомками класса MOVING . Все классы — его правильные потомки, за исключением самого класса. Правильными предками класса TAXI являются классы VEHICLE и MOVING .

Рядом с каждым классом на рисунке показан один или два компонента, вводимые этим классом. Обратите внимание на соглашение, принятое при представлении наследования на диаграммах: наследование изображается в виде одиночной стрелки, Напоминаю, что другое отношение — "клиентское" — изображается двойной стрелкой.

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

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

От вас не требуется вручную рисовать диаграмму, отображающую отношения между классами. Если классы скомпилированы, то инструментарий Diagram Tool EiffelStudio создаст диаграмму. Достаточно просто щелкнуть вкладку Diagram, и появится требуемая диаграмма:


Если класс не существует, но вы занимаетесь его проектированием, то также можно пользоваться этим инструментарием. Можно графически описать классы и задать связи (наследования и клиентские) между ними. В этом режиме будут автоматически сгенерированы соответствующие тексты классов.

Компоненты, приходящие от высших авторитетов

Мы можем теперь оценить новинку, вводимую наследованием. Понятие "компоненты класса" больше не означает только компоненты, заданные в классе, но и компоненты, наследуемые от родителя. Так, объявив объекты m: MOVING, v: VEHICLE, t: TAXI, мы можем помимо прочего писать:

Возможны и другие вызовы, такие как:

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

Определения: компоненты класса, непосредственные, наследуемые, вводимые

Компонент класса - это одно из двух:

  • наследуемый компонент, если это компонент одного из родителей класса;
  • непосредственный компонент, если он объявлен в классе и не наследуется. В этом случае говорят, что класс вводит компонент.

Заметьте, что определение включает рекурсию.

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

Плоский облик

Как тогда получить полную картину? Плоским обликом класса называется искусственно сконструированная версия, которая включает все компоненты, непосредственные и наследуемые. Это не то, что вы пишете, создавая класс, но лишь облик, подобный контрактному облику, который, как мы видели, дает нам свободную от реализации версию класса. EiffelStudio создает этот взгляд на класс, этот облик, для чего достаточно выбрать класс и щелкнуть по кнопке "Flat view" :


Результат похож на обычный текст класса (с некоторой новой нотацией, которая позже прояснится). Отметьте появление нового вида комментариев, здесь для компонента LINKED_LIST :

 Плоский облик

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

Контрактный облик класса выводится (как объяснялось ранее, удаляется закрытая информация) не из исходного текста класса, а из его плоского облика. Для клиента класса обычно все равно, наследованы ли экспортируемые классом компоненты или введены самим классом. Интерфейсный облик класса позволяет ограничиться только непосредственными компонентами.

Читайте также: