Каковы преимущества использования классов в VBA?

29

Я занимаюсь программированием на VBA в Excel 2007 и у меня есть одна рабочая книга, из которой все таблицы данных должны быть скопированы на другой лист. На новом листе будет несколько строк заголовков, и я хотел бы отслеживать, где они находятся, поэтому мне не нужно постоянно находить в них слова.

Это самая простая вещь - использовать классы и поддерживать их работоспособными, пока открыт документ Excel? Или это сделает его тяжелым и трудным в обращении, и я должен продолжать работать с подпрограммами? Каковы преимущества использования классов? Это не так, как у меня есть несколько объектов, только листы и проверки по столбцам.

Теги:
class
excel-vba
excel

3 ответа

75

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

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

Я скопировал объяснение классов из одного из моих предыдущих ответов на переполнение стека:


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

В редакторе VBA перейдите к Insert > Class Module. В окне "Свойства" (внизу слева от экрана по умолчанию) измените имя модуля на WorkLogItem. Добавьте в класс следующий код:

Option Explicit

Private pTaskID As Long
Private pPersonName As String
Private pHoursWorked As Double

Public Property Get TaskID() As Long
    TaskID = pTaskID
End Property

Public Property Let TaskID(lTaskID As Long)
    pTaskID = lTaskID
End Property

Public Property Get PersonName() As String
    PersonName = pPersonName
End Property

Public Property Let PersonName(lPersonName As String)
    pPersonName = lPersonName
End Property

Public Property Get HoursWorked() As Double
    HoursWorked = pHoursWorked
End Property

Public Property Let HoursWorked(lHoursWorked As Double)
    pHoursWorked = lHoursWorked
End Property

Вышеприведенный код даст нам строго типизированный объект, специфичный для данных, с которыми мы работаем. Когда вы используете многомерные массивы для хранения ваших данных, ваш код напоминает это: arr(1,1) - это идентификатор, arr(1,2) - имя PersonName, а arr(1,3) - HoursWorked. Используя этот синтаксис, трудно понять, что к чему. Предположим, вы по-прежнему загружаете свои объекты в массив, но вместо этого используете WorkLogItem, который мы создали выше. Это имя, вы можете сделать arr(1).PersonName, чтобы получить имя человека. Это значительно упрощает чтение кода.

Продолжим двигаться с этим примером. Вместо хранения объектов в массиве мы попытаемся использовать collection.

Затем добавьте новый модуль класса и назовите его ProcessWorkLog. Вставьте здесь следующий код:

Option Explicit

Private pWorkLogItems As Collection

Public Property Get WorkLogItems() As Collection
    Set WorkLogItems = pWorkLogItems
End Property

Public Property Set WorkLogItems(lWorkLogItem As Collection)
    Set pWorkLogItems = lWorkLogItem
End Property

Function GetHoursWorked(strPersonName As String) As Double
    On Error GoTo Handle_Errors
    Dim wli As WorkLogItem
    Dim doubleTotal As Double
    doubleTotal = 0
    For Each wli In WorkLogItems
        If strPersonName = wli.PersonName Then
            doubleTotal = doubleTotal + wli.HoursWorked
        End If
    Next wli

Exit_Here:
    GetHoursWorked = doubleTotal
        Exit Function

Handle_Errors:
        'You will probably want to catch the error that will '
        'occur if WorkLogItems has not been set '
        Resume Exit_Here


End Function

Вышеприведенный класс будет использоваться для "сделать что-то" с коллекцией WorkLogItem. Первоначально мы просто подсчитываем общее количество отработанных часов. Позвольте проверить код, который мы написали. Создайте новый модуль (а не модуль класса на этот раз, просто "обычный" модуль). Вставьте следующий код в модуль:

Option Explicit

Function PopulateArray() As Collection
    Dim clnWlis As Collection
    Dim wli As WorkLogItem
    'Put some data in the collection'
    Set clnWlis = New Collection

    Set wli = New WorkLogItem
    wli.TaskID = 1
    wli.PersonName = "Fred"
    wli.HoursWorked = 4.5
    clnWlis.Add wli

    Set wli = New WorkLogItem
    wli.TaskID = 2
    wli.PersonName = "Sally"
    wli.HoursWorked = 3
    clnWlis.Add wli

    Set wli = New WorkLogItem
    wli.TaskID = 3
    wli.PersonName = "Fred"
    wli.HoursWorked = 2.5
    clnWlis.Add wli

    Set PopulateArray = clnWlis
End Function

Sub TestGetHoursWorked()
    Dim pwl As ProcessWorkLog
    Dim arrWli() As WorkLogItem
    Set pwl = New ProcessWorkLog
    Set pwl.WorkLogItems = PopulateArray()
    Debug.Print pwl.GetHoursWorked("Fred")

End Sub

В приведенном выше коде PopulateArray() просто создается коллекция WorkLogItem. В вашем реальном коде вы можете создать класс для анализа ваших листов Excel или ваших объектов данных для заполнения коллекции или массива.

Код TestGetHoursWorked() просто демонстрирует, как использовались классы. Вы заметили, что ProcessWorkLog создается как объект. После его создания коллекция WorkLogItem становится частью объекта pwl. Вы заметили это в строке Set pwl.WorkLogItems = PopulateArray(). Затем мы просто вызываем написанную нами функцию, которая действует на коллекцию WorkLogItems.

Почему это полезно?

Предположим, что ваши данные изменяются, и вы хотите добавить новый метод. Предположим, что ваш WorkLogItem теперь содержит поле для HoursOnBreak, и вы хотите добавить новый метод для его вычисления.

Все, что вам нужно сделать, это добавить свойство к WorkLogItem так:

Private pHoursOnBreak As Double

Public Property Get HoursOnBreak() As Double
    HoursOnBreak = pHoursOnBreak
End Property

Public Property Let HoursOnBreak(lHoursOnBreak As Double)
    pHoursOnBreak = lHoursOnBreak
End Property

Конечно, вам нужно будет изменить свой метод для заполнения вашей коллекции (метод образца, который я использовал, был PopulateArray(), но для этого, вероятно, должен быть отдельный класс). Затем вы просто добавляете свой новый метод в класс ProcessWorkLog:

Function GetHoursOnBreak(strPersonName As String) As Double
     'Code to get hours on break
End Function

Теперь, если мы хотим обновить наш метод TestGetHoursWorked(), чтобы вернуть результат GetHoursOnBreak, все, что нам нужно было бы сделать, добавив следующую строку:

    Debug.Print pwl.GetHoursOnBreak("Fred")

Если вы передали массив значений, представляющих ваши данные, вам нужно было бы найти все места в вашем коде, где вы использовали массивы, а затем обновить их соответствующим образом. Если вместо этого вы используете классы (и их созданные объекты), вы можете гораздо легче обновить свой код для работы с изменениями. Кроме того, когда вы разрешаете использовать класс несколькими способами (возможно, для одной функции требуется только 4 свойства объектов, а другая - 6), они все равно могут ссылаться на один и тот же объект. Это не позволяет вам иметь несколько массивов для разных типов функций.

Для дальнейшего чтения я настоятельно рекомендую получить копию VBA Developer Handbook, 2-е издание. Книга полна отличных примеров и передовых методов и тонны кода образца. Если вы инвестируете много времени в VBA для серьезного проекта, стоит потратить время на изучение этой книги.

  • 0
    Спасибо за пример, в функции TestGetHoursWorked необходимо ли иметь объявление Dim arrWli (), так как WorkLogItem - классы меня смущают и, насколько я могу судить, пример работает без этой строки. Нужен ли для какого-то класса вуду?
  • 0
    @SlowLearner: Нет, похоже, неиспользуемый код.
Показать ещё 1 комментарий
6

Если есть много подпрограмм или подпрограмм, очень долго, то может помочь структурирование кода в классах. Если есть только пара подпрограмм, скажем, каждый из них составляет всего 10 строк кода, то это больше, чем убийство. Преимущество структурирования кода в классах заключается в том, что его легче читать и изменять, когда вы возвращаетесь к нему по линии. Таким образом, еще одна причина для структурирования кода в классах - это если код, вероятно, понадобится изменить строку

4

Есть еще одна вещь, которую вы могли бы добавить к преимуществам, заявленным другими участниками (извините, если это где-то в Ben McCormack отличный ответ, и я пропустил это). Классы могут использовать их, если ваш VBA script, вероятно, будет запрограммирован в какой-то момент.

Например, я разрабатываю систему управления заказами. Он должен использоваться несколькими коллегами довольно долгое время, но может потребоваться повторная прогамминга, если правила упорядочения изменится. Поэтому я разработал класс основных предметов, который собирает всю информацию об элементе запаса. Однако правила о том, как эти данные анализируются для любого заказа, записываются в легкодоступные и хорошо прокомментированные подпрограммы. Делая это, я надеюсь, что будущие программисты VBA могут легко изменить математические правила, с помощью которых создаются заказы, без необходимости иметь дело с тем, как все данные собираются по определенному элементу запаса (все это делается подпрограммами и функциями внутри класса, которые активируются, когда классу передается номер запаса). Общественные свойства класса также подхватываются intellisense, позволяя следующему программисту, а также самому себе, облегчить его время.

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню