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

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

< Лекция 5 || Лекция 6: 123456 || Лекция 7 >

Обертывание коллекции VBA

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

Не будем изобретать новых примеров и построим класс КоллекцияЛичностей. Этот класс будет обладать всеми стандартными свойствами и методами класса Collection, дополненными методами класса СписокЛичностей. Он будет иметь:

  1. Свойство Count - возвращает число элементов коллекции. Доступно только для чтения, имеет тип возвращаемого значения Long.
  2. Метод AddPerson (item, key, before, after) - добавляет элементы в коллекцию. Первый параметр Item является обязательным и задает добавляемый элемент. Параметр key - необязателен, он задается, когда элементу ставится в соответствие ключ. Два последних необязательных параметра уточняют позицию вставки, - задают индекс или ключ элемента, перед или после которого добавляется новый элемент. Только один из этих параметров может быть задан. Если не задан ни один, элемент добавляется в конец коллекции. Заметьте, несмотря на внешнее сходство метод AddPerson отличается от метода Add класса Collection. Во-первых, параметр Item будет иметь строго фиксированный тип Личность. Во-вторых, есть различия в реализации. Коллекция будет хранить не ссылку на элемент Item класса Личность, а копию этого элемента.
  3. Метод Remove(key) - удаляет элементы коллекции. Удаляется элемент с заданным ключом: заметьте, это может быть индекс элемента. После удаления происходит перенумерация элементов и уменьшается счетчик Count.
  4. Метод Item(key) - возвращает значение элемента списка с заданным ключом. И здесь в роли ключа может выступать обычный индекс элемента.
  5. Метод PrintList() - печатает элементы списка в его текущем состоянии.
  6. Метод PrintHystory - печатает историю создания списка, - все его элементы, даже если они и были удалены впоследствии.

Наряду с открытыми свойствами и методами наш класс будет содержать и закрытые свойства и методы:

  1. Свойство First - указатель на начало списка, хранящего элементы коллекции.
  2. Событие Initialize - конструктор по умолчанию, инициализирует список.
  3. Метод AddFirst - создает копии элементов и добавляет их в список, представляющий внутреннюю память коллекции.

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

Option Explicit

'Определение класса КоллекцияЛичностей
'Свойства
		Private First As ЭлементСпискаЛичностей
		Private Count As Long
		'Обратите внимание: встраиваем коллекцию
		Private Persons As Collection
		
'Методы
Private Sub Class_Initialize()
		Set First = Nothing
		Count = 0
		'при создании объекта класса КоллекцияЛичностей создается
		'и внутренняя коллекция для хранения его элементов
		Set Persons = New Collection
End Sub

Private Sub AddFirst(F As Личность)
		Dim Elem As New ЭлементСпискаЛичностей
		Dim Info As New Личность
		'Создаем копию переменной F. В списке будем использовать копию, а не ссылку.
		Info.CopyPerson F
		Set Elem.Сам = Info
		Set Elem.Друг = First
		Set First = Elem
		Count = Count + 1
End Sub

Public Sub PrintList()
		'Печать текущего состояния списка
		Dim i As Long
		For i = 1 To Persons.Count
			Persons(i).PrintPerson
		Next i
End Sub

Public Sub AddPerson(Item As Личность, Optional key As String = "", _
			Optional before As Long = 0, Optional after As Long = 0)
		Dim P As Личность
		'Вначале добавляем элемент в линейный список,
		'используя внутренний метод AddFirst
		AddFirst Item
		
		'Разбор случаев вызова
		Set P = First.Сам
		If key <> "" Then
			Persons.Add P, key
		ElseIf before <> 0 Then
			Persons.Add P,, before
		ElseIf after <> 0 Then
			Persons.Add P,,, after
		Else
			Persons.Add P
		End If
		Count = Persons.Count
End Sub
Public Property Get Количество() As Long
	Количество = Count
End Property


Public Sub Remove(key As Variant)
		Persons.Remove key
		Count = Persons.Count
End Sub

Public Function Item(key As Variant) As Личность
		Set Item = Persons.Item(key)
		
End Function

Public Sub PrintHystory()
'Печать всех элементов в порядке, обратном их добавлению в список.
'Печатаются все элементы, независимо от того, были ли они удалены.
	Dim P As ЭлементСпискаЛичностей
		Dim Q As Личность
		Set P = First
		While Not (P Is Nothing)
			Set Q = P.Сам
			Q.PrintPerson
			Set P = P.Друг
		Wend
End Sub
5.11.

Приведем комментарии к этому тексту:

  1. Прежде всего, следует обратить внимание на то, что объект Persons класса Collection встроен, как свойство, в создаваемый класс. Этот объект инициализируется конструктором по умолчанию, - обработчиком события Initialize в тот момент, когда создается сам объект класса КоллекцияЛичностей. Благодаря этому встроенному объекту, становятся доступны все свойства и методы стандартного класса Collection.
  2. Наиболее интересна реализация метода AddPerson. В этом методе вначале вызывается закрытый метод AddFirst, создающий копию добавляемого элемента и помещающий эту копию в линейный список, представляющий внутреннюю память коллекции. Следующим шагом является вызов метода Add встроенным объектом Persons. Этому предшествует разбор случаев вызова. Дело в том, что метод AddPerson, также как и его прародитель метод Add имеет возможные параметры, которые могут быть заданы или опущены в момент вызова. Так что приходится разбираться, какие из параметров были заданы при вызове.
  3. Функция Item и метод Remove достаточно просты в реализации, они вызывают соответствующие методы встроенного объекта.
  4. Свойство Count, доступное для чтения, соответствует значению этого свойства внутреннего объекта Persons.
  5. В интерфейс класса добавлены два метода печати элементов списка. Один из них распечатывает текущее состояние коллекции, состав элементов которой может динамически изменяться. Другой метод распечатывает историю создания коллекции. Список, представляющий внутреннюю память коллекции, сохраняет эту историю

Приведем процедуру для работы с этой коллекцией:

Public Sub Великие()
		'Создание и работа с коллекцией личностей
		Dim Личности As New КоллекцияЛичностей
		Dim ЭтоЛичность As Личность
'Работа с коллекцией, как со списком
		Dim Адам As New Личность
		Адам.InitPerson "Адам", "Первый Человек", #1/1/100#
		Личности.AddPerson Адам, "Первый"
		Dim Ной As New Личность
		Ной.InitPerson "Ной", "Праведник", #1/1/100#
		Личности.AddPerson Item:=Ной, after:=1
	
'Работа с коллекцией, как с динамическим массивом
		Dim Шекспир As New Личность
		Шекспир.InitPerson "Вильям", "Шекспир", #4/23/1564#
		Личности.AddPerson Item:=Шекспир, after:=2
		Dim Гомер As New Личность
		Гомер.InitPerson "Гомер", "Великий Слепой", #1/1/100#
		Личности.AddPerson Item:=Гомер, before:=3
		
'Работа с коллекцией, как со словарем
		Dim Булгаков As New Личность
		Булгаков.InitPerson "Михаил", "Булгаков", #1/23/1891#
		Личности.AddPerson Item:=Булгаков, key:="Мастер"
		Dim Пушкин As New Личность
		Пушкин.InitPerson "Александр", "Пушкин", #6/6/1799#
		Личности.AddPerson Item:=Пушкин, key:="Гений"
	
'Печать всего списка
		Личности.PrintList
		Debug.Print Личности.Количество
 'Удаление элементов
		Личности.Remove "Первый"
		Личности.Remove 2
'Печать после удаления
		Личности.PrintList
		Debug.Print Личности.Количество
'Доступ к отдельным элементам по ключу
		Set ЭтоЛичность = Личности.Item("Гений")
		ЭтоЛичность.PrintPerson
		Set ЭтоЛичность = Личности.Item(2)
		ЭтоЛичность.PrintPerson
End Sub
5.12.

Вот результаты отладочной печати при работе этой процедуры:

Адам                Первый Человек            родился          01.01.100 
Ной                 Праведник                 родился          01.01.100 
Гомер               Великий Слепой            родился          01.01.100 
Вильям              Шекспир                   родился          23.04.1564 
Михаил              Булгаков                  родился          23.01.1891 
Александр           Пушкин                    родился          06.06.1799 
 6 
Ной                 Праведник                 родился          01.01.100 
Вильям              Шекспир                   родился          23.04.1564 
Михаил              Булгаков                  родился          23.01.1891 
Александр           Пушкин                    родился          06.06.1799 
 4 
Александр           Пушкин                    родился          06.06.1799 
Вильям              Шекспир                   родился          23.04.1564

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

Public Sub ВводКоллекции()
	'Создание коллекции в диалоге с пользователем
	Dim Личности As New КоллекцияЛичностей
	Dim ЭтоЛичность As New Личность
	Dim Имя As String, Фамилия As String, Дата As Date
	If MsgBox("Начнем создавать список личностей?", vbYesNo) = vbYes Then
		Do
			ЭтоЛичность.ВашеИмя = InputBox("Введите имя личности")
			ЭтоЛичность.ВашаФамилия = InputBox("Введите Фамилию")
			ЭтоЛичность.ВашаДатаРождения = InputBox("Введите дату рождения ")
			Личности.AddPerson ЭтоЛичность
		Loop Until MsgBox("Продолжим создавать список личностей?", vbYesNo) = vbNo
	Личности.PrintList
	End If
End Sub

На этом мы и поставим точку в этой лекции.

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