Работа с объектом в основных модулях программы




ООП в VBA

В VBA для описания объекта используется модуль класса. Для каждого абстрактного объекта надо использовать отдельный модуль класса.

Вставить модуль класса в проект:

Редактор VB – Меню – Insert – Class Module

Модулю класса надо присвоить разумное имя, которое станет именем объекта в тексте программы на VBA. Основная «смысловая» проблема: выбор между единственным и множественным числом. Будем считать, что наш класс описывает не множество полос, а абстрактную полосу и сделаем выбор в пользу единственного числа.

Имя модуля присваивается в Редакторе VB, в окне свойств, как значение свойства (Name). В нашем примере в качестве значения надо ввести Polosa.

 

 

Свойства объекта описываются как переменные уровня модуля. Это логично: они должны быть видны во всех методах объекта, т.е. во всех процедурах модуля. Здесь есть принципиальный вопрос: хотим ли мы, чтобы свойства были видны за пределами объекта, в основных модулях. Если хотим, то переменные надо объявлять как глобальные с ключевым словом Public (вместо Dim). Мы в нашем решении будем придерживаться принципа инкапсуляции и закроем свойства для внешнего окружения, поэтому объявление будет таким:

Dim a1 As Integer ‘ (начальная клетка)

Dim d As Integer ‘ (шаг прогрессии)

Методы объекта реализуются с помощью процедур модуля класса. Как правило первым методом является метод create, который задает начальные значения свойств нового (пустого) объекта. В нашем случае, чтобы создать, например, первую вертикальную полосу (т.е. полосу от клетки 1 с шагом 3), надо будет написать:

create 1, 3

Т.е. у процедуры должны быть два параметра, они имеют тот же смысл, что свойства a1 и d, но их имена должны отличаться от a1 и d. Решим это просто: добавим к каждому имени начало «p_» (от «параметр»). В результате текст процедуры будет такой.

Sub create(p_a1 As Integer, p_d As Integer)

a1 = p_a1

d = p_d

End Sub

 

Работа с объектом в основных модулях программы

В вашем проекте я рекомендую завести отдельный модуль (не модуль класса, а просто «Module») и присвоить ему имя «ООП» (или латинскими буквами “OOP”). По двум причинам:

§ в основном модуле нашего проекта уже много текста, и не стоит его перегружать еще;

§ по прошествии времени вам будет удобней увидеть пример работы с классами в виде отдельного модуля.

Как правило абстрактный объект создается потому, что таких объектов в программе должно быть несколько. Так, в нашем проекте будет 8 полос. Соответственно, обычно объекты объединяются в массивы или в коллекции. В предыдущем предложении «массив» и «коллекция» - это не синонимы, а два разных понятия и две разных структуры. Мы используем вариант коллекции.

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

Для ссылки на коллекцию используется объектная переменная с универсальным типом Object либо более узким Collection. Я рекомендую первый вариант. Эта переменная должна быть объявлена как минимум как переменная уровня модуля (т.е. вначале модуля, до описания процедур), а, поскольку мы работаем с двумя модулями, то как переменная уровня проекта, т.е. с ключевым словом Public.

Public Poloski As Object.

Логика создания коллекции такова: сначала эта переменная должна быть привязана к новой (пустой) коллекции, а потом к этой коллекции должны быть добавлены ее элементы. Это будет происходить в процедуре, выполняющей «инициализацию» коллекции, поэтому дадим ей имя Poloski_Ini. Создадим эту процедуру и включим в нее (пока) начальную привязку переменной Poloski:

Sub Poloski_InI()

Set Poloski = New Collection

End Sub

В единственной строке процедуры используется уже известный вам формат работы с объектными переменными:

Set <имя> = <ссылка на объект>

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

Для добавления элементов в коллекцию создадим процедуру AddPolosa. Сразу представим себе ее вызов (пример для первой вертикальной полосы):

AddPolosa 1, 3

В этой процедуре вы увидите принцип работы с объектами пользовательского класса.

Sub AddPolosa (a1 As Integer, d As Integer) Заголовок процедуры. Это одна строчка программы. Совпадение имен параметров и свойств объекта Polosa проблемой не является, т.к. свойства объекта Polosa не видны на уровне текущего модуля
Dim pls As Object Объявляем объектную переменную для ссылки на объект Polosa. Используем универсальный тип Object. Имя переменной должно отличаться от имени класса Polosa, поэтому используем вариант «без гласных».
Set pls = New Polosa Привязываем объектную переменную к новому (New) объекту Polosa. Объект будет создан в момент выполнения этой строки
pls.create a1, d Для заполнения свойств объекта используем метод create класса Polosa. Напомним: мы создали этот метод в модуле класса.Обратите внимание на формат «через точку»: <объект>.<метод> <аргументы> Аргументы метода берем из параметров процедуры.
Poloski.Add pls Добавляем объект (полосу) к коллекции. Здесь используется метод Add объекта «коллекция». Уточним, что этот метод создан разработчиками VBA и всегда присутствует в языке. Мы используем метод Add в простейшем варианте, при котором объект добавляется в конец коллекции.
End Sub  

 

Теперь в процедуру Poloski_InI надо включить 8 строчек вида

AddPolosa 1, 3

с параметрами каждой из 8 полос. Впрочем, можно придумать и более «изящный» вариант и добавить 3 горизонтальные и 3 вертикальные полосы с помощью двух циклов.

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

 

Вернемся к нашей основной задаче – программированию умного хода компьютера. Вспомним один из элементов стратегии: «если есть полоса с двумя нашими знаками и пустой клеткой (с картой, равной «20»), то надо ставить знак в пустую клетку, мы выиграли, СТОП». Для реализации такой идеи нам надо создать два метода объекта

§ подсчет количества крестиков и ноликов в виде «карты» полосы (см. об этом выше);

§ определение номера первой пустой клетки полосы (в ситуациях, которые нас интересуют, «первая» пустая клетка будет единственной)

Оба метода должны быть описаны как процедуры в модуле класса Polosa. Поскольку оба метода возвращают по одному значению, они должны быть оформлены как функции. Займемся ими.

Первая функция – это практически типовая задача подсчета количества вхождений элемента в последовательность, усложненная тем, что надо параллельно считать два количества.

Function karta() As String  
Dim i As Integer счетчик цикла
Dim kx As Integer, ko As Integer счетчики количества крестиков и ноликов
Dim kl_nom As Integer объявление переменных, см. далее
Dim s As String  
For i = 1 To 3 запускаем цикл по 3 клеткам полосы
kl_nom = a1 + d * (i - 1) Определяем номер клетки по правилу арифметической прогрессии. Обратите внимание: при запуске функции VBA автоматически подставит a1 и d той полосы, для которой будет вызван метод
s = kl_znak(kl_nom) Узнаем знак, стоящий в клетке. Напомним: - kl_znak это функция-оболочка, написанная нами ранее. Отметим это: в модуле класса могут использоваться процедуры, описанные в других модулях.
If s = "o" Then ko = ko + 1 Стандартные строки подсчета по условию. Использован сокращенный формат If, с одним действием на ветви.
If s = "x" Then kx = kx + 1
Next i  
karta = kx & ko Выход функции – текстовое сцепление двух чисел (цифр). Явное преобразование типов не обязательно.
End Function  

 

Вторая функция (поиск пустой клетки) – это также типовая задача поиска позиции элемента в последовательности. Особенностью этой задачи является немедленный выход из функции в тот момент, когда элемент найден, а также использование значения «по договоренности», если элемент вообще не будет найден. В качестве значения «по договоренности» будем использовать ноль.

Function kl_free() As Integer  
Dim i As Integer счетчик цикла
Dim kl_nom As Integer объявление переменных, см.далее
For i = 1 To 3 запускаем цикл по 3 клеткам полосы
kl_nom = a1 + d * (i - 1) Определяем номер клетки по правилу арифметической прогрессии (см. предыдущую задачу)
If is_pusto(kl_nom) Then «Если клетка пустая». В условии используем функцию-оболочку is_pusto, написанную нами ранее.
kl_free = kl_nom номер клетки – на выход функции
Exit Function нашли, немедленный выход из функции
End If  
Next i  
kl_free = 0 отправляем на выход функции значение по договоренности
End Function  

 

Методы объекта Polosa созданы, и мы можем возвращаться к работе в основном модуле. Перед реализацией основной стратегии нам надо решить одну техническую задачу. Дело в том, что компьютер (от лица которого мы сейчас думаем) может играть и крестиками, и ноликами, и нам надо уметь «переворачивать» карту полосы, чтобы первым было количество знаков, которыми играет компьютер. Создадим для этого функцию karta_by_clr (карта по цвету) и поместим ее в основной модуль, перед процедурой PC_hod.

У функции два параметра: карта в формате «крестики-нолики» (xo) и «цвет» интересующего нас игрока (clr, 1 – крестики, 2 – нолики). Если цвет равен единице, то карта остается без изменений, иначе мы соединяем правый символ карты (функция Right, т.е. справа, от xo, 1 символ) и левый символ карты (функция Left, т.е. слева, от xo, 1 символ).

Function karta_by_clr(xo As String, clr As Integer) As String

If clr = 1 Then

karta_by_clr = xo

Else

karta_by_clr = Right(xo, 1) & Left(xo, 1)

End If

End Function

 

Перебор коллекции

Это последняя теоретическая информация перед реализацией стратегии. В ходе проверки возможных ситуаций нам надо будет рассматривать все 8 полос, то есть перебирать элементы коллекции. Для перебора коллекции в VBA существует специальный цикл For Each. Его формат («ОП» - объектная переменная):

For Each <ОП> In <коллекция>

Действия (с использованием ОП для ссылки на элемент коллекции)

Next <ОП>

Объектная переменная должна быть объявлена до начала цикла с универсальным типом Object, либо более точным типом конкретной коллекции (в нашем случае – Polosa).

 

Вот теперь можно переходить к реализации стратегии. Мы будем работать с процедурой PC_hod и включать в нее новые фрагменты текста. Сначала – объявление новых переменных (после строки Dim nom As Integer)

Dim pls As Object объектная переменная для перебора коллекции полос
Dim xo As String карта полосы (крестики-нолики)
Dim pc_pl As String карта в формате «компьютер-игрок»

 

Текст процедуры PC_hod, относящийся к проверке начала игры (распределение цветов) остается без изменений, а после него можно начинать проверку ситуаций.

Итак, ситуация-1, проверка выигрыша пользователя: если карта полосы равна «03», то мы проиграли, СТОП:

For Each pls In Poloski Запускаем цикл «перебор коллекции». Объектная переменная pls обозначает текущую полосу внутри цикла.
xo = pls.karta В переменной xo запоминаем карту полосы. Здесь вызывается метод karta по формату <объект>.<метод>. Метод является функцией, поэтому используется справа от знака равенства.
pc_pl = karta_by_clr(xo, pc_clr) В переменной pc_pl запоминаем карту полосы с поправкой на «цвет» компьютера
If pc_pl = "03" Then По тексту стратегии: если карта полосы равна «03»
MsgBox "You win" Выводим сообщение
Exit Sub Выходим из процедуры
End If Конец If
Next pls Конец цикла For Each

Ключевая строка с точки зрения ООП, это

xo = pls.karta

Оцените простоту и лаконичность вызова метода объекта. Ради такой простоты и придуман принцип ООП.

 

Ситуация-2, проверка выигрыша компьютера: если карта полосы равна «20», то надо ставить знак в пустую клетку, мы выиграли, СТОП. Прокомментируем только новые моменты

For Each pls In Poloski  
xo = pls.karta  
pc_pl = karta_by_clr(xo, pc_clr)  
If pc_pl = "20" Then По тексту стратегии: если карта полосы равна «20»
nom = pls.kl_free В переменной nom запоминаем номер пустой клетки. Здесь вызывается метод kl_free объекта полоса, по тем же правилам, что и метод karta.
hod_kletka nom, pc_clr Делаем ход в найденную клетку своим «цветом». Напомним: hod_kletka – это процедура, написанная нами ранее.
MsgBox "I win" Выводим сообщение
Exit Sub  
End If  
Next pls  

 

Ситуация-3, защита от выигрыша игрока: если карта полосы равна «02», то надо ставить знак в пустую клетку, СТОП. Эту ситуацию реализуйте самостоятельно, по аналогии с двум предыдущими. Обратите внимание, что в этом случае не будет никакого сообщения, т.к. игра будет продолжаться.

 

Если ни одна из ситуаций не встретилась, то компьютер по-прежнему будет делать случайный ход. Соответственно, начиная со строки

nom = kl_free_rnd()

останется без изменений старый текст процедуры PC_hod.

На этом нововведения можно считать реализованными. Можно проверять работу проекта.

 



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2019-08-08 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: