Каковы преимущества использования классов в VBA? - PullRequest
31 голосов
/ 29 января 2010

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

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

Ответы [ 3 ]

76 голосов
/ 29 января 2010

Преимущество использования классов вместо просто подпрограмм состоит в том, что классы создают уровень абстракции, который позволяет вам писать более чистый код. Надо признать, что если вы никогда раньше не использовали классы в 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) - это ID, 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, второе издание . Книга полна замечательных примеров и лучших практик, а также множества примеров кода. Если вы вкладываете много времени в VBA для серьезного проекта, стоит потратить время на изучение этой книги.

6 голосов
/ 29 января 2010

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

4 голосов
/ 13 января 2014

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...