Волею судеб сложилось так, что пришлось писать на Visual Basic For Applications версии 6.0, нужно было провести испытания разработанного модуля в сторонней ИС, под рукой оказалась ИС, спроектированная в Excel. Конечно то, что мне дали, с большим натягом можно назвать ИС, скорее это набор несвязных таблиц, содержащих стохастически сгенерированную информацию, забитую в совершенно непонятном порядке :D Но не в этом суть, в общем-то. Как бы "это" противно не выглядело, нужно было выполянть свою работу, таким образом двигаясь дальше на пути испытания разработанного мною метода автоматизации в выданной мне ИС. Решил я сделать так, как надо, а не так как требуют того обстоятельства данного начатого проекта. И начал, вот что из этого получилось.
Суть моего модуля состоит в автоматизации составления отчетов в различных ИС. Итак, мне нужен инструмент для манипуляции информацией (нечто, которое смогло бы мне предоставить что-то наподобие языка запросов к содержащимся на листах данных), инструментарий для отображения этой информации. Пока большего не хотелось, поскольку на полную интеграцию моего модуля нужно было дописывать имеющуюся ИС (ну не нравится она мне и все тут!), мне нужно было только провести испытания согласно моему ТЗ.
Возможности языка VBA6 достаточно ограничены, однако не настолько, чтобы на нем нельзя было реализовать стандартные паттерны программирования. Касательно ООП в VBA6, то могу отметить следующие пункты, оказавшие мне существенные неудобства в процессе адаптации модуля моего проекта:
- отсутствие наследования (полное, как ни странно);
- сомнительный полиморфизм;
- неполноценная инкапсуляция;
- ну и так далее, насколько Вы можете понять, если Вам все же приходилось разрабатывать на человеческих языках программирования с полной поддержкой ООП (хотя, спешу заметить, что ООП - совсем не панацея).
Было принято решение оформить класс для работы с данными в форме итератора (что, в общем-то и логично, данные получать надо множеством, передвигаясь по нему от записи к записи). Поразмышляв над логикой этого класса и поискав на просторах интернета реализации данного шаблона, получил следующий код для модуля класса CTableIterator:
' количество строк в запросе
Private FCount As Integer
' массив, хранящий в себе значения итерируемых строк
Private Items() As Integer
' метод-получатель количества строк
Public Property Get Count() As Integer
Count = FCount
End Property
' метод-установщик количества строк
Private Property Let Count(value As Integer)
FCount = value
End Property
' метод, возвращающий текущий элемент
Public Function Item(index As Integer)
Item = Items(index)
End Function
Private FCount As Integer
' массив, хранящий в себе значения итерируемых строк
Private Items() As Integer
' метод-получатель количества строк
Public Property Get Count() As Integer
Count = FCount
End Property
' метод-установщик количества строк
Private Property Let Count(value As Integer)
FCount = value
End Property
' метод, возвращающий текущий элемент
Public Function Item(index As Integer)
Item = Items(index)
End Function
Во время инициализации класса, конструктору объекта передается лист, по которому осуществляется итерация, начальная строка таблицы, начальный столбец таблицы (чтобы обеспечить возможность итерации по листам, где таблицы начинаются в разных местах):
' начальная строка итерируемой таблицы
Private iRow As Integer
' начальный столбец итерируемой таблицы
Private iCol As Integer
' имя листа, по которому осуществляется итерация
Private sheetName As String
' дополнительная инициализация объекта класса
Public Sub Init(initSheetName As String, initRow As Integer, initCol As Integer)
iRow = initRow ' начальная строка
iCol = initCol ' начальный столбец
sheetName = initSheetName ' имя листа
Sheets(sheetName).Select ' выбор активного листа
End Sub
Private iRow As Integer
' начальный столбец итерируемой таблицы
Private iCol As Integer
' имя листа, по которому осуществляется итерация
Private sheetName As String
' дополнительная инициализация объекта класса
Public Sub Init(initSheetName As String, initRow As Integer, initCol As Integer)
iRow = initRow ' начальная строка
iCol = initCol ' начальный столбец
sheetName = initSheetName ' имя листа
Sheets(sheetName).Select ' выбор активного листа
End Sub
Далее нужны функции для непосредственной работы с данными. Это функции выборки, вставки, обновления, удаления строк. Опишу лишь прототипы этих функций, не вдаваясь в подробности:
' выборка - указывается индекс столбца и значение, по которому нужно фильтровать
Public Sub Select(colindex As Integer, value As String)
' обновление - индекс строки и массив значений для ее обновления
Public Sub Update(index As Integer, values() As String)
' удаление - индекс удаляемой строки
Public Sub Delete(index As Integer)
' вставка - массив вставляемых значений
Public Sub Add(values() As String)
Public Sub Select(colindex As Integer, value As String)
' обновление - индекс строки и массив значений для ее обновления
Public Sub Update(index As Integer, values() As String)
' удаление - индекс удаляемой строки
Public Sub Delete(index As Integer)
' вставка - массив вставляемых значений
Public Sub Add(values() As String)
При работе с этим классом встает проблема целостности таблиц, к которым мы имеем доступ. Поэтому нужно определить ряд правил для поддержания целостности данных (конечно, здесь не приходится говорить о полноценном ее варианте, однако целостную структуру таблицы, для начала, мы можем обеспечить). Под целой таблицей понимать будем такую таблицу, которая не имеет в себе пустых строк, поскольку пустую строку мы используем в качестве флага конца таблицы на листе excel (это наиболее простой вариант, можно придумать нечто более изощренное :) ). Поэтому пишем функцию, которая будет "сдвигать" таблицу таким образом, чтобы она не содержала пустых строк:
' функция-дефрагментатор таблицы
Private Sub Defragment(index As Integer)
Private Sub Defragment(index As Integer)
Используем данную функцию после операции удаления (очистки) строки таблицы на листе. Она сдвигает ту часть таблицы, которая ниже удаленной строки вверх. Все просто и логично, на мой взгляд.
Итак, класс-итератор для передвижения по таблицам excel закончен, теперь можно приступить к написанию визуальной части проекта для отображения данных таблиц. Об этом в следующий раз. Пасип за внимание ;)
PS Да, совсем забыл пример работы с классом:Итак, класс-итератор для передвижения по таблицам excel закончен, теперь можно приступить к написанию визуальной части проекта для отображения данных таблиц. Об этом в следующий раз. Пасип за внимание ;)
Dim ti As New CTableIterator
ti.Init "mysheet", 1, 1
ti.Select 1, "value"
Dim i As Integer
Dim arr() As Integer
For i = 1 To ti.count
Redim arr( UBound( ti.Item(i) ) )
MsgBox arr(2)
Next i
ti.Init "mysheet", 1, 1
ti.Select 1, "value"
Dim i As Integer
Dim arr() As Integer
For i = 1 To ti.count
Redim arr( UBound( ti.Item(i) ) )
MsgBox arr(2)
Next i
Конечно же, у вас должен существовать лист с названием "mysheet" и таблица на нем. Вот теперь все. :)
Словарик:
ИС - информационная система;
VBA - Visual Basic For Applications - встроенное в ms excel средство программирования;
Паттерны проектирования (шаблоны проектирования, шаблоны программирования) - набор удачных конструкций для реализации программных систем;
ООП - объектно-ориентированное программирование.