Объекты Visual Basic
Хочу сразу предупредить - то что вы будете читать здесь - ни в коем случае не является академическим описанием объектно ориентированного программирования. Более того - это нельзя назвать даже нормальной учебной статьей. То о чем я буду писать вероятнее всего уже вам знакомо, или вы обо всем этом догадывались. В то же время так, не совсем традиционно изложенная информация может в какой-то мере прояснить вам картину видения обьектов, сквозь призму Visual Basic. Так что - Вперед!
Как вы уже несомненно слышали, версии Бэйсика начиная от пятой и выше поддержвают создание почти полноценных обьектов. Почему почти? Потому что полноценнные обьекты должны иметь возможность наследоваться, а также поддерживать инкапсуляцию и полиморфизм. Не поленились посмотреть что я там в словарике написал? Ну, ну. Запомните это - это основа, которую все равно надо знать. Так вот, из трех указанных в Бэйсике, реализовано только два последних. Как-же. как-же закричат любители правды - мы можем сослаться внутри обьекта на методы и свойства другого класса и в результате получить эти самые методы и свойства к использованию, как унаследованные. Правильно, но вы не наследуете обьект - вы наследуете его интерфейсы. И сейчас, давайте лучше плюнем на это - что дано, тем и пользуемся.А начнем мы с самых азов. Что такое класс и как написать свой собственнный первый объект.
Класс (а в Бэйсике он реализуеся добавлением в проект class module) это просто шаблон. Некая коробка, на которой большими буквами написано - то что лежит внутри это не просто код, это ОБЪЕКТ! Испугались? Вот и Windows такой надписи пугается, да так сильно, что все, что вы обьявили с волшебным словом Public превращается в свойства или методы объекта. Итак, добавили в проект класс модуль, в свойствах этого класс модуля изменили имя с бестолкового Class1 на MyFirstClass, и смело пишем
Public A as integer Public Sub AA () End Sub Public Function AAA() as integer End function
Возвращаемся в форму, и на Form_load создаем обьект типа MyFirstClass:
dim obj as new MyFirstClass obj.
И тут справа от точки выпадает список свойств и методов нашего новоявленного объекта. То, что обозначенно пиктограмкой летящего зеленого кирпича - это методы, а листик с перстом указующим - свойства. Запоминаем эти обозначения, с ними вы встретитесь еще не раз, и не только в этом месте. Итак, листик содержит А, АА, ААА Чем между собой отличаются 2 последних ? А ничем, точнее почти ничем - функция может вернуть значение прямо в своем имени, а процедура только в одном или нескольких аргументах. Снаружи они выглядят одинаково, только немного по разному вызываются. Как мы можем использовать класс? Очень просто. Выбирайте один из методов или свойств обьекта obj.aa
При этом выполнится код, содержашийся в процедуре АА. Любой объект проходит один раз при рождении через процедуру class_initialaize а при смерти - class_terminate Когда же происходят рождение и смерть Инстанса класса? Рождение (и инициализация) класса происходит при первом вызове метода или свойства объекта. Смерть класса наступает при уничтожении переменной, являющейся референсом (ссылкой на данный обьект - В нашем случае эта переменная obj). Последнее верно до тех пор, пока референс только один
Мы дошли до интересного момента - это переменные ссылки на обьект. Вы можете спросить что же в них интересного? А интересного в них - поведение. Вы наверное слышали в детстве от мамы, что присвоить рефреренс объекту просто, надо сказать Set референс_name = Class_Object_name Ключевое слово SET обязательно. Декларация референс_name как обьекта, тоже обязательна. При этом происходит присваивание ссылки объекту (но не создание обьекта). Ссылка на обьект является полноценным "хозяином" этого обьекта, его именем. Зная это имя вы можете манипулировать свойствами, методами, участвовать в эвентах. А что будет если присвоить одной ссылке другую?
Set референс2_name = референс_name
Вы получите Две одинаковые и равноправные ссылки на обьект - Если убить одну, с самим обьектом ничего не случится. Вот если прибить и вторую...
Да, я так вольно толкую о времени жизни этих ссылок. Естественно время жизни переменной оределяется тем, где эта самая преременная обьявленна. Если на General формы - то и умрет вместе с формой, если в процедуре - то по окончанию процедуры. Естественно существует способ убивать обьектные переменные - Set референс_name = Nothing
Если референс на обьект(единственный референс) будет перенаправлен на другой обьект - смерти первому обьекту не миновать. Поэкспериментируйте с эвентами Initialize & Terminate класса, поместив туда мессадж боксы.
Теперь про волшебное слово NEW - оно меняет смысл указанных выше присвоений
Set референс_name = New Class_Object_name
При этом вы создаете не ссылку на существующий обьект , а ссылку на Новую Инстанс этого обьекта. Сам этот несуществующий обьект еще не родится, (родится он при первом же обращении к свойствам или методам) но ссылка уже будет подготовлена. Так же как и раньше вы можете иметь несколько ссылок на один обьект (родившийся или не родившийся), но использование слова NEW создаст вам еще одну копию (инстранс). Ну и напоследок - ссылка это все-таки не класс из которого рожден обьект, поэтому конструкция типа
Set референс2_name = NEW референс_name - вызовет ошибку времени выполнения.
Обещанное продолжение:Начну , пожалуй с того , что открою вам тайну. Даже две. Первая, наиболее страшная, это то, что обьекты внутри состоят из обычного кода, который вы лихо лепите тысячами строк. Т.е. не надо ожидать увидить внутри классов что-то, чего вы не видите в обычных формах . Кстати, как дополнение к этой страшной тайне - формы, такие привычные и любимые нами формы - это тоже инстансы классов. Предопределенные заранее, уложенные в коллекцию форм, но в то же время просто объекты.
Думаю к последнему факту мы еще вернемся.
Вторая тайна, неким неуловимым образом тоже связанная с первой, состоит в том, что изнутри класса - обьект (инстанс этого класса), как таковой - не виден. Его там (внутри класс модуля) не существует и существовать не может. Зато ооттуда вы легко можете получить доступ ко всем свойствам и методам, так как это, не более чем переменные, процедуры и функции. Из того, что не встречалось вам в обычном программировании можно отметить специальные процедуры - Property Let, Property Get, Property Set.
Интуитивно ясно, для чего предназначенны эти процедуры. Let - установить значение свойства , Get - получить у обьекта установленное значение, Set - то же что и первое, только для свойства типа объект.
Предвижу вопросы типа "А зачем это вообще нужно? Мы легко создаем свойства объявляя в классе Public переменные." Да. Вы действительно можете, и обязательно будете это делать - только такие свойства - статические. Максимум на что они годятся - это хранить значения. Чаще же изменение значения свойства влечет за собой какое либо действие. Давайте рассмотрим тривиальный пример - наша форма [ Еще помните про первую тайну :-) ? ] уже имеет свойство StartUpPosition, однако это такое "одноразовое" свойство. Создадим нашему обьекту-форме еще одно свойство - Center. Это простое свойство будет иметь всего 2 значения - Да и Нет (boolean). В случае установки этого свойства в True Форма должна изменить свое положение, и переместится в центр экрана. В случае установки этого свойства в False Форма должна "вспомнить" свое прошлое положение.Для приготовления этого блюда вам понадобится кастрюля, свежее мясо... Впрочем отвлекся я куда-то в сторону. Так вот нам понадобится вспомнить про вторую тайну. Т.е. форма изнутри - совсем не обьект, и для обращения с ней, как с обьектом вам нужна вторая форма, чтобы "смотреть" на первую "снаружи". А внутри формы нужно добавить две переменные для хранения координат формы (мы ведь собираемся из восстанавливать по False), и Property Let & Property Get Последние две проще всего добавить так - откройте окно кода, затем выберите в меню Tools Бэйсика "Add Procedure", надписать Center, пометить Property и Public. После Ok, вы увидите в коде две новые процедуры.
Public Property Get Center() As Variant End Property Public Property Let Center(ByVal vNewValue As Variant) End Property
Что такое vNewValue? Собственно, это переменная, которая будет принимать значение передаваемое в свойство. Эта переменная Должна быть того же типа что и сам аргумент принимаемый и возвращаемый свойством. Естественно и Property Get Center должно быть того же типа. Так что меняем оба Variant на Boolean
Далее нам необходимы две Private переменные MyLeft и MyTop (long), и еще одна , чтобы помнить какое же значение этого свойства установлено сейчас - blnCenter (boolean) Последняя нужна так же для передачи этого значения между Let и Get. Вот что получится:
Option Explicit Dim MyTop As Long Dim MyLeft As Long Dim blnCenter As Boolean Public Property Get Center() As Boolean Center = blnCenter End Property Public Property Let Center(ByVal vNewValue As Boolean) blnCenter = vNewValue If blnCenter Then MyTop = Me.Top MyLeft = Me.Left Me.Top = (Screen.Height - Me.Height) / 2 Me.Left = (Screen.Width - Me.Width) / 2 Else Me.Top = MyTop Me.Left = MyLeft End If End Property Добавьте вторую форму, на нее 2 кнопки, и код Option Explicit Dim obj As New Form1 Private Sub cmdCenter_Click() obj.Center = True End Sub Private Sub cmdNonCenter_Click() obj.Center = False End Sub Private Sub Form_Load() obj.Show obj.Left = 500 obj.Top = 1000 End Sub
Да, не забудьте в свойствах проекта указать Startup Form - Form2Рекомендую поставить точки останова , и посмотреть как это точно работает.
Да, в списке свойств Fom1 появилось только одно свойство Center, хотя процедур 2. Также не видны и три наши переменные. Теперь вы легко догадаетесь, как сделать свойство "только для чтения" или "только для записи"- правильно - стереть ненужную процедуру. И, помните событие class_initialaize? Это самый удобный момент присвить переменной blnCenter значение по умолчанию (соответсвенно и свойство будет иметь такое же значение. Ну, а в качестве домашнего задания сделате метод SlowMove, которые медленно и плавно двигает форму в указанную точку, и свойство Proporcional - несущее отношение высоты формы к ширине. Что то , связанное со свойствам осталось непонятным? Пишите вопросы- буду дописыватьСобственно, появилось дополнение, и чтобы не вводить вас в заблужедение, я должен о нем сказать - внутри обьекта вы можете обратиться к самому обьекту использую псевдоним Me.
Поставленная после Ме точка подсветит вам все существующие свойства и методы обьекта.
Обещанное продолжение:Обьекты изнутри.
Тайну эту я вам уже выболтал в прошлом выпуске. Там внутри просто код. Ну и соотвественно все , что мы можем делать в обычном коде - можем и там. Какие возможности открывает перед нами запихивание кода в класс? А разные! Вот например такая задачка. Всем хороши коллекции. Но нередко необходимо ограничить количество элементов. Можем мы это сделать в оригинальной коллекции? Нет, не можем. Мы не в состоянии влезть внутрь реализованных встроенных свойств и методов. Зато мы можем написать собственный класс. Вот и давайте быстренько это сделаем.
- Добавить класс модуль
- Назвать его MyCol
- Создать Private переменную типа New Collection
- Тут стоп. Почему Private? Потому что нам не надо , чтобы коллекцию было видно снаружи. Почему New Collection? Потому что иным способом обьявить коллекцию нельзя. Можно создать ссылку на существующую коллекцию, но не более. А нам нужна новая.
- Создать методы Remove, ADD, Item, Count - первая как процедура, последние 3 как функции. Начнем с простой - Count :
Public Function Count() As Long Count = Col.Count End Function
Аналогично создаются и остальные - просто повторяете матоды коллекции, принимая их аргументы. Остановимся на ADD:
Public Function Add(Item As Variant, Key As String) As Boolean if Col.CountИтак, обратите внимание - функция возвращает True только если добавить удалось. Если не удалось - ложь. Второе - первый аргумент обьявлен как variant - очень удобно, можно передать и строку, и число, и даже обьект. И это не всегда плюс - тут у вас в руках еще один механизм ограничениий - измените тип переменной, и получите коллекцию кнопок или строк, или...
Да, вы можете спросить как на счет необязательных параметров Before и After в нашем этом методе. Хороший Вопрос. Почитайте ответ в хелпе на IsMissing и Optional Arguments
В этом примере количество элементов, допустимых к добавлению - зашито в коде - а это как то не к лицу нашему универсальному классу. Пэтому давайте добавим Public свойство NumberOfItem, и на всякий случай зададим ему значение по умолчанию, и сделаем это в событии class_initialaize: NumberOfItem = 32000 (Число - чисто к примеру, и ничего не означает) Еще необходимо осветить один момент, Что делать с ошибками? Конечно, если кода так мало - как в нашем примере - то все можно предусмотреть, и ошибок времени выполнения успешно избежать. Если же код большой, то и возможность появления этих ошибок растет. Необходимы обработчики ошибок . Эти обработчики прекрасно делают свое дело - если вы пишете просто классы, которые встраиваются в ваш проект, а не оформляются в виде отдельных модулей exe (серверов)
Если вам надо вернуть ошибку в вызывающую программу , то могут возникнуть сложности - в этом случае нужно использовать Rise метод Err обьекта. Рекомендую вам попробовать это ручками.
Правда до этого вам надо почитать по поводу создания и регистрации внешних модулей. Кое что я писал об этом в "DCOM или Компоненты типа Клиент-Сервер", скорее всего я размещю аналогичный материал и здесь. Но это тема для нашего следующего "урока".
Инстансы Обьектов.
Мы говорили уже о разных способах создания обьектов. Вы уже обращали внимание на то, что в разных случаях (тип библлиотеки класса exe или dll, значение свойства public ) свойство Instansing имеет различные доступные значения. Это естественно, так как это свойство определяет какой конретно тип доступа к экземплярам класса будет назначен.
Для VB6 это свойство будет иметь 6 разных значений:
1 - Private : Экземпляры этого класса не могут быть созданны извне.Более того они извне не видны Вы можете создавать и использовать такие обьекты только изнутри проекта, в которм "сидит" этот класс модуль.
2 - PublicNotCreatable : Экземпляры этого класса тоже не могут быть созданны извне , однако они могут быть использованы если уже созданы "изнутри".
3 - SingleUse : Вы можете использовать CreateObject function или ключевое слово New для создания экземпляров этого класса. При этом каждый новый элемент класса будет стартовать в отдельном "рабочем пространстве" Это свойство не появляется, если вы создаете ActiveX DLL. Обратите внимание вы таки можете насоздавать хоть сотню экземпляров этого класса.
4 -GlobalSingleUse : Префикс Global означает , что использовать этот тип класса можно без дополнительный обьявлений. В остальном это то же самое что и 3
5 - MultiUse : Да, именно, вы можете создавать и использовать экземпляры этого класса отовсюду, при этом допустимо, что одна физическая копия обьекта в памяти будет обслуживать все созданные экземпляры. Некоторые проблемы, связанные с использованием этого свойства(отказ в корректной работе при определенных настройках DCOM ) я расcматривал в статье о настройке DCOM компонентов
6 - GlobalMultiUse : Префикс Global означает , что использовать этот тип класса можно без дополнительный обьявлений, т.е класс создается автоматически, вы можете сразу использовать его свойства и методы как глобальные функции. . В остальном это то же самое что и 5
Создание эвентов класса.
PS. Я ничего не пишу здесь об UserControls. Это тема для другого большого разговора. И, к тому же, 9/10 всех моих попыток написать свой собственный юзерконтрол приводили к тому, что я задумавшись над предстоящей работой понимал - лучше этого не делать. Много труда придется вложить, а реальная польза от написанного не всегда настолько очевидна. К тому же нередко такой контрол за тебя уже написан. Я не навязываю этот свой опыт - Юзерконтролы модный инструмент, но однако - Думайте перед написанием кода. Делайте это всегда :-) Удачи!
Словарик терминов
Наследование - Означает то, что один обьект может быть построен на базе другого. при этом могут быть унаследованы свойства, методы и события. К примеру класс магнитофон - это нечто умеющее проигрывать музыку с аудио кассет. На его основе можно построить класс плеер - спрятать свойство "запись" и минимизировать свойства "размер" и "вес" (шучу - но идеологически правильно)
Инкапсуляция - Инкапсуляцию можно тремя словами описать так обьект это "вещь в себе". Т.е. информация об обьекте - его свойства и методы содержатся в описании этого обьекта. Если вернуться к нашему магнитофону - то в описание магнитофона входит , например "двухкассетная дека", с тюнером и приемником FM. Методами в данном случае будут: перемотать ленту, воспроизводить, записывать.
Полиморфизм - Полиморфизмом называют способность многих обьектов использовать один и тот же метод. При этом производимые действия будут зависить от того, какого типа обьект их инициировал. С магнитофоном тут пример не пойдет :-) А вот из Бэйсика - пожалуйста. Почти все контролы в бэйсике имеют метод SetFocus , но передаваться он будет именно тому контролу, который его вызвал. И в случае с кнопкой - это вызовет появление рамки, а в случае с текстбоксом - перенос курсора.
Методы - Методы это в принципе то, как ваш обьект реагирует на события.. Возвращаясь к уже надоевшему магнитофону - вы просто нажимете кнопку Play (это событие) далее магнитофон начинает прокручивать пленку, усиливать сигнал ... Вам уже не надо обьяснять ему что делать дальше.
Интерфейс - Интерфейсом для обьекта можно назвать набор связанных свойств и методов
Инстанс класса - Инстансом называется одна копия обьекта загруженная в память. Одновременно в памяти может существовать много инстансов одного обьекта.
Коллекции - Коллекции - как ясно из названия, это набор элементов, собранный в кучу. Тип элементов не существенен. Доступ к элементам осуществляется по текстовому уникальному ключу. Этот ключ присваевается элементу при добавлении в коллекцию. Так же до любого элемента коллекции можно добраться последовательным перебором.
Оставить комментарий
Комментарии
Ya mogy predlagat nemnogo!
nazirjon1982@mail.ru
Iskandar
Если найдете сообщиете мне тоже пожалуйста
nazirjon1982@mail.ru