Тверской государственный университет
Опубликован: 13.09.2006 | Доступ: свободный | Студентов: 3491 / 369 | Оценка: 4.65 / 4.29 | Длительность: 30:37:00
Специальности: Программист, Менеджер
Лекция 6:

Семейство классов и наследование интерфейсов. Динамические классы

< Лекция 5 || Лекция 6: 123456 || Лекция 7 >
Проект "Люди и Машины"

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

Схема семейства классов "Люди и Машины"

Рис. 5.3. Схема семейства классов "Люди и Машины"

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

Option Explicit
'Класс Машина
'Свойства класса
Private Марка As String
Private ДатаВыпуска As Date
Private Цвет As String

'Конструкторы класса

Private Sub Class_Initialize()
		Марка = "Форд"
		ДатаВыпуска = "20.07.1925"
		Цвет = "Вишневый"
End Sub

Public Sub НоваяМашина(M As String, D As Date, C As String)
		Марка = M
		ДатаВыпуска = D
		Цвет = C
End Sub

'Методы класса
Public Sub PrintDataCar()
		Debug.Print "Марка = ", Марка
		Debug.Print "ДатаВыпуска = ", ДатаВыпуска
		Debug.Print "Цвет = ", Цвет
		
End Sub

Public Property Get МаркаМашины() As String
		МаркаМашины = Марка
End Property

Public Property Get ЦветМашины() As String
		ЦветМашины = Цвет
End Property

Public Property Get ДатаВыпускаМашины() As Date
		ДатаВыпускаМашины = ДатаВыпуска
End Property
5.4.

У класса Машина имеется:

  • три закрытых свойства, обладающие статусом Read only,
  • два конструктора, - закрытый конструктор по умолчанию и открытый конструктор НоваяМашина,
  • один типичный для классов открытый метод, задающий печать свойств - PrintDataCar

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

Option Explicit
'Класс ВладелецМашины
'Наследует интерфейсы классов Личность и Машина
Implements Машина
Implements Личность

'Свойства класса
Private Сам As Личность
Private ЕгоМашина As Машина

Private Sub Class_Initialize()
 Set Сам = New Личность
 Set ЕгоМашина = New Машина
End Sub

'Реализация интерфейсов класса Личность
Private Sub Личность_CopyPerson(You As Личность)
		Сам.CopyPerson (You)
End Sub

Private Sub Личность_InitPerson(ByVal FN As String, ByVal LN As String, ByVal DoB As Date)
 'Инициализация личности
		Сам.InitPerson FN, LN, DoB
End Sub

Private Sub Личность_PrintPerson()
 'Печать в отладочном окне Immediate
		Сам.PrintPerson
End Sub

Private Sub Личность_SayWhoIs()
'	Вывод сообщения о поле и возрасте владельца машины
		Dim StrMsg As String
		StrMsg = "Думаю, Владелец машины марки: " & _
		ЕгоМашина.МаркаМашины & " это - "
		If Сам.WhoIs Then
			If Year(Сам.ВашаДатаРождения) > 1967 Then
				StrMsg = StrMsg & "молодая девушка!"
			Else: StrMsg = StrMsg & "женщина!"
			End If
		Else
			If Year(Сам.ВашаДатаРождения) > 1967 Then
				StrMsg = StrMsg & "молодой человек!"
			Else: StrMsg = StrMsg & "мужчина!"
			End If
		End If
		MsgBox (StrMsg)
End Sub

Private Function Личность_WhoIs() As Boolean
		Сам.WhoIs
End Function

Private Property Let Личность_ВашаДатаРождения(ByVal NewValue As Date)
		Сам.ВашаДатаРождения = NewValue
End Property

Private Property Get Личность_ВашаДатаРождения() As Date
		'Зажигает событие ДеньРождения
		'в зависимости от значения текущей даты
		Личность_ВашаДатаРождения = Сам.ВашаДатаРождения
End Property

Private Property Let Личность_ВашаФамилия(ByVal NewValue As String)
	'Зажигает событие ИзменениеФамилии
		Сам.ВашаФамилия = NewValue
End Property

Private Property Get Личность_ВашаФамилия() As String
		Личность_ВашаФамилия = Сам.ВашаФамилия
End Property

Private Property Let Личность_ВашеИмя(ByVal NewValue As String)
		Сам.ВашеИмя = NewValue
End Property

Private Property Get Личность_ВашеИмя() As String
		Личность_ВашеИмя = Сам.ВашеИмя
End Property

Private Property Let Личность_ВашеОтчество(ByVal NewValue As String)
	Сам.ВашеОтчество = NewValue
End Property

Private Property Get Личность_ВашеОтчество() As String
		Личность_ВашеОтчество = Сам.ВашеОтчество
End Property

'Реализация интерфейсов класса Машина
Private Property Get Машина_ДатаВыпускаМашины() As Date
	Машина_ДатаВыпускаМашины = ЕгоМашина.ДатаВыпускаМашины
End Property

Private Property Get Машина_МаркаМашины() As String
		Машина_МаркаМашины = ЕгоМашина.МаркаМашины
End Property

Private Property Get Машина_ЦветМашины() As String
		Машина_ЦветМашины = ЕгоМашина.ЦветМашины
End Property

Private Sub Машина_PrintDataCar()
		ЕгоМашина.PrintDataCar
 End Sub

Private Sub Машина_НоваяМашина(M As String, D As Date, C As String)
 ЕгоМашина.НоваяМашина M, D, C
End Sub

'Собственный интерфейс класса ВладелецМашины
'Public методы - интерфейс Владельца машины
Public Sub InitCarOwner(FN As String, LN As String, DoB As Date, _
		Marka As String, DB As Date, Color As String)
		'Инициализация данных о хозяине и его машине
		Личность_InitPerson FN, LN, DoB
		Машина_НоваяМашина Marka, DB, Color
End Sub

 Public Sub ConnectOwnerAndCar(pers As Личность, car As Машина)
		'соединяет данные о хозяине и его новой машине
		Сам.CopyPerson pers
		Машина_НоваяМашина car.МаркаМашины, car.ДатаВыпускаМашины, _
			car.ЦветМашины
End Sub

Public Sub PrintOwnerData()
		Личность_PrintPerson
		Debug.Print " владеет машиной: "
		Машина_PrintDataCar
End Sub
5.5.

Прокомментируем этот довольно длинный текст. Вот на какие моменты следует обратить внимание:

  1. Для того, чтобы реализовать полноценное наследование свойств и поведения родителей, а не только наследование интерфейсов, используется встраивание, - в классе определены свойства Сам класса Личность и свойство ЕгоМашина класса Машина.
  2. В конструкторе по умолчанию Class_Initialize, вызываемом при создании объекта класса ВладелецМашины, объекты Сам и ЕгоМашина инициализируются, - будут вызваны их конструкторы по умолчанию класса Личность и класса Машина.
  3. При наследовании интерфейсов класса Личность почти для всех методов наследовалось поведение родительского класса. Реализуется это достаточно просто - вызывается соответствующий метод объекта Сам.
  4. Для метода SayWhoIs поведение переопределено. В данном случае при вызове метод учитывает специфику класса и сообщает некоторые данные, как о владельце, так и о его машине. Так что при вызове этого метода у просто личностей и у личностей, являющихся владельцами машин, результат будет различным.
  5. Поскольку родительский класс допускает события, то и у потомка при вызове методов зажигаются соответствующие события.
  6. Интерфейс класса Машина реализуется подобным образом. Полностью наследуется поведение родительского класса.
  7. Собственный интерфейс класса довольно прост. У объектов этого класса нет новых дополнительных свойств, - все спрятано в свойствах встроенных объектов Сам и ЕгоМашина. Интерфейс составляют два конструктора и метод, осуществляющий печать данных. Обратите внимание, один конструктор InitCarOwner позволяет сконструировать новый объект класса по терминальным данным, характеризующим личность и его машину. Второй конструктор ConnectOwnerAndCar в качестве параметров использует ранее созданные объекты классов Личность и Машина, соединяя эти два объекта в один объект класса ВладелецМашины.

На рисунке 5.4 отражен один из моментов проектирования класса. Можно видеть, что в окне кода список объектов содержит объекты Class, Личность и Машина. При выборе объекта Class правый раскрывающийся список покажет список стандартных событий класса, а при выборе объектов Личность и Машина будет раскрываться список наследуемых методов.

Проектирование класса ВладелецМашины

увеличить изображение
Рис. 5.4. Проектирование класса ВладелецМашины

Определение семейства классов дано и нам осталось продемонстрировать работу с различными объектами этих классов. Главное, что хотелось показать, это совместную работу с объектами разных классов. Начнем с программного текста:

Option Explicit
'Модуль Примеры
Public FriendOne As New Личность
Public FriendTwo As New Личность
Public FriendThree As New Личность
Public carOne As New Машина
Public carTwo As New Машина
Public carThree As New Машина
Public OwnerOne As New ВладелецМашины
Public OwnerTwo As New ВладелецМашины
Public OwnerThree As New ВладелецМашины
Public FOne As New Личности

Public Sub Люди()
'Вызывается конструктор с параметрами
'и происходит знакомство с объектами
FriendOne.InitPerson FN:="Станислав", LN:="Федотов", _
		DoB:="21.05.39"
FriendTwo.InitPerson FN:="Катя", LN:="Павлова", _
		DoB:="22.03.79"
FriendThree.InitPerson FN:="Остап", LN:="Бендер", DoB:="23.07.1910"
FriendOne.PrintPerson
FriendTwo.PrintPerson
FriendOne.SayWhoIs
FriendTwo.SayWhoIs
'Связывание с двойниками.
'Теперь объекты могут реагировать на события!
FOne.Connect
End Sub

Public Sub Cars()
'Вызывается конструктор с параметрами
carOne.НоваяМашина "Антилопа", "12.12.12", "Неопределенный"
carTwo.НоваяМашина "Москвич", "12.11.98", "Морская волна"
carThree.НоваяМашина "Jeep", "23.05.97", "Orange"
End Sub

Public Sub CarOwners()
		OwnerOne.ConnectOwnerAndCar FriendOne, carTwo
		OwnerTwo.ConnectOwnerAndCar FriendThree, carOne
		OwnerThree.InitCarOwner FN:="Юрий", LN:="Вегера", _
		DoB:="21.08.34", Marka:="Газ69", DB:="20.01.76", Color:="Зеленый"
		OwnerOne.PrintOwnerData
		OwnerTwo.PrintOwnerData
		OwnerThree.PrintOwnerData
End Sub
Public Sub CallEvents()
		Dim DoB As Date
		'Вызов методов приведет к возникновению событий!
		'При замене фамилии возникнет событие ИзменениеФамилии
		'Заметьте, не всегда фамилия будет изменена!
		FriendOne.ВашаФамилия = "Фидотов"
		FriendTwo.ВашаФамилия = "Волконская"
		'При попытке узнать дату рождения
		'может быть вызван обработчик события ДеньРождения.
		DoB = FriendOne.ВашаДатаРождения
		Debug.Print DoB
		DoB = FriendTwo.ВашаДатаРождения
		Debug.Print DoB
		FriendOne.PrintPerson
		FriendTwo.PrintPerson
		
	'События не наследуются
		Set FriendOne = OwnerTwo
		'Нельзя связать теперь объект FriendOne с двойником
		'FOne.Connect
		FriendOne.ВашаФамилия = "Воробьянинов"
		FriendOne.PrintPerson

End Sub
Public Sub Группа()
		Const SizeGroup = 6
		Const SizeGarage = 6
		Dim i As Byte
		Dim Group(1 To SizeGroup) As Личность
		Dim Гараж(1 To SizeGarage) As Машина
		
		Set Group(1) = FriendOne
		Set Group(2) = FriendTwo
		Set Group(3) = FriendThree
		Set Group(4) = OwnerOne
		Set Group(5) = OwnerTwo
		Set Group(6) = OwnerThree
		For i = 1 To SizeGroup
			Group(i).SayWhoIs
		Next i

		Set Гараж(1) = carOne
		Set Гараж(2) = carTwo
		Set Гараж(3) = carThree
		Set Гараж(4) = OwnerOne
		Set Гараж(5) = OwnerTwo
		Set Гараж(6) = OwnerThree
		For i = 1 To SizeGarage
			Гараж(i).PrintDataCar
		Next i
End Sub

Public Sub ЛюдиИМашины()
		Люди
		Cars
		CarOwners
		Группа
		PolyMorf FriendTwo
		PolyMorf OwnerTwo
End Sub

Public Sub PolyMorf(One As Личность)
		One.SayWhoIs
End Sub
5.6.

Дадим теперь комментарии к этому тексту:

  1. В модуле Примеры вначале объявляются по три объекта классов Личность, Машина и ВладелецМашины. В методах Люди, Cars, CarOwners происходит инициализация этих объектов. Некоторые из этих личностей уже встречались в предыдущих примерах, но появился Остап Бендер и его знаменитая машина.
  2. Обратите внимание на создание объектов Owner класса ВладелецМашины, они создаются разными конструкторами.
  3. Основную работу выполняет метод Группа. Здесь создается массив элементов Group из трех объектов Friend и трех объектов Owner, принадлежащих разным классам. Тем не менее все эти объекты обрабатываются в едином цикле и для них вызывается метод SayWhoIs, который, как мы говорили ранее по-разному работает для объектов класса Личность и класса ВладелецМашины. Так что простой цикл по элементам Group демонстрирует реализацию полиморфизма.
  4. В методе Группа создана и продемонстрирована работа с еще одной группой элементов, - массивом Garage. Эту группу составляют объекты класса Машина и объекты класса ВладелецМашины. Обратите внимание, одни и те же объекты Owner входят в обе группы элементов и могут быть обработаны нужным образом.
  5. В процессе обработки массива Garage вызывается метод PrintDataCar, который унаследован объектами класса ВладелецМашины.
  6. В модуле Примеры приведена полиморфная функция PolyMorf(One As Личность ), с формальным параметром родительского класса Личность. Она дважды вызывается в процедуре ЛюдиИМашины с фактическими параметрами разных классов.
  7. Процедура ЛюдиИМашины является процедурой, организующей всю работу, она поочередно запускает все перечисленные ранее процедуры. Сама она вызывается в обработчике события Click командной кнопки, встроенной в наш тестовый документ.

В процессе выполнения этой процедуры откроется серия диалоговых окон, где вначале будет сказано, кто такой Федотов и Катя, будет спрошено отчество Вегеры Юрия, затем опять будет рассказано о Федотове, Кате и Бендере, после чего появится информация о владельцах машин разных марок. На последнем этапе опять будет рассказано о Кате и владельце машины "Антилопа". Нам осталось привести результаты отладочной печати, появляющиеся при работе этой процедуры:

Станислав                 Федотов            родился         21.05.39 
Катя                      Павлова            родилась        22.03.79 
Станислав                 Федотов            родился         21.05.39 
 владеет машиной: 
Марка =			Москвич
ДатаВыпуска =					12.11.98 
Цвет =			Морская волна
Остап                     Бендер             родился         23.07.1910 
 владеет машиной: 
Марка =			Антилопа
ДатаВыпуска =					12.12.12 
Цвет =			Неопределенный
Юрий            Алексеевич      Вегера       родился         21.08.34 
 владеет машиной: 
Марка =			Газ69
ДатаВыпуска =					20.01.76 
Цвет =			Зеленый
Марка =			Антилопа
ДатаВыпуска =					12.12.12 
Цвет =			Неопределенный
Марка =			Москвич
ДатаВыпуска =					12.11.98 
Цвет =			Морская волна
Марка =			Jeep
ДатаВыпуска =					23.05.97 
Цвет =			Orange
Марка =			Москвич
ДатаВыпуска =					12.11.98 
Цвет =			Морская волна
Марка =			Антилопа
ДатаВыпуска =					12.12.12 
Цвет =			Неопределенный
Марка =			Газ69
ДатаВыпуска =					20.01.76 
Цвет =			Зеленый
5.7.

Как мы уже отмечали, родительский класс Личность реагирует на события. Это не мешает наследовать его интерфейсы, но не означает, что потомки будут наследовать события своего родителя. Чтобы убедиться, что зажигание событий не мешает работе потомков и не влияет на их работу, мы включили процедуру CallEvents. В процессе ее работы возникают события у объектов родительского класса и они правильно обрабатываются. Но, заметьте, после того как объект FriendOne был связан с объектом - потомком FriendOwner, при смене фамилии соответствующее событие уже не возникало, так что ничто не помешало Остапу Бендеру сменить свою фамилию. По ходу работы этой процедуры в появляющихся диалоговых окнах будет сообщено об отказе смены фамилии Федотову и появится поздравление Кате Павловой в связи с замужеством. Приведем результаты отладочной печати при работе этой процедуры:

21.05.39 
22.03.79 
Станислав          Федотов              родился            21.05.39
Катя               Волконская           родилась           22.03.79
Остап              Воробьянинов         родился            23.07.1910

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

< Лекция 5 || Лекция 6: 123456 || Лекция 7 >
полина есенкова
полина есенкова
Дмитрий Вологжин
Дмитрий Вологжин
Добрый день, прошел тесты с 1 по 9, 10 не сдал, стал читать лекцию и всё пройденные тесты с 1 по 9 сбросились, когда захотел пересдать 10 тест.