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

Классы и объекты

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

Модуль класса

Свойства

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

' Класс Rational
'Определяет новый тип данных - рациональные числа и основные
'операции над ними - сложение, вычитание, умножение и деление.
'Рациональное число задается парой целых чисел (m,n) и изображается,
'обычно, в виде дроби m/n. Число m называется числителем, а
'n - знаменателем.  Для каждого рационального числа существует
'множество его представлений, например, - 1/2, 2/4, 3/6, 4/8, …
'задают одно и тоже рациональное число. Среди всех представлений
'можно выделить то, в котором числитель и знаменатель несократимы.
'Именно такие представители будут храниться в нашем классе.
'Операции над рациональными числами определяются естественным
'образом. Лучшим их описанием будут соответствующие им методы.

После комментария следует описание переменных, задающих свойства класса. Это могут быть терминальные свойства, заданные обычными переменными VBA, как, например, Имя, Фамилия в классе " Личность ", так и свойства - участники. Напомним, что свойства - участники это объекты других классов. Без них не обойтись, если мы строим семейство классов. Помните, что в Office 2000 нет наследования классов в классическом понимании, - его заменяет встраивание. Так что при построении семейства классов, приходится иметь дело с "толстыми" объектами, свойства которых являются объектами, имеющими свойства, являющиеся объектами и так далее. Взгляните еще раз на рисунок 1.1 первой лекции и просмотрите ее раздел "встраивание против наследования", имеющиеся там примеры поясняют ситуацию.

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

'Свойства класса Rational
Private m As Integer	'числитель
Private n As Integer 	'знаменатель
Сокрытие свойств

Возникает вопрос, почему свойства объявлены с описателем Private, а не Public Заметьте, таким же образом мы описывали свойства и в классе " Личность ". Такова общепринятая практика объектно-ориентированного программирования. Если бы свойства имели атрибут Public, - были бы открытыми, тогда при работе с объектом, можно было бы иметь к ним прямой доступ, как при чтении, так и записи. Но такая свобода, как правило, недопустима. Вот возможные стратегии при работе со свойствами:

  • Чтение, запись ( Read - Write ).
  • Чтение, запись при первом обращении ( Read, Write-once ).
  • Только чтение ( Read-only ).
  • Только запись ( Write-only ).
  • Ни чтение, ни запись ( Not Read - Not Write ), - свойство закрыто.

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

При задании свойств делайте их закрытыми.

Для доступа к закрытым свойствам и реализации перечисленных выше стратегий работы с ними предложены специальные методы - свойства Property Let, Set и Get, примеры их мы видели в классе " Личность ", а подробный разговор еще предстоит.

Конструкторы и деструкторы. Стандартные события

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

Dim MyRationalNumber As New Rational

В этот момент:

  • создается типизированный указатель MyRationalNumber,
  • создается новый объект - экземпляр класса Rational,
  • при создании объекта вызывается конструктор по умолчанию, инициализирующий этот объект, определяющий значения его свойств,
  • указатель связывается с объектом.

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

В VBA все проще. Во многом это объясняется тем, что здесь, в отличие от многих других языков программирования, есть разумная стратегия начальной инициализации переменных, - мы о ней говорили ранее. Поэтому здесь есть только конструктор по умолчанию - конструктор без параметров, да и тот часто не определяется, полагаясь на стандартную инициализацию. Конечно же, инициализировать объект "настоящими" значениями все равно придется в какой-то момент. Поэтому всегда для класса создаются свои конструкторы, синтаксически являющиеся методами, - их может быть несколько. В классе " Личность " таким конструктором с параметрами является метод InitPerson. Заметьте, мы могли бы определить еще один конструктор, который в отличие от первого, проводил бы полную инициализацию всех свойств, включая Отчество нашей личности.

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

Стандартные события Initialize и Terminate

Говоря о конструкторах и деструкторах, мы не сказали главного. Роль конструктора по умолчанию в классах VBA играет обработчик события Initialize. Это общее для многих объектов событие, встречается, когда объект загружается, для объектов - экземпляров классов оно возникает при создании объекта. У обработчика этого события нет параметров, поэтому он играет роль конструктора по умолчанию, не имеющего параметров. Роль деструктора играет обработчик события Terminate. Он вызывается, когда все ранее установленные ссылки на экземпляр объекта получают значение Nothing или все указатели перестают существовать, выйдя из области своего определения. Заметьте, что при ненормальном завершении программы это событие не возникает. Обработчик этого события (деструктор) пишется значительно реже, поскольку в момент его вызова объект и так корректно будет уничтожен.

Два конструктора класса Rational

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

'Конструкторы класса Rational
Private Sub Class_Initialize()
    'Конструктор по умолчанию
    'инициализирует рациональное число дробью 1/1
    m = 1
    n = 1
End Sub

Public Sub CreateRational(ByVal a As Integer, ByVal b As Integer)    
    'Собственный конструктор
    'Выполняет довольно сложные действия,
    'прежде чем свойства получат значения
    Dim d As Integer    'Наибольший общий делитель a и b
    If b = 0 Then
        MsgBox " Ошибка при создании рационального числа!" _
        & Chr(13) & "Знаменатель не должен равняться 0."
    Else
        ' приведение знака
        If b < 0 Then
            b = -b: a = -a
        End If
        ' приведение к несократимой дроби
        d = nod(a, b)       ' d - НОД(a,b)
        m = a \ d
        n = b \ d
    End If
End Sub

' Скрытая функция вычисления НОД(m,n)
Private Function nod(ByVal m As Integer, ByVal n As Integer) As Integer
    Dim p As Integer
    m = Abs(m): n = Abs(n)
    If n > m Then
        p = m: m = n: n = p
    End If
    Do
        p = m Mod n: m = n: n = p
    Loop Until n = 0
    nod = m
End Function
4.2.

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

Процедуры - свойства

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

  • Property Let позволяет установить новое значение терминального свойства, выполняя операцию Write (присваивание).
  • Property Set выполняет те же действия, что и предыдущая процедура, но применима к свойствам - участникам. Вы понимаете, что в VBA присваивание значений обычным переменным и объектам выполняется двумя различными операторами - Let и Set.
  • Property Get является дополнительной к предыдущим процедурам, выполняя операцию чтения Read. Она применима к терминальным свойствам и свойствам - участникам.

Рассмотрим пять ранее описанных стратегий применения свойств. В первой стратегии Read - Write каждому закрытому свойству будет соответствовать пара взаимно дополняющих процедур - свойств Let ( Set ) - Get. Эту стратегию мы реализовали для свойств класса " Личность ". Стратегия Write-once предполагает, что значение свойства должно быть записано только при первом обращении, а далее не должно изменяться. Для реализации этой стратегии процедуры - свойства Let и Set должны включать в себя код, осуществляющий специальную проверку, проводилось ли ранее присвоение значения свойству. Для объектов это проверка типа " value Is Nothing ", для переменных типа Variant - " value = Empty " и так далее. Мы не станем приводить подробный пример, подобную проверку мы осуществляли при работе со свойством Отчество в процедуре WhoIs класса " Личность. В следующих двух стратегиях используется только одна из процедур пары, либо Get, либо Let ( Set ). Пятая стратегия предполагает, что доступ к свойствам закрыт, и процедуры - свойства Get, Let ( Set ) в классе вообще не объявляются.

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

Как создаются процедуры- свойства

В классе Rational есть два закрытых свойства, заданных переменными m и n и определяющими соответственно числитель и знаменатель дроби, представляющей рациональное число. Зададим для каждого из этих свойств пару Property Let - Property Get с именами Числитель и Знаменатель соответственно. Для создания процедур - свойств обычной практикой является использование заготовок, создаваемых автоматически. Выбрав в Редакторе Visual Basic пункт меню Insert | Procedure, мы задали в появившемся диалоговом окне Add Procedure значение Property для типа процедуры. Задав еще имя процедуры - свойства " Числитель ", и, щелкнув OK, мы получили две стандартные заготовки Property Let и Property Get.

Создание процедур - свойств

Рис. 4.3. Создание процедур - свойств

Повторив эти действия, мы получили вторую пару заготовок с именем " Знаменатель ". Заготовки затем наполняются, как обычно, содержанием и слегка модифицируются. В заготовках предусмотрен общий тип Variant для параметра в Property Let и возвращаемого значения в Property Get. Разумно изменить этот общий тип на конкретный тип, используемый в данной ситуации. Естественно, что иногда приходится заменить Let на Set или руками добавить еще одну заготовку для Set. Приведем тексты этих процедур для класса Rational после их модификации и заполнения:

Public Property Get Числитель() As Integer
    Числитель = m
End Property

Public Property Let Числитель(ByVal NewValue As Integer)
     CreateRational NewValue, n
End Property

Public Property Get Знаменатель() As Integer
    Знаменатель = n
End Property

Public Property Let Знаменатель(ByVal NewValue As Integer)
     CreateRational m, NewValue
End Property

Обратите внимание, для задания новых значений свойств в процедурах Let Числитель и Let Знаменатель мы вызываем конструктор, который может изменить нужным образом и числитель и знаменатель.

Замечание:

Для класса Rational доступ к свойствам Числитель и Знаменатель следовало бы закрыть полностью. Рациональные числа представляются для пользователей неделимыми и их внутренняя структура должна быть недоступной. Для них следовало бы использовать пятую стратегию работы со свойствами, когда свойства Let и Get не объявляются. Причину, по которой были введены эти процедуры - свойства, мы объясним позже, когда речь пойдет о реализации методов класса.

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

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