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

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

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

Методы

Не будем повторяться. О методах было сказано уже достаточно. Любая процедура ( Sub ) или функция ( Function ), описанная в разделе методов класса, является его методом. Напомним синтаксис методов класса:

[Private | Public | Friend] [Static] Sub name [(arglist)] 
[statements]
[Exit Sub]
[statements]
End Sub
[Public | Private | Friend] [Static] Function name [(arglist)] [As type]
[statements]
[name = expression]
[Exit Function] 
[statements]
[name = expression]
End Function

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

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

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

Public Function Plus(a As Rational) As Rational
    Dim d As Integer, u As Integer, v As Integer
    Dim R As New Rational
   
	u = m * a.Знаменатель + n * a.Числитель
    v = n * a.Знаменатель
    d = nod(u, v)
    R.Числитель = u \ d
    R.Знаменатель = v \ d
    Set Plus = R
End Function

Public Function Minus(a As Rational) As Rational
    Dim d As Integer, u As Integer, v As Integer
    Dim R As New Rational
    
	u = m * a.Знаменатель - n * a.Числитель
    v = n * a.Знаменатель
    d = nod(u, v)
    R.Числитель = u \ d
    R.Знаменатель = v \ d
    Set Minus = R
End Function

Public Function Mult(a As Rational) As Rational
    Dim d As Integer, u As Integer, v As Integer
    Dim R As New Rational
   
	u = m * a.Числитель
    v = n * a.Знаменатель
    d = nod(u, v)
    R.Числитель = u \ d
    R.Знаменатель = v \ d
    Set Mult = R
End Function

Public Function Divide(a As Rational) As Rational
    Dim d As Integer, u As Integer, v As Integer
    Dim R As New Rational
    u = m * a.Знаменатель
    v = n * a.Числитель
    If v = 0 Then
        MsgBox ("деление на нуль невозможно")
    Else
        d = nod(u, v)
        R.Числитель = u \ d
        R.Знаменатель = v \ d
        Set Divide = R
    End If
End Function

Public Sub PrintRational()
    Debug.Print (m & "/" & n)
End Sub
4.5.

Замечание:

Внутри класса при реализации операций над рациональными числами доступ к закрытым свойствам объекта, естественно, разрешен, и можно обращаться непосредственно к переменным m и n. Однако нельзя обратиться к закрытым свойствам параметров класса Rational, передаваемых методам Plus, Minus, Mult, Divide. Точно также, нельзя обратиться к свойствам локальных объектов (объекту R ), объявленных внутри методов. Единственный выход состоит в том, чтобы использовать для них Public Property Let и Public Property Get. Но тогда и вне класса можно использовать эти процедуры - свойства, изменяя, например, значение знаменателя дроби. Разрешать этого не хотелось бы.

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

Приведем процедуру, которая работает с рациональными числами:

Public Sub WorkWithRational()
Dim a As New Rational, b As New Rational, c As New Rational
    
    a.CreateRational 5, 7
    b.CreateRational -12, -18
    Set c = a.Plus(b)
    c.PrintRational
    Set a = c.Minus(b)
    a.PrintRational
    Set c = a.Mult(b)
    c.PrintRational
    Set b = c.Divide(a)
    b.PrintRational
End Sub

При ее запуске получены следующие результаты:

29/21
5/7
10/21
2/3

Friend методы

Методы класса, в том числе процедуры - свойства Get, Let и Set могут иметь описатель Friend наряду с описателями Public и Private. Каждый из них задает свою область видимости метода, область программы, где метод доступен для вызова. Напомним, Private делает метод видимым только внутри класса, Public - делает метод доступным и область действия может распространяться не только на тот проект, в котором определен данный класс, но и на другие проекты. Чтобы управлять этим процессом, расширяя или сужая область видимости Public объектов, применяют специальные спецификаторы, например, опцию Option Private, которая закрывает модуль от других проектов. Методы с описателем Friend называют дружественными . Описатель Friend, действуя только на один метод, распространяет его область видимости на проект, в котором описан класс с Friend методом. Но для других проектов дружественные методы не доступны, даже в том случае, когда там доступны Public методы. Так что метод дружит только со своим проектом и не более. Кто программировал на С++ и других языках, где есть наследование классов, знаком с дружественными методами, - там они дружат с классами - потомками родительского класса, в котором задан Friend метод. Вне семейства классов дружественные методы не доступны. На VBA роль семейства играет проект.

События

В мире объектов время от времени происходят события. Причиной их могут быть действия пользователя или реакция системы на ранее происшедшие события. При возникновении события, объекту с ним связанному, система посылает сообщение, при получении которого вызывается специальный метод - обработчик события. Эти методы, вызываемые специальным образом, и называются событиями. Итак, каждый класс имеет свойства, методы и события. Все объекты - экземпляры класса имеют одинаковый набор свойств, методов и событий, заданных определением класса. Методы у всех экземпляров класса одинаковы. Но, заметьте, обработчики событий у каждого экземпляра собственные! Впрочем, это же касается и значений свойств, - каждый экземпляр уникален. Все командные кнопки имеют метод Delete, который всегда выполняется одинаково, удаляя кнопку. Но щелчок по каждой кнопке, вызывающий событие Click, приводит к совершенно разным результатам. В этом сила событий, - поведение объектов становится уникальным.

VBA позволяет работать с объектами Office 2000, которым несть числа, и собственными объектами, классы которых мы умеем определять. Большинство из объектов Office 2000 не имеет событий, например, прославленный объект Range имеет множество свойств и методов, но ни одно из событий с ним не связывается. С другой стороны все объекты, предназначенные для организации интерфейса с пользователем, - формы, кнопки, списки и прочие объекты обладают набором событий. Но не только интерфейсные объекты имеют события. Объекты, задающие документы Word и Excel, страницы рабочих книг Excel, объекты Application также имеют встроенный набор событий. Для большинства из них мы подробно рассмотрели, как создаются обработчики событий для конкретных экземпляров. Напомним, есть специальный тип модулей, связанных с объектами, имеющими события, - сюда относятся модули форм, документов и другие. В окне кода этого модуля можно выбрать конкретный объект, выбрать для него возможное событие, построить заготовку обработчика события и наполнить затем ее содержанием. Подробнее об этом сказано в предыдущей лекции, посвященной модулям. Нам осталось рассмотреть две серьезные задачи:

  • Есть важная группа объектов Office 2000, которая обладает встроенным набором событий. Однако эти объекты могут существовать в двух ипостасях, как объекты без событий и объекты с событиями With Events. В эту группу входят, например, объекты Application. По умолчанию эти объекты появляются как объекты, не имеющие событий, поэтому у них нет специального модуля, в окне кода которого можно было бы найти объект, найти список его возможных событий, создать обработчик события. Первая задача состоит в том, чтобы для таких стандартных объектов, классы которых обладают событиями, уметь создавать объекты с событиями ( With Events ), создавать для них модуль, в котором будут появляться заготовки обработчиков событий.
  • Для собственных объектов нужно решить более сложную задачу, - необходимо не только уметь строить обработчики событий, но предварительно в классе определить сам набор возможных событий.

К решению этих двух важных задач мы сейчас и приступим.

Классы, объекты With Events и обработчики событий

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

  • Создать модуль, в котором можно строить обработчики событий:
  • Создать сам объект Excel.Application With Events и связать его с текущим Excel приложением.

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

Модуль класса с объектом WithEvents

Модуль, в котором будут появляться обработчики событий объекта Excel.Application, синтаксически является модулем класса, который нужно создать. Этот класс очень прост по своей структуре и при создании он содержит единственное закрытое свойство - соответствующий объект Application, объявленный со спецификатором WithEvents. Так что в нашем примере все описание создаваемого класса, который назовем, например, ExcelWithEvents сводится к одной содержательной строке:

Option Explicit
'Класс ExcelWithEvents
Public WithEvents xlApp As Excel.Application

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

Класс с объектом Excel.Application WithEvents

увеличить изображение
Рис. 4.4. Класс с объектом Excel.Application WithEvents

Мы ограничились созданием двух обработчиков события NewWorkbook и OpenWorkbook. Вот их текст:

Private Sub xlApp_NewWorkbook(ByVal Wb As Excel.Workbook)
    MsgBox("Рад, что вы открыли книгу " & Wb.Name)
End Sub 

Private Sub xlApp_WorkbookOpen(ByVal Wb As Excel.Workbook)
    MsgBox ("Рад, что вы открыли книгу " & Wb.Name)
End Sub

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

Объект WithEvents

Вторая проблема решается также довольно просто. Но здесь есть одна тонкость, которую следует понимать. Реально создаются два объекта, мы называем их двойниками. Один из них это обычный объект Excel.Application, другой - объект Excel.ApplicationWithEvents из класса ExcelWithEvents. Эти два объекта должны быть связаны одной ссылкой. Вот процедура, которая в нашем примере вызывается в документе Word в тот момент, когда пользователь решит начать работу с книгами Excel:

Public Sub CreateExApp()
    Const PathToMyDir = "e:\O2000\CD2000\Ch4\"
    'Чистый Excel
        Dim MyEx As New Excel.Application
    'Excel With Events
        Dim MyExWithEv As New ExcelWithEvents
    'Связывание
    Set MyExWithEv.xlApp = MyEx
    'Добавляем новую книгу и открываем существующую
    'Обработчики событий New и Open работают!
    With MyEx
       .Visible = True
       .Workbooks.Add 1
       .Workbooks.Add PathToMyDir & "BookOne"
    End With
End Sub

Поверьте, обработчики событий действительно вызывались, как при создании новой книги, так и открытии книги, основанной на существующем шаблоне. Осталось сказать несколько слов о том, как это все могло выглядеть, если всю эту процедуру осуществлять в самом Excel. Обратите внимание, в нашем примере Excel Application с событиями появляется до того, как открыта какая либо из его книг. Если же работать в Excel, то описание объекта с событиями и связывание необходимо поместить в первую из открываемых книг. Мы проделали эту работу и в проекте книги BookOne создали класс ExcelWithEvents, а в одном из стандартных модулей создали процедуру:

Public Sub ConnectingWithEvents()
    Const PathToMyDir = "e:\O2000\CD2000\Ch4\"
    'Excel With Events
        Dim MyExWithEv As New ExcelWithEvents
    'Связывание
    Set MyExWithEv.xlApp = Excel.Application
    'Добавляем новую книгу и открываем существующую
    'Обработчики событий New и Open работают!
    With Application
       .Visible = True
       .Workbooks.Add 1
       .Workbooks.Add PathToMyDir & "BookTwo"
    End With

End Sub

Как видите, эта процедура отличается от процедуры CreateExApp лишь незначительными деталями. Возникает вопрос, как и когда запускать эту процедуру. Можно это делать по-разному, например, вызывать ее в обработчике события Click командной кнопки, можно вызывать ее в обработчике события OpenDocument. В последнем случае, обработчики событий объекта Application будут выполняться для всех книг, сразу после открытия книги BookOne.

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