VBA+OOP: что, когда, зачем
Будучи автором серии статей о полноценной объектно-ориентированной игре "Морской бой", и вообще постоянно рассуждая об ООП в VBA, я вдруг понял, что, возможно, мне не удалось внятно объяснить, когда использование ООП в VBA действительно оправдано.
ООП — это парадигма, что подразумевает определённый способ мышления о коде. Функциональное программирование (ФП) — это другая парадигма, предполагающая иной подход к коду. Процедурное программирование также является парадигмой, где код представляет собой последовательность выполняемых команд. У каждой парадигмы есть свои плюсы и минусы, своя ценность и определённые задачи, которые решаются лучше всего именно в её рамках и,... конечно же, у каждой из них есть свои преданные приверженцы, которые уверены, что их путь – единственно верный. Не верьте всему, что читаете в интернете – мыслите о парадигмах как о разных инструментах: одна из них — молоток, другая — отвертка, третья — лопата.
Не нужно становиться членом команды "Молоток!", "Отвертка!" или "Лопата!" — всё это искусственные рамки. Разные инструменты лучше всего подходят для разных задач.
Поэтому первым вопросом, который вам стоит задать себе, это…
Для чего вы используете VBA?
Скрипты/Автоматизация
Если вы занимаетесь исключительно автоматизацией в Excel с помощью скриптов, то скорее всего, вам не нужно ООП. Применение объектно-ориентированного подхода для скриптов выглядит излишним, раздутым и неоправданным. Вместо этого, для написания чистого кода возможно стоит воспользоваться следующим методом: создавайте по одному макросу на модуль. Пусть у вас будет одна Public
процедура в начале ("точка входа"), на высоком уровне абстракции, чтобы сразу было понятно, что она делает. Затем располагайте все вызываемые Private
процедуры ниже, в порядке их вызова, так чтобы модуль/макрос разворачивался как история — с обзором на высоком уровне в начале и деталями на низком уровне внизу.
Основной концепцией здесь является уровни абстракции — абстракция является одной из основ ООП, но не является исключительно присущей ООП. Абстракция также очень полезна и в процедурном коде!
Процедурное программирование само по себе не плохо и не зло. Хорошо написанный процедурный код на правильном уровне абстракции приятно читать, а если вы мыслите в терминах функций (т.е. входные данные -> выходные данные), а не "шагов" или "инструкций", то вы можете писать чистые функции, которые также могут (и должны) быть протестированы на уровне юнит-тестов.
Если вы когда-либо писали пользовательскую функцию, которая вызывается из рабочих листов, то вероятно вы уже писали чистую функцию: она принимает входные данные и производит выходные без обращения или изменения других состояний. Если вы это делали, поздравляю, вы освоили фундаментальный элемент парадигмы функционального программирования! ..тем не менее, чистые функции не являются исключительной особенностью ФП.
Большинство кода VBA попадает в эту категорию. Пытаться внедрить ООП в такой код было бы губительно; более того, ООП здесь вообще неуместен. Однако, подход, похожий на функциональное программирование, не обязательно плохая идея… хотя VBA явно не был создан с учетом особенностей функционального программирования, так что вы быстро столкнетесь с его ограничениями… но нет ничего плохого в том, чтобы стараться писать скрипты, избегая функций с побочными эффектами и распространения глобального состояния.
Фреймворк/Библиотеки инструментов
Между обычными скриптами и полноценными приложениями существует такой тип проектов на VBA, который вы создаете для себя как своеобразную "библиотеку инструментов" с полезным кодом, который вы часто переносите из одного проекта в другой и практически всегда импортируете в свои новые VBA-проекты.
На мой взгляд, именно здесь ООП действительно проявляет себя в VBA наилучшим образом: не имеет значения, будет ли код использоваться в процедурном стиле или в объектно-ориентированном – это все равно будет ООП. Подобно тому, как сама объектная модель Excel состоит из объектов и не заботится о том, является ли потребляющий ее код процедурным или объектно-ориентированным.
Мы можем говорить о полноценно переиспользуемом классе ProgressIndicator, полиморфном инструменте Logger
, который можно настроить для логирования данных в отладчик, текстовый файл, базу данных, или же о наборе классов для работы с пользовательскими типами данных – например, класс Stack
, или обертка ArrayList
, или класс File
, который содержит операции с файлами и, возможно, функционал Scripting.FileSystemObject
, или что-то еще: вы уловили суть.
Полноценные приложения
Если вы воспринимаете VBA как способ создания приложений, встроенных в документы, аналогично VB6 (что на самом деле так и есть), и считаете, что с его помощью можно сделать всё, что мог сделать VB6, то вы уже занялись совершенно иной областью разработки. Вы решаете задачи, которые находятся за пределами обычной автоматизации таблиц. Например, вы пишете приложение для управления данными (CRUD), чтобы автоматизировать или упростить ввод данных в вашу ERP-систему, или поддерживаете набор вспомогательных таблиц в корпоративной базе данных. Вполне вероятно, что кто-то из программистов может спросить: «Почему вы делаете это на VBA?»
Ответ «Потому что могу» здесь вполне приемлем, хотя чаще встречается ответ «Потому что приходится». В любом случае это не столь важно: хорошо написанный код на VBA лучше, чем плохо написанный код на VB.NET, C# (или Java, или любом другом языке). Если вы пишете на VB.NET и в ваших модулях/классах в начале стоит «Imports Microsoft.VisualBasic», то скорее всего вы пишете не идиоматичный код .NET, а просто улучшенную версию VB6 с использованием современного синтаксиса в современной IDE.
Плохой код — это вина программиста, а не языка.
Когда вы разрабатываете полноценное приложение, процедурное программирование может быть вредным. Вы создаете сложную систему, используя парадигму, которая плохо масштабируется. Функциональное программирование могло бы быть вариантом для основной логики приложения, но VBA не предназначен для функционального программирования. В такой ситуации наиболее разумным подходом кажется объектно-ориентированное программирование.
А как насчет RAD?
Программные средства для быстрого создания приложений (Rapid Application Development), такие как Microsoft Access, стирают границы: вам предоставляется каркас для написания событийно-ориентированного кода (что, по сути, происходит из ООП), однако применение объектно-ориентированных шаблонов (например, MVC) может ощущаться как работа против этого фреймворка... что никогда не является хорошим признаком. Оптимальный подход в данном случае – принять фреймворк и постараться выделить основную логику в небольшие, специализированные и самостоятельные компоненты, которые можно будет протестировать по отдельности.