Вы абстрагируете Worksheet
за "прокси" классом;по определению, он связан с рабочим листом, и вам нужно убедиться, что абстракция герметична, чтобы вы не смотрели на негерметичную абстракцию и в итоге не связали другой код с Excel.Worksheet
type, который побеждает всю цель.
Для остальной части проекта прокси-класс рабочего листа действует как фасад , который манипулирует и понимает все, что нужно знать о конкретном Excel.Worksheet
: следствием этого является то, что теперь вы можете использовать два модуля для абстрагирования элементов рабочего листа - самого рабочего листа и прокси-класса:
- Код для рабочего листа может абстрагировать такие вещи, как
ListObject
/ tablesименованные диапазоны и т.д .;используя Property Get
членов, которые может использовать прокси. - Класс прокси-таблицы Worksheet абстрагирует манипуляции с таблицами от остальной части кода.
Действительно, этот подход не оставляет много места /потребность в фактическом выделении кода на листе: я бы начал кодировать все в прокси-классе, и если этот модуль становится слишком многословным, или если я считаю, что его уровень абстракции должен стать немного выше, то я бы переместил нижнийвыравнивать материал по самому коду рабочего листа. Модули
Workheet
и другие модули документов не должны реализовывать интерфейсы - создание рабочего листа, реализующего интерфейс, является хорошим способом запутать и вывести VBA из строя: не делайте этого,Так что это может быть ваш программный код:
Option Explicit
Public Property Get SomeSpecificRange() As Range
Set SomeSpecificRange = Me.Names("SomeSpecificRange").RefersToRange
End Property
Тогда прокси-класс может сделать это:
Option Explicit
Private sheetUI As Sheet1
Private WithEvents sheet As Worksheet
Private Sub Class_Initialize()
Set sheet = Sheet1
Set sheetUI = Sheet1
End Sub
Private Sub sheet_Change(ByVal Target As Range)
If Intersect(Target, sheetUI.SomeSpecificRange) Then
'...
End If
End Sub
Таким образом, прокси-класс может отлично обрабатывать события рабочей таблицы без подключения всего адаптера.,Он также может обрабатывать команды, поступающие от вашего докладчика, через его открытых членов Public
.
Но прокси-класс, также называемый «абстрактный лист», не является подходящим местом для ответа на события: это докладчик, который должензапустить шоу.
Таким образом, вы заставляете прокси запускать событие в ответ на события рабочего листа, завершая и отправляя сообщение докладчику:
Option Explicit
Public Event SomeSpecificRangeChanged()
Private sheetUI As Sheet1
Private WithEvents sheet As Worksheet
Private Sub Class_Initialize()
Set sheet = Sheet1
Set sheetUI = Sheet1
End Sub
Private Sub sheet_Change(ByVal Target As Range)
If Intersect(Target, sheetUI.SomeSpecificRange) Then
RaiseEvent SomeSpecificRangeChanged
End If
End Sub
Затем докладчик может обработать SomeSpecificRangeChanged
off прокси-класс - вызовите некоторую UserForm, запустите некоторый запрос к базе данных, независимо от требований:
Private WithEvents proxy As Sheet1Proxy
Private Sub Class_Initialize()
Set proxy = New Sheet1Proxy
End Sub
Private Sub proxy_SomeSpecificRangeChanged()
'business logic to run when SomeSpecificRange is changed
End Sub
Проблема в том, что прокси-класс связан с рабочим листом, и теперь докладчикв сочетании с прокси-сервером: мы абстрагировали много вещей, но по-прежнему нет способа поменять зависимость рабочего листа / прокси-сервера на что-то другое и проверить логику презентатора без использования рабочего листа.
Итак, мы создаем интерфейсотделить докладчика от прокси - скажем, ISheet1Proxy
... и теперь мы застряли, потому что мы не можем разоблачитьСобытия в интерфейсе.
Именно здесь в игру вступает шаблон адаптера, позволяющий нам формализовать интерфейсы для «команд» (презентатор -> просмотр) и «событий» (просмотр -> докладчик).
С помощью адаптера рабочий лист / прокси-сервер и презентатор теперь полностью отделены, и теперь вы можете реализовать логику презентатора без знания каких-либо Excel.Worksheet
и в идеале любых Excel.Range
или Excel.*
: каждыйВзаимодействие с рабочим листом формализуется в виде некоторой «команды», отправляемой представлению / рабочему листу / прокси-серверу, или некоторого «события», отправляемого докладчику, точно так же, как в проекте «Морской бой».
Примечание, обнаруженное мнойчто материал WeakReference
не всегда был нужен для правильного разрушения иерархий объектов: поэтому он больше не используется в текущей версии кода линейного корабля.
Очевидно, это много работы.Это отличная практика для принципов ООП и обучения написанию разъединенного кода, который можно тестировать модульно ... но для небольшого VBA-проекта это сильно излишнее ИМО.
Все это рассматривает Excel.*
классы как конкретные типы, что, как и VBA, вполне может иметь место.Тем не менее, все типы взаимодействия Excel
являются интерфейсами в отношении .NET, поэтому Rubberduck собирается значительно упростить все , предоставив API-оболочку для Moq , чрезвычайно популярного.NET mocking Framework:
Это избавит от необходимости полностью отделять рабочие листы от кода пользователя, чтобы сделать его полностью тестируемым - единственным требованием будет внедрение зависимостей, т.е. предпочитаю это:
Public Sub DoSomething(ByVal target As Range)
target.Value = 42
End Sub
За это:
Public Sub DoSomething()
Dim target As Range
Set target = Sheet1.Range("A1")
target.Value = 42
End Sub