Есть ли недостатки в создании кода в Userforms вместо модhive?

Есть ли недостатки в том, чтобы вводить код в пользовательскую форму VBA вместо «нормального» модуля?

Это может быть простой вопрос, но я не нашел для него окончательного ответа при поиске в Интернете и stackoverflow.

Предпосылки: Я разрабатываю Front-End приложение базы данных в Excel-VBA. Для выбора разных фильтров у меня разные пользовательские формы. Я спрашиваю, какой общий дизайн программы лучше: (1) поместить структуру управления в отдельный модуль OR (2), поместив код для следующей пользовательской формы или действия в пользовательскую форму .

Давайте сделаем пример. У меня есть кнопка Active-X, которая запускает мои фильтры и мои формы.

Вариант1: Модули

В CommandButton:

Private Sub CommandButton1_Click() call UserInterfaceControlModule End Sub 

В модуле:

 Sub UserInterfaceControllModule() Dim decisionInput1 As Boolean Dim decisionInput2 As Boolean UserForm1.Show decisionInput1 = UserForm1.decision If decisionInput1 Then UserForm2.Show Else UserForm3.Show End If End Sub 

В Варианте 1 структура управления находится в нормальном модуле. И решения, которые пользовательформат отображает дальше, отделены от пользовательской формы. Любая информация, необходимая для принятия решения о том, какую пользовательскую форму для показа дальше следует вытащить из пользовательской формы.

Вариант2: Пользовательская форма

В CommadButton:

 Private Sub CommandButton1_Click() UserForm1.Show End Sub 

В Userform1:

 Private Sub ToUserform2_Click() UserForm2.Show UserForm1.Hide End Sub Private Sub UserForm_Click() UserForm2.Show UserForm1.Hide End Sub 

В Варианте 2 структура управления находится непосредственно в пользовательских формах, и пользовательская форма имеет инструкции, которые следуют за ней.

Я начал разработку с использованием метода 2. Если это была ошибка, и есть некоторые серьезные недостатки этого метода, я хочу знать это скорее раньше, чем позже.

    Отказ от ответственности Я написал статью Виктора К, связанную с . Я владею этим блогом и управляю проектом надстройки VBIDE с открытым исходным кодом, для которого он предназначен.

    Ни одна из ваших альтернатив не идеальна. Вернуться к основам.


    Для выбора разных фильтров у меня есть разные (пользовательские) пользовательские формы.

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

    Создание формы, ответственной за что-либо, кроме проблемы с представлением, является распространенной ошибкой, и у нее есть имя: это шаблон Smart UI [anti-], и проблема заключается в том, что он не масштабируется . Это отлично подходит для прототипирования (т. Е. Быстро сделать то, что «работает», – обратите внимание на кавычки), а не столько для чего-то, что нужно поддерживать в течение многих лет.

    Вероятно, вы видели эти формы с 160 элементами управления, 217 обработчиками событий и 3 частными процедурами, закрывающимися на 2000 строк кода: вот как сильно умный пользовательский интерфейс масштабируется, и это единственный возможный результат по этой дороге.

    Вы видите, что UserForm является модулем classа: он определяет схему объекта . Объекты обычно хотят быть инстанцированными , но тогда у кого-то была гениальная идея предоставить все экземпляры MSForms.UserForm предварительно установленного идентификатора , который в терминах СОМ означает, что вы в основном получаете глобальный объект бесплатно.

    Большой! Нет? Нет.

     UserForm1.Show decisionInput1 = UserForm1.decision If decisionInput1 Then UserForm2.Show Else UserForm3.Show End If 

    Что произойдет, если UserForm1 «X’d-out»? Или если UserForm1 Unload ed? Если форма не обрабатывает событие QueryClose , объект уничтожается – но поскольку это экземпляр по умолчанию , VBA автоматически / молча создает для вас новый, как раз перед тем, как ваш код читает UserForm1.decision – в результате вы получаете все, что угодно начальное глобальное состояние для UserForm1.decision .

    Если это не был экземпляр по умолчанию , а QueryClose не обрабатывался, доступ к .decision уничтоженного объекта дал бы вам classическую ошибку 91 времени выполнения для доступа к ссылке на нулевой объект.

    UserForm2.Show и UserForm3.Show обе делают то же самое: fire-and-forget – что бы ни случилось, и чтобы выяснить, что именно состоит, вам нужно выкопать его в соответствующем коде.

    Другими словами, формы запускают шоу . Они несут ответственность за сбор данных, представление этих данных, сбор пользовательских данных и выполнение любой работы с ним . Вот почему он называется «Smart UI»: пользовательский интерфейс все знает.

    Есть лучший способ. MSForms является предшественником COM-интерфейса .NET WinForms UI, и то, что у предка имеет общее с его преемником .NET, заключается в том, что он отлично работает со знаменитым шаблоном Model-View-Presenter (MVP).


    Модель

    Это ваши данные . По сути, это то, что ваша логика приложения должна знать из формы.

    • UserForm1.decision пойдем с этим.

    Добавьте новый class, назовите его, скажем, FilterModel . Должен быть очень простой class:

     Option Explicit Private Type TModel SelectedFilter As String End Type Private this As TModel Public Property Get SelectedFilter() As String SelectedFilter = this.SelectedFilter End Property Public Property Let SelectedFilter(ByVal value As String) this.SelectedFilter = value End Property Public Function IsValid() As Boolean IsValid = this.SelectedFilter <> vbNullString End Function 

    Это действительно все, что нам нужно: class для инкапсуляции данных формы. Класс может нести ответственность за какую-либо логику проверки или что-то еще – но он не собирает данные, он не представляет его пользователю и не потребляет его. Это данные.

    Здесь есть только 1 свойство, но у вас может быть еще много: подумайте об одном поле в форме => одно свойство.

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


    Вид

    Это ваша форма. Он несет ответственность за знание контроля, запись и чтение из модели и … вот и все. Мы смотрим на диалог здесь: мы его поднимаем, пользователь заполняет его, закрывает его, и программа действует на него – сама форма ничего не делает с данными, которые она собирает. Модель может подтвердить это, форма может решить отключить кнопку Ok до тех пор, пока модель не сообщит, что ее данные являются правильными и хорошими, но ни при каких обстоятельствах пользовательская UserForm читает или не записывает с рабочего листа, базы данных, файла, URL-адреса, или что-нибудь.

    Кодировка кода формы проста: она соединяет интерфейс с экземпляром модели и включает / отключает его кнопки по мере необходимости.

    Важно помнить:

    • Hide , не Unload : представление – это объект, а объекты не разрушаются самостоятельно.
    • НИКОГДА не обращайтесь к экземпляру по умолчанию в форме.
    • Всегда снова обрабатывайте QueryClose , чтобы избежать саморазрушающего объекта («X-ing out» формы в противном случае уничтожит экземпляр).

    В этом случае код-код может выглядеть так:

     Option Explicit Private Type TView Model As FilterModel IsCancelled As Boolean End Type Private this As TView Public Property Get Model() As FilterModel Set Model = this.Model End Property Public Property Set Model(ByVal value As FilterModel) Set this.Model = value Validate End Property Public Property Get IsCancelled() As Boolean IsCancelled = this.IsCancelled End Property Private Sub TextBox1_Change() this.Model.SelectedFilter = TextBox1.Text Validate End Sub Private Sub OkButton_Click() Me.Hide End Sub Private Sub Validate() OkButton.Enabled = this.Model.IsValid End Sub Private Sub CancelButton_Click() OnCancel End Sub Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer) If CloseMode = VbQueryClose.vbFormControlMenu Then Cancel = True OnCancel End If End Sub Private Sub OnCancel() this.IsCancelled = True Me.Hide End Sub 

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


    Ведущий

    Это объект «клей», который соединяет точки.

     Option Explicit Public Sub DoSomething() Dim m As FilterModel Set m = New FilterModel With New FilterForm Set .Model = m 'set the model .Show 'display the dialog If Not .IsCancelled Then 'how was it closed? 'consume the data Debug.Print m.SelectedFilter End If End With End Sub 

    Если данные в модели должны поступать из базы данных или какого-либо рабочего листа, он использует экземпляр classа (да, другой объект!), Который отвечает за выполнение именно этого.

    Вызывающий код может быть вашим клиентом обработчика ActiveX, New up до ведущего и вызовом метода DoSomething .


    Это не все, что нужно знать о OOP в VBA (я даже не упоминал об интерфейсах, polymorphismе, тестовых заглушках и модульном тестировании), но если вы хотите объективно масштабируемый код, вы захотите спуститься по кроличьей дыре MVP и исследовать возможности, которые по-настоящему объектно-ориентированный код приносит VBA.


    TL; DR:

    Код («бизнес-логика») просто не принадлежит коду кода в любой кодовой базе, которая означает масштабирование и сохранение в течение нескольких лет.

    В «варианте 1» код трудно следовать, потому что вы перепрыгиваете между модулями, а проблемы с представлением смешиваются с логикой приложения: задача не состоит в том, чтобы знать, какая другая форма отображалась при нажатии кнопки A или кнопки B. Вместо этого он должен позволить ведущему знать, что означает пользователь, и действовать соответствующим образом.

    В «варианте 2» код трудно следовать, потому что все скрыто в кодировке userforms: мы не знаем, что такое логика приложения, если мы не вникаем в этот код, который теперь намеренно смешивает проблемы представления и бизнес-логики. Это именно то , что делает анти-шаблон «Умный интерфейс».

    Другими словами, вариант 1 немного лучше, чем вариант 2, потому что, по крайней мере, логика не находится в коде, но она по-прежнему является «интеллектуальным интерфейсом», потому что она запускает шоу, а не говорит своему вызывающему, что происходит .

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

    Обработайте формы, как объекты, которые они представляют: создайте их!

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

    Interesting Posts

    При обработке отчета произошла ошибка. Отчеты -RLDC в ASP.NET MVC

    Почему летучесть используется в этом примере двойной проверки блокировки

    Ограничение от двух до трех знаков после запятой

    Robocopy: ОШИБКА 5 (0x00000005) Доступ к исходному каталогу \\ сервер \ ParentFolder \ ChildFolder \ Access запрещен

    Какие языки программирования могут использоваться для разработки в Android?

    Как заставить make / gcc показать мне команды?

    Инициализация строки по умолчанию: NULL или Empty?

    Почему String.split требует, чтобы разделитель строк был экранирован?

    Есть ли способ синхронизировать контакты Google на вашем iPhone?

    Нет выхода видео с материнской платы. Как отладить это?

    Как я могу набрать испанский инвертированный знак вопроса в Emacs?

    Mongoose удалить элемент массива в документе и сохранить

    Как использовать два интернет-соединения (WiFi / Wired)?

    Как извлечь n-е слово и подсчитать вхождения в строку MySQL?

    ASP.NET MVC DropDownListДля выбора значения из модели

    Давайте будем гением компьютера.