programing

모듈 대신 사용자 양식에 코드를 입력하는 것에 단점이 있습니까?

magicmemo 2023. 5. 31. 15:47
반응형

모듈 대신 사용자 양식에 코드를 입력하는 것에 단점이 있습니까?

코드를 "일반" 모듈이 아닌 VBA 사용자 양식에 입력하면 단점이 있습니까?

이것은 간단한 질문일 수도 있지만 웹과 스택 오버플로를 검색하는 동안 결정적인 답변을 찾지 못했습니다.

배경:저는 Excel-VBA에 있는 데이터베이스의 프런트 엔드 애플리케이션을 개발하고 있습니다.다른 필터를 선택하기 위해 다른 사용자 양식이 있습니다.(1) 제어 구조를 별도의 모듈에 넣거나 (2) 다음 사용자 양식 또는 동작을 위한 코드를 사용자 양식에 넣는 것이 어떤 일반적인 프로그램 설계가 더 나은지 묻습니다.

예를 들어 보겠습니다.필터와 양식을 트리거하는 Active-X 버튼이 있습니다.

변형 1: 모듈

명령 버튼에서:

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:사용자 양식

Commad 버튼에서 다음 작업을 수행합니다.

Private Sub CommandButton1_Click()
  UserForm1.Show
End Sub

사용자 양식 1:

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

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

변형 2에서 제어 구조는 사용자 양식에 직접 있으며 각 사용자 양식에는 그 뒤에 나오는 내용에 대한 지침이 있습니다.

저는 2번 방법을 사용하여 개발을 시작했습니다.만약 이것이 실수이고 이 방법에 심각한 단점이 있다면 저는 그것을 빨리 알고 싶습니다.

고지 사항 빅터 K가 링크기사를 작성했습니다.저는 그 블로그를 소유하고 있으며, 그 블로그가 사용하는 오픈 소스 VBIDE 애드인 프로젝트를 관리하고 있습니다.

두 가지 대안 모두 이상적이지 않습니다.기본으로 돌아가세요.


다른 필터를 선택하기 위해 (sic) 사용자 양식이 다릅니다.

에 따라할 수 하며, 했습니다.UserForm거기서부터 내리막길입니다.

프레젠테이션 문제 이외의 다른 문제에 대해 폼을 책임지게 하는 것은 일반적인 실수이며, Smart UI [반(反)] 패턴이라는 이름이 있습니다. 문제는 폼이 확장되지 않는다는 것입니다.프로토타이핑(즉, "효과가 있는" 빠른 것을 만드는 것 - 위협적인 인용문을 참고)에 적합합니다. 수년에 걸쳐 유지해야 하는 모든 것에 적합하지 않습니다.

160개의 컨트롤, 217개의 이벤트 핸들러 및 3개의 개인 절차가 각각 2000줄의 코드로 마감되는 이러한 양식을 본 적이 있을 것입니다. 스마트 UI의 확장성이 형편없으며, 앞으로 가능한 유일한 결과입니다.

아잖있▁a,UserForm클래스 모듈: 개체Blueprint를 정의합니다.객체들은 보통 인스턴스화되기를 원하지만, 누군가는 모든 예시들을 허용하는 천재적인 아이디어를 가지고 있었습니다.MSForms.UserFormCOM 용어로 글로벌 객체를 기본적으로 무료로 얻는 것을 의미하는 사전 선언된 ID.

좋아요! 아닌가요?

UserForm1.Show
decisionInput1 = UserForm1.decision

If decisionInput1 Then
  UserForm2.Show
Else
  UserForm3.Show
End If

그러면 어떻게 됩니까?UserForm1"요?X'd out 인가요?아니면 만약에UserForm1이라Unload?않는 QueryClose이벤트, 객체는 파괴되지만 기본 인스턴스이기 때문에 VBA는 코드가 읽기 직전에 자동/사일런트로 새 인스턴스를 만듭니다.UserForm1.decision결과적으로 초기 글로벌 상태가 무엇이든 얻을 수 있습니다.UserForm1.decision.

기본 인스턴스가 아니라면,QueryClose처리되지 않았습니다. 그 다음에 액세스합니다..decision파괴된 개체의 구성원은 null 개체 참조에 액세스할 수 있는 고전적인 런타임 오류 91을 제공합니다.

UserForm2.Show그리고.UserForm3.Show둘 다 같은 일을 합니다. 화재와 폭발 - 무슨 일이 일어나든, 그리고 그것이 정확히 무엇으로 구성되어 있는지 알기 위해서는, 당신은 그것을 양식의 각각의 코드 이면에서 파내야 합니다.

즉, 양식이 프로그램을 실행하고 있습니다.그들은 데이터를 수집하고, 그 데이터를 제시하고, 사용자 입력을 수집하고, 그것으로 해야 일을 무엇이든 할 책임이 있습니다.이것이 바로 "스마트 UI"라고 불리는 이유입니다. UI는 모든 것을 알고 있습니다.

더 좋은 방법이 있습니다.MSForms는 .NET의 WinForms UI 프레임워크의 COM 조상이며, 이 조상과 .NET의 공통점입니다.NET의 후계자는 유명한 MVP(Model-View-Presenter) 패턴과 특히 잘 작동한다는 것입니다.


모델

그것은 당신의 데이터입니다.기본적으로 는 애플리케이션 로직이 양식을 벗어나서 알아야 하는 사항입니다.

  • UserForm1.decision그렇게 하죠.

새 클래스를 추가하고, 호출하고, 이렇게 말합니다.FilterModel매우 간단한 클래스여야 합니다.

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

양식의 데이터를 캡슐화하는 클래스만 있으면 됩니다.클래스는 일부 검증 로직 또는 기타 작업을 담당할 수 있지만 데이터를 수집하지 않고 사용자에게 제공하지 않으며 사용하지도 않습니다.그것은 데이터입니다.

여기에는 단 하나의 속성이 있지만 더 많은 것을 가질 수 있습니다. 양식에 있는 하나의 필드 => 하나의 속성을 생각해 보십시오.

모델은 또한 응용프로그램 로직을 통해 폼이 알아야 하는 것입니다.예를 들어 양식에 가능한 선택 항목의 수를 표시하는 드롭다운이 필요한 경우 모델이 해당 선택 항목을 노출하는 개체가 됩니다.


더 뷰

그게 당신의 양식입니다.컨트롤에 대해 알고, 모델에 글을 쓰고, 모델에서 읽고, 그리고...그게 다야여기서는 대화상자를 보고 있습니다. 우리는 대화상자를 열고, 사용자는 채우고, 닫으면, 프로그램은 그에 따라 작동합니다. 양식 자체는 수집한 데이터를 처리하지 않습니다.모델이 유효성을 검사할 수도 있고, 모델이 데이터가 유효하고 이동하기 좋다고 말할 때까지 버튼을 비활성화하기로 결정할 수도 있지만, 어떠한 상황에서도UserForm워크시트, 데이터베이스, 파일, URL 등에서 읽거나 씁니다.

이 양식의 코드백은 매우 간단합니다. 모델 인스턴스와 UI를 연결하고 필요에 따라 버튼을 활성화/비활성화합니다.

기억해야 할 중요한 사항:

  • 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

모델의 데이터가 데이터베이스 또는 일부 워크시트에서 가져와야 하는 경우 이를 수행하는 클래스 인스턴스(예, 다른 개체!)를 사용합니다.

버튼의 핸들러인 "ActiveX"일 수 .New-발표자에게 전화를 걸어 발표자에게 전화하기DoSomething방법.


이것이 VBA의 OOP에 대해 알아야 할 전부는 아닙니다(인터페이스, 다형성, 테스트 스텁 및 유닛 테스트에 대해서는 언급하지 않았습니다). 하지만 객관적으로 확장 가능한 코드를 원한다면 MVP 토끼굴로 내려가 진정한 객체 지향 코드가 VBA에 가져올 가능성을 탐구하고 싶을 것입니다.


TL;DR:

코드("비즈니스 논리")는 양식의 코드 뒤에 포함되지 않으며, 몇 년에 걸쳐 확장 및 유지 관리가 가능한 코드 기반에는 포함되지 않습니다.

"변수 1"에서는 모듈 사이를 건너뛰기 때문에 코드를 따르기 어렵고 프레젠테이션 문제가 응용 프로그램 로직과 혼합됩니다. 주어진 버튼 A 또는 버튼 B를 누른 다른 형식을 아는 것은 양식의 일이 아닙니다.대신 발표자에게 사용자가 무엇을 해야 하는지 알려주고 그에 따라 행동해야 합니다.

"변종 2"에서는 모든 것이 사용자 양식의 코드 뒤에 숨겨져 있기 때문에 코드를 따르기 어렵습니다. 이제 프레젠테이션과 비즈니스 논리 문제를 의도적으로 혼합하는 코드를 파고들지 않으면 응용 프로그램 논리가 무엇인지 알 수 없습니다.그것이 바로 "스마트 UI" 안티패턴이 하는 일입니다.

즉, 적어도 논리가 코드백에 있지 않기 때문에 변종 1이 변종 2보다 약간 낫지만, 발신자에게 무슨 일이 일어나고 있는지 알려주는 대신 쇼를 실행하기 때문에 여전히 "스마트 UI"입니다.

두 경우 모두 폼의 기본 인스턴스에 대한 코딩은 상태를 전역 범위로 설정하기 때문에 해롭습니다(누구나 코드의 어느 곳에서나 기본 인스턴스에 액세스하여 상태에 대한 모든 작업을 수행할 수 있음).

폼을 개체처럼 처리합니다. 인스턴스화!

두 경우 모두 폼의 코드가 애플리케이션 로직과 밀접하게 연결되어 있고 프레젠테이션 관련 문제와 얽혀 있기 때문에 현재 진행 중인 작업의 단일 측면을 다루는 단일 단위 테스트를 작성하는 것은 완전히 불가능합니다.MVP 패턴을 사용하면 구성 요소를 완전히 분리하고 인터페이스 뒤에서 추상화하고 책임을 분리할 수 있습니다.그리고 모든 기능을 망라하는 수십 개의 자동 장치 테스트를 작성하고 설명서를 작성하지 않고도 사양이 무엇인지 정확하게 문서화합니다. 코드는 자체 설명서가 됩니다.

언급URL : https://stackoverflow.com/questions/47288496/are-there-disadvantages-in-putting-code-into-userforms-instead-of-modules

반응형