Что называется иерархией наследования для чего и как ее создают

Обновлено: 28.06.2024

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

Задачи

Оглавление

Методы класса являются функциями (Function) или процедурами, подпрограммами (Sub). Функции возвращают значения, подпрограммы – нет; это соответствует вашим имеющимся знаниям по организации модульности программного кода. Область видимости методов регулируется ключевыми словами: Public (доступны всем), Private (используются только внутри самого класса), Friend (доступны внутри класса, но при условии, что создан экземпляр класса), Protected (доступны только внутри самого класса и наследуемых от него классов).

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

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

Базовым принципом объектно-ориентированного проектирования является то, что название метода должно отражать то, что метод делает. Часто требуется иметь несколько методов, которые делают похожие вещи, но принимают разные аргументы. Такие методы принято называть одинаково, используя механизм перегрузки методов. Перегрузка активно используется в библиотеках базовых классов и может быть обнаружена в информации IntelliSense, отображаемой при кодировании вызовов в Visual Studio. Первая версия метода отображается с дополнительной строкой .

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

Метод CalculateCost (), который принимает два аргумента, объект Order и Double

Private Sub CalculateCost(ByVal objOrder As Order, ByVal OrderCost As Double)
End Sub

Имеет следующую сигнатуру:

В то же время функция CalculateCost()

Public Function CalculateCost(Byval OrderCost As Double, ByVal DeliveryFree As Double) As Double
Return OrderCost+DeliveryFree+HandilingCharge
End Function

Имеет следующую сигнатуру:

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

Если вы хотите перегрузить метод, необходимо соблюдать несколько правил:

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

Разделяемые (Shared) методы

Во фрагменте кода, приведенном ниже, приведен пример разделяемого метода SaveDrivers () для сохранения объектов типа Driver в коллекции, передаваемой в базу данных.

Imports System.Collections
Module SharedMethods
Public Class Driver
Private m_DriverName As String
Public Shared Function SaveDrivers(ByRef DriverCollection As ArrayList) As Integer
Dim objDriver As New Driver
For Each objDriver In DriverCollection
‘Код для сохранения объектов в базе данных
Next
End Function
End Class
Sub Main ()
‘Это коллекция, в которой мы храним данные
Dim DriverCol As New ArrayList()
‘ Это объект класса Driver
Dim objDriver1 As New Driver()
‘Добавим объект в коллекцию
DriversCol. Add (objDriver 1)
‘Вызов разделяемого метода из объекта класса Driver. Технически это возможно, но приводит к путанице в коде. Смысл разделяемого метода в том, что он не принадлежит ни одному из объектов класса, должен вызываться из самого класса.
objDriver 1. SaveDrivers (DriversCol)
‘Поскольку метод SaveDrivers () не принадлежит какому-либо конкретному экземпляру класса Driver. Этот метод должен быть вызван из самого класса:
Driver.SaveDrivers (DriverCol)
End Sub
End Module

Разработка иерархий наследования

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

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

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

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

Рис. 8.2. Наследование и полиморфизм

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

Класс BankAccount определяет два члена-данных с именами Balance и Number, потому что все виды банковских счетов нуждаются в этой информации. Класс BankAccount также определяет операции с именами debit, Credit, PrintStatement и Funds, так как все виды банковских счетов требуют проведения этих операций.

Класс SavingAccount наследует все данные и операции класса BankAccount и определяет дополнительный член-данных (InterestRate) и дополнительную операцию (ApplyInterest), потому что накопительные счета предлагают проценты.

Класс CheckingAccount наследует все данные и операции класса BankAccoiuny и определяет дополнительный член-данных (CheksWritten) для хранения информации обо всех чеках, которые были выписаны для этого счета. Класс CheckingAccount также определяет дополнительную операцию (DebitByCheck), чтобы позволить пользователю снимать деньги по выписанному чеку.

Класс StudentsAccount наследует все данные и операции класса BankAccount и определяет дополнительный член-данных (OverdraftLimit), чтобы предоставить студенту возможность превысить кредит. Класс StudentAccount также определяет дополнительную операцию (IncreaseLimit), чтобы студенты могли повысить свой предел превышения кредита, если они попадают в сложную финансовую ситуацию.

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

Переопределение и полиморфизм

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

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

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

UML не предоставляет никакой специальной нотации для указания того, что операция переопределяемая; мы полагаем, что операция переопределяемая, если она переопределяется в одном или нескольких классах.

На следующей (рис. 8.3) диаграмме UML CheckingAccount и StudentAccount переопределяют операцию PrintStatement, предназначенную для распечатки дополнительной информации о счете. Класс StudentAccount также переопределяет операцию Founds, потому что доступные средства для студенческого счета зависят от возможности превышения кредита.

Рис. 8.3. Переопределение методов базового класса

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

Переопределенные методы у классов наследником помечаются ключевым словом (модификатором) Overrides, а у класса родителя – модификатором Overridable. Например,

Public Overridable Function PrintStatement() As String

Так будет выглядеть описание метода, подлежащего переопределению, у родительского класса BankAccount,

Public Overrides Function PrintStatement() As String

а так будет выглядеть сигнатура переопределенного метода у класса наследника StudentAccount.

Абстрактные классы и абстрактные методы

Абстрактный класс – это класс, для которого нельзя создавать экземпляры. Другими словами, клиентский код не может создать объект типа абстрактного класса. Суперклассы часто объявляются абстрактными, потому, что они не содержат достаточно информации, чтобы предоставлять реальные объекты нашей системы; суперклассы играют исключительно роль хранилища общих данных и операций, требуемых их подклассам. Для того чтобы создать такой абстрактный класс необходимо добавить ключевое слово MustInherit к оператору описания класса: Public MustInherit Class BankAccount.

Противоположностью абстрактного класса является конкретный класс. Клиентский код может создавать объекты типа конкретного класса. При описании дочернего класса следует указывать ключевое слово Inherit – наследник:

Public Class StudentAccount
Inherit BankAccount

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

Абстрактные методы в описании абстрактных классов должны быть помечены ключевым словом MustOverride. Более того, подобные методы могут иметь только заголовок (сигнатуру), но не иметь ключевых слов End Function или End Sub. Но таким образом описанные методы абстрактного класса должны обязательно быть переопределены в классах наследниках.

Критерии качества программ

Основными стандартами в области качества стали международные стандарты серии ИСО 9000, разработанные Международной организацией по стандартизации. Увеличивающаяся в настоящее время конкуренция между производителями программных продуктов приводит к установлению жестких требований к качеству этой продукции. Для того чтобы быть конкурентоспособными, организации должны применять эффективные системы, ведущие к повышению качества программ и более совершенному удовлетворению требований заказчика. Под системой качества понимается, согласно ИСО 8402, совокупность организационной структуры, методик, процессов и ресурсов, необходимых для осуществления общего руководства качеством продукции, производимой организацией.

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

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

Выводы

Мы можем создать наши собственные иерархии, содержащие суперклассы и подклассы, чтобы удовлетворить потребности нашей бизнес-модели, но необходимо помнить, что все типы данных наследуются (либо напрямую, либо через промежуточные классы) от класса общей иерархии наследования System.Object.

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

Литература

Конструкторы и деструкторы классов. Методы как интерфейс класса. Сигнатура методов: процедуры и функции. Перегрузка методов и конструкторов класса – разновидность полиморфизма. Наследование классов. Базовый класс, его конструкторы, свойства, методы. Ключевые слова MyBase, MyClass, Me. Полиморфизм. Переопределенные методы. Абстрактные классы, их назначение и устройство. Применение модификаторов доступа в базовых классах: Public, Private, Protected. Использование затенения (shadowing) методов базового класса

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

Различают несколько типов зависимых сущностей:

Характеристическая– зависимая дочерняя сущность, которая связана только с одной родительской и по смыслу хранит информацию о характеристиках родительской сущности.

Ассоциативная– сущность, связанная с несколькими родительскими сущностями. Такая сущность содержит информацию о связях сущностей.

Именующая– частный случай ассоциативной сущности, не имеющей собственных атрибутов (только атрибуты родительских сущностей, мигрировавших в качестве внешнего ключа).

Категориальная– дочерняя сущность в иерархии наследования.

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

Ключи

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

Первичный ключ (primary key)– это атрибут или группа атрибутов, однозначно идентифицирующая экземпляр сущности. Атрибуты первичного ключа на диаграмме не требуют специального обозначения – это те атрибуты, которые находятся в списке атрибутов выше горизонтальной линии.

При внесении нового атрибута в диалоге Attribute Editorдля того, чтобы сделать его атрибутом первичного ключа, нужно включить флажок Primary Key в нижней части закладки General(рис.9). На диаграмме неключевой атрибут можно внести в состав первичного ключа, воспользовавшись режимом переноса атрибутов (кнопка в палитре инструментов).

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

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

Уникальность.Два экземпляра не должны иметь одинаковых значений возможного ключа.

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

Атрибуты ключа не должны содержать нулевых значений. Значение атрибутов ключа не должно меняться в течение всего времени существования экземпляра сущности. Каждая сущность должна иметь, по крайней мере, один потенциальный ключ. Многие сущности имеют только один потенциальный ключ. Такой ключ становится первичным. Некоторые сущности могут иметь более одного возможного ключа. Тогда один из них становится первичным, а остальные альтернативными ключами. Альтернативный ключ (Alternate Key)– это потенциальный ключ, не ставший первичным.

Атрибуты, участвующие в неуникальных индексах, называются инверсионными входами (Inversion Entries). Инверсионные входы - это атрибут или группа атрибутов, которые не определяют экземпляр уникальным образом, но часто используются для обращения к экземплярам сущности. ERWin генерирует неуникальный индекс для каждого инверсионного входа.

Внешние ключи (Foreign Key)создаются автоматически, когда связь соединяет сущности: связь образует ссылку на атрибуты первичного ключа в дочерней сущности, и эти атрибуты образуют внешний ключ в дочерней сущности (миграция ключа). Атрибуты внешнего ключа обозначаются символом (FK)после своего имени.

Зависимая сущность может иметь один и тот же внешний ключ из нескольких родительских сущностей. Сущность может также получить один и тот же внешний ключ несколько раз от одного и того же родителя через несколько разных связей. Когда ERwin обнаруживает одно из этих событий, он распознает, что два атрибута одинаковы, и помещает атрибут внешнего ключа в зависимой сущности только один раз. Хотя в закладке Key Groupдиалога Attributeэтот атрибут будет входить в два внешних ключа, на диаграмме он показывается только один раз. Это комбинирование или объединение идентичных атрибутов называется унификацией.

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

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

Денормализация

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

Денормализация обычно проводится на физическом уровне модели. ERWin имеет следующие возможности по поддержке процесса денормализации:

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

• Таблицы, столбцы, индексы и домены можно создавать только на физическом уровне. В ERWin существует возможность выделения элементов модели таким образом, чтобы они не появлялись на логическом уровне. Эта возможность напрямую поддерживает денормализацию физической модели, так как позволяет проектировщику включать таблицы, столбцы и индексы в физическую модель, ориентированную на конкретную СУБД.

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

Характеристическая - зависимая дочерняя сущность, которая связана только с одной родительской и по смыслу хранит информацию о характе­ристиках родительской сущности.

Ассоциативная - сущность, связанная с несколькими родительскими сущностями. Такая сущность содержит информацию о связях сущностей.

Именующая - частный случай ассоциативной сущности, не имеющей собственных атрибутов (только атрибуты родительских сущностей, мигри­ровавших в качестве внешнего ключа).

Категориальная - дочерняя сущность в иерархии наследования.

Иерархия наследования (или иерархия категорий) представляет собой особый тип объединения сущностей, которые разделяют общие характери­стики.

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

Д ля создания категориальной связи следует:

• установить курсор на кнопке в палитре инструментов и нажать ле­вую кнопку мыши;

• щелкнуть сначала по родовому предку, а затем по потомку;

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

Для редактирования категорий нужно щелкнуть правой кнопкой мыши по символу категории и выбрать в контекстном меню пункт Subtype Relationship Editor. В диалоге Subtype Relationship можно указать атрибут - дискриминатор категории (список Discriminator Attribute Choice) и тип категории - полная/неполная (радиокнопки Complete/Incomplete).

7.2.5. Ключи

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

Первичный ключ (primary key) - это атрибут или группа атрибутов, одно­значно идентифицирующая экземпляр сущности. Атрибуты первичного ключа на диаграмме не требуют специального обозначения - это те атрибу­ты, которые находятся в списке атрибутов выше горизонтальной линии. При внесении нового атрибута в диалоге Attribute Editor для того, чтобы сделать его атрибутом первичного ключа, нужно включить флажок Primary Key в нижней части закладки General. На диаграмме неключевой атрибут можно внести в состав первичного клю­ча, воспользовавшись режимом переноса атрибутов.

В одной сущности могут оказаться несколько атрибутов или наборов атрибутов, претендующих на роль первичного ключа. Такие претенденты называются потенциальными ключами (candidate key).

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

Каждая сущность должна иметь, по крайней мере, один потенциальный ключ. Многие сущности имеют только один потенциальный ключ. Такой ключ становится первичным. Некоторые сущности могут иметь более од­ного возможного ключа. Тогда один из них становится первичным, а ос­тальные - альтернативными ключами. Альтернативный ключ (Alternate Key) - это потенциальный ключ, не ставший первичным. ERwin позволяет выделить атрибуты альтернативных ключей, и по умолчанию в дальнейшем при генерации схемы БД по этим атрибутам будет генерироваться уникаль­ный индекс.

При работе ИС часто бывает необходимо обеспечить доступ к несколь­ким экземплярам сущности, объединенным каким-либо одним признаком. Для повышения производительности в этом случае используются неуни­кальные индексы. ERwin позволяет на уровне логической модели назначить атрибуты, которые будут участвовать в неуникальных индексах. Атрибуты, участвующие в неуникальных индексах, называются Inversion Entries (инверсионные входы). Inversion Entry - это атрибут или группа атрибутов, которые не определяют экземпляр сущности уникальным образом, но часто используются для обращения к экземплярам сущности. ERwin генерирует неуникальный индекс для каждого Inversion Entry.

Создать альтернативные ключи и инверсионные входы можно в заклад­ке Key Group диалога Attribute Editor. Если щелкнуть по кнопке [. ] расположенной в правой верхней части закладки, вызывается диалог Key Group Editor. В верхней части диалога находится список ключей, в нижней - список атрибутов, доступных для включения в состав ключа (слева), и список ключевых атрибутов. Каждый вновь созданный ключ должен иметь хотя бы один атрибут. Для включения атрибута в состав ключа следует выделить его в левом списке и щелкнуть по кнопке [>]

Для создания нового ключа следует щелкнуть по кнопке New. Появля­ется диалог New Key Group. Имя нового ключа присваивается автоматически ("Alternate Key N" для альтернативного ключа и "Inversion Entry N" для инверсионного входа, где N - порядковый номер ключа).

Каждому ключу соответствует индекс, имя которого также присваивает­ся автоматически ("XAKNENTITY" для альтернативного ключа и "XIENENTITY" для инверсионного входа, где N - порядковый номер ключа, ENTITY - имя сущности). Имена ключа и индекса при желании можно изменить вручную.

На диаграмме атрибуты альтернативных ключей обозначаются как (AKn.m), где n - порядковый номер ключа, m - порядковый номер атрибута в ключе. Когда альтернативный ключ содержит несколько атрибутов, (AKn.m) ставится после каждого. По умолчанию номера альтернативных ключей и инвер­сионных входов рядом с именем атрибута на диаграмме не показываются. Для отображения номера следует в контекстном меню, которое появляется, если щелкнуть левой кнопкой мыши по любому месту диаграммы, не заня­тому объектами модели, выбрать пункт Display Options/Entities и затем включить опцию Alternate Key Designator (AK).

Внешние ключи (Foreign Key) создаются автоматически, когда связь со­единяет сущности: связь образует ссылку на атрибуты первичного ключа в дочерней сущности и эти атрибуты образуют внешний ключ в дочерней сущности (миграция ключа). Атрибуты внешнего ключа обозначаются сим­волом (FK) после своего имени.

Зависимая сущность может иметь один и тот же внешний ключ из не­скольких родительских сущностей. Сущность может также получить один и тот же внешний ключ несколько раз от одного и того же родителя через несколько разных связей. Когда ERwin обнаруживает одно из этих собы­тий, он распознает, что два атрибута одинаковы, и помещает атрибут внешнего ключа в зависимой сущности только один раз. Хотя в закладке Key Group диалога Attribute Editor этот атрибут будет входить в два внеш­них ключа, на диаграмме он показывается только один раз. Это комбини­рование или объединение идентичных атрибутов называется унификацией.

Унификация производится, поскольку правила нормализации запреща­ют существование в одной сущности двух атрибутов с одинаковыми имена­ми. В некоторых случаях этот результат соответствует действи­тельности.

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

Наследование было изобретено в 1969 году для Simula [2] и теперь используется во многих объектно-ориентированных языках программирования, таких как Java , C ++ или Python.



Существуют различные типы наследования, основанные на парадигме и конкретном языке. [7]

где подклассы наследуют свойства одного суперкласса. Один класс приобретает свойства другого класса.

где один класс может иметь более одного суперкласса и наследовать функции от всех родительских классов.

«Множественное наследование . широко должно быть очень трудно эффективно реализовать. Например, в резюме C ++ в своей книге на Objective C , Брэд Кокс фактически утверждал , что добавление множественного наследования C ++ было невозможно. Таким образом, множественное наследование , казалось более сложная задача. Поскольку я рассматривал множественное наследование еще в 1982 году и нашел простой и эффективный метод реализации в 1984 году, я не мог устоять перед проблемой. Я подозреваю, что это единственный случай, когда мода повлияла на последовательность событий . " [8]


Класс служит в качестве базового класса для производного класса B , который в свою очередь служит в качестве базового класса для производного класса C . Класс B известен как промежуточный базовый класс , поскольку он обеспечивает связь для наследования между A и C . Цепочка ABC известна как путь наследования .

Производный класс с многоуровневым наследованием объявляется следующим образом:

Этот процесс можно расширить до любого количества уровней.

Здесь один класс служит суперклассом (базовым классом) для более чем одного подкласса. Например, родительский класс A может иметь два подкласса B и C. Родительский класс B и C - это A, но B и C - это два отдельных подкласса.

Гибридное наследование - это когда происходит сочетание двух или более из вышеперечисленных типов наследования. Примером этого является случай, когда класс A имеет подкласс B, который имеет два подкласса, C и D. Это смесь как многоуровневого, так и иерархического наследования.

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

Общая форма определения производного класса: [9]

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

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

Неклассифицируемые классы

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

Непереопределяемые методы

Виртуальные методы

Если метод суперкласса является виртуальным , вызовы метода суперкласса будут отправляться динамически . Некоторые языки требуют, чтобы методы были специально объявлены как виртуальные (например, C ++ ), а в других все методы являются виртуальными (например, Java ). Вызов невиртуального метода всегда будет отправляться статически (т. Е. Адрес вызова функции определяется во время компиляции). Статическая отправка выполняется быстрее, чем динамическая, и позволяет проводить оптимизацию, например встроенное расширение .

В следующей таблице показано, какие переменные и функции наследуются в зависимости от видимости, заданной при создании класса. [10]

Наследование используется для соотнесения двух или более классов друг с другом.

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

Повторное использование кода

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

Ниже приведен пример Python.

Еще одно частое использование наследования - гарантировать, что классы поддерживают определенный общий интерфейс; то есть они реализуют одни и те же методы. Родительский класс может быть комбинацией реализованных операций и операций, которые должны быть реализованы в дочерних классах. Часто между супертипом и подтипом не происходит изменения интерфейса - дочерний класс реализует описанное поведение вместо своего родительского класса. [13]

Наследование похоже на подтипирование, но отличается от него . [14] Подтипы обеспечивает заданный тип , чтобы быть заменены на другой тип или абстракцию, и , как говорит учредить это-а отношения между подтипом и некоторой существующей абстракцией, явно или неявно, в зависимости от поддержки языка. Отношения могут быть явно выражены через наследование на языках, которые поддерживают наследование как механизм выделения подтипов. Например, следующий код C ++ устанавливает явное отношение наследования между классами B и A , где B является как подклассом, так и подтипом A , и может использоваться как A везде, где указан B (через ссылку, указатель или сам объект).

В языках программирования, которые не поддерживают наследование в качестве механизма выделения подтипов , отношения между базовым классом и производным классом являются только отношениями между реализациями (механизм для повторного использования кода) по сравнению с отношениями между типами . Наследование, даже в языках программирования, которые поддерживают наследование как механизм подтипов, не обязательно влечет за собой поведенческое подтипирование . Вполне возможно создать класс, объект которого будет вести себя некорректно при использовании в контексте, где ожидается родительский класс; см. принцип подстановки Лискова . [15] (Сравните коннотацию / обозначение .) В некоторых языках ООП понятия повторного использования кода и подтипа совпадают, потому что единственный способ объявить подтип - это определить новый класс, который наследует реализацию другого.

Ограничения дизайна

Широкое использование наследования при разработке программы накладывает определенные ограничения.

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

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

Наследование реализации вызывает споры среди программистов и теоретиков объектно-ориентированного программирования, по крайней мере, с 1990-х годов. Среди них есть авторы Design Patterns , которые вместо этого выступают за наследование интерфейсов и предпочитают композицию наследованию . Например, шаблон декоратора (как упоминалось выше ) был предложен для преодоления статического характера наследования между классами. В качестве более фундаментального решения той же проблемы ролевое программирование вводит отчетливую взаимосвязь, в которой сочетаются свойства наследования и композиции в новую концепцию. [ необходима цитата ]

Сообщается, что изобретатель Java Джеймс Гослинг высказался против наследования реализации, заявив, что он не включил бы его, если бы он перепроектировал Java. [16] Языковые конструкции, которые отделяют наследование от подтипов (наследование интерфейсов), появились еще в 1990 году; [18] современным примером этого является язык программирования Go .

Сложное наследование или наследование, используемое в недостаточно зрелом дизайне, может привести к проблеме йо-йо . Когда в конце 1990-х годов наследование использовалось в качестве основного подхода к структурированию кода в системе, разработчики, естественно, начали разбивать код на несколько уровней наследования по мере роста функциональности системы. Если команда разработчиков объединила несколько уровней наследования с принципом единой ответственности, она создала много супертонких слоев кода, многие из которых имели бы только 1 или 2 строки кода на каждом уровне. До того, как команды на собственном горьком опыте узнали, что 2 или 3 уровня являются оптимальным числом уровней, уравновешивающим преимущество повторного использования кода с увеличением сложности с каждым уровнем, не было ничего необычного в работе над фреймворками наследования с 10 и до 30 уровней. Например, 30 уровней сделали отладку серьезной проблемой, просто чтобы знать, какой уровень нужно отлаживать. PowerBuilder создал одну из лучших библиотек кода, которая в основном использовала наследование, она была построена с 3-4 уровнями. Количество слоев в библиотеке наследования имеет решающее значение и должно составлять не более 4 слоев, иначе библиотека станет слишком сложной и требует много времени для использования.

Другая проблема с наследованием заключается в том, что подклассы должны быть определены в коде, что означает, что пользователи программы не могут добавлять новые подклассы. Другие шаблоны проектирования (такие как Entity – component – ​​system ) позволяют пользователям программы определять варианты объекта во время выполнения.

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