VBA
ऑब्जेक्ट-ओरिएंटेड VBA
खोज…
मतिहीनता
अमूर्तता का स्तर चीजों को विभाजित करने के लिए निर्धारित करने में मदद करता है।
तेजी से विस्तृत कोड के साथ कार्यक्षमता को लागू करके अमूर्तता प्राप्त की जाती है। एक मैक्रो का प्रवेश बिंदु एक उच्च अमूर्त स्तर के साथ एक छोटी सी प्रक्रिया होनी चाहिए जो एक नज़र में समझ लेना आसान बनाता है कि क्या हो रहा है:
Public Sub DoSomething()
With New SomeForm
Set .Model = CreateViewModel
.Show vbModal
If .IsCancelled Then Exit Sub
ProcessUserData .Model
End With
End Sub
DoSomething
प्रक्रिया में एक उच्च अमूर्त स्तर होता है : हम बता सकते हैं कि यह एक फॉर्म प्रदर्शित कर रहा है और कुछ मॉडल बना रहा है, और उस ऑब्जेक्ट को कुछ ProcessUserData
प्रक्रिया से गुजर रहा है जो जानता है कि इसके साथ क्या करना है - मॉडल कैसे बनाया जाता है, यह एक अन्य प्रक्रिया का काम है:
Private Function CreateViewModel() As ISomeModel
Dim result As ISomeModel
Set result = SomeModel.Create(Now, Environ$("UserName"))
result.AvailableItems = GetAvailableItems
Set CreateViewModel = result
End Function
CreateViewModel
फ़ंक्शन केवल कुछ ISomeModel
उदाहरण बनाने के लिए ज़िम्मेदार है। उस जिम्मेदारी का एक हिस्सा उपलब्ध वस्तुओं का एक सरणी प्राप्त करना है - इन वस्तुओं को कैसे प्राप्त किया जाता है यह एक कार्यान्वयन विवरण है जो GetAvailableItems
प्रक्रिया के पीछे सार है:
Private Function GetAvailableItems() As Variant
GetAvailableItems = DataSheet.Names("AvailableItems").RefersToRange
End Function
यहां प्रक्रिया DataSheet
वर्कशीट पर नामांकित सीमा से उपलब्ध मानों को पढ़ रही है। यह सिर्फ एक डेटाबेस से उन्हें पढ़ सकता है, या मूल्यों को हार्ड-कोडित किया जा सकता है: यह एक कार्यान्वयन विवरण है जो उच्चतर अमूर्त स्तरों में से किसी के लिए चिंता का विषय नहीं है।
encapsulation
एन्कैप्सुलेशन क्लाइंट कोड से कार्यान्वयन विवरण छिपाता है।
हैंडलिंग QueryClose उदाहरण इनकैप्सुलेशन को प्रदर्शित करता है: फ़ॉर्म में एक चेकबॉक्स नियंत्रण होता है, लेकिन इसका क्लाइंट कोड सीधे इसके साथ काम नहीं करता है - चेकबॉक्स एक कार्यान्वयन विवरण है , क्लाइंट कोड को यह जानना होगा कि सेटिंग सक्षम है या नहीं।
जब चेकबॉक्स मान बदलता है, तो हैंडलर एक निजी क्षेत्र का सदस्य प्रदान करता है:
Private Type TView IsCancelled As Boolean SomeOtherSetting As Boolean 'other properties skipped for brievety End Type Private this As TView '... Private Sub SomeOtherSettingInput_Change() this.SomeOtherSetting = CBool(SomeOtherSettingInput.Value) End Sub
और जब क्लाइंट कोड उस मूल्य को पढ़ना चाहता है, तो उसे एक चेकबॉक्स के बारे में चिंता करने की आवश्यकता नहीं है - इसके बजाय यह केवल कुछ SomeOtherSetting
संपत्ति का उपयोग करता है:
Public Property Get SomeOtherSetting() As Boolean SomeOtherSetting = this.SomeOtherSetting End Property
SomeOtherSetting
गुण चेकबॉक्स स्थिति को SomeOtherSetting
करता है; क्लाइंट कोड को यह जानने की जरूरत नहीं है कि इसमें एक चेकबॉक्स शामिल है, केवल यह कि बूलियन मान के साथ एक सेटिंग है। द्वारा encapsulating Boolean
मूल्य, हम चेकबॉक्स के चारों ओर एक अमूर्त स्तर जोड़ा है।
अपरिवर्तनशीलता को लागू करने के लिए इंटरफेस का उपयोग करना
आइए धक्का देते हैं कि एक समर्पित वर्ग मॉड्यूल में फॉर्म के मॉडल को संलग्न करके एक कदम आगे। लेकिन अगर हमने UserName
और Timestamp
लिए एक Public Property
बनाई है, तो हमें Public Property
को UserName
करने वाले Property Let
एक्सेसर्स को उजागर करना होगा, और हम क्लाइंट कोड को इन मूल्यों को सेट करने के बाद बदलने की क्षमता नहीं रखना चाहते हैं।
Abstraction उदाहरण में CreateViewModel
फ़ंक्शन एक ISomeModel
श्रेणी देता है: यह हमारा इंटरफ़ेस है , और यह इस तरह दिखता है:
Option Explicit
Public Property Get Timestamp() As Date
End Property
Public Property Get UserName() As String
End Property
Public Property Get AvailableItems() As Variant
End Property
Public Property Let AvailableItems(ByRef value As Variant)
End Property
Public Property Get SomeSetting() As String
End Property
Public Property Let SomeSetting(ByVal value As String)
End Property
Public Property Get SomeOtherSetting() As Boolean
End Property
Public Property Let SomeOtherSetting(ByVal value As Boolean)
End Property
नोटिस Timestamp
और UserName
गुण केवल एक Property Get
UserName
बेनकाब करते हैं। अब SomeModel
वर्ग उस इंटरफ़ेस को लागू कर सकता है:
Option Explicit
Implements ISomeModel
Private Type TModel
Timestamp As Date
UserName As String
SomeSetting As String
SomeOtherSetting As Boolean
AvailableItems As Variant
End Type
Private this As TModel
Private Property Get ISomeModel_Timestamp() As Date
ISomeModel_Timestamp = this.Timestamp
End Property
Private Property Get ISomeModel_UserName() As String
ISomeModel_UserName = this.UserName
End Property
Private Property Get ISomeModel_AvailableItems() As Variant
ISomeModel_AvailableItems = this.AvailableItems
End Property
Private Property Let ISomeModel_AvailableItems(ByRef value As Variant)
this.AvailableItems = value
End Property
Private Property Get ISomeModel_SomeSetting() As String
ISomeModel_SomeSetting = this.SomeSetting
End Property
Private Property Let ISomeModel_SomeSetting(ByVal value As String)
this.SomeSetting = value
End Property
Private Property Get ISomeModel_SomeOtherSetting() As Boolean
ISomeModel_SomeOtherSetting = this.SomeOtherSetting
End Property
Private Property Let ISomeModel_SomeOtherSetting(ByVal value As Boolean)
this.SomeOtherSetting = value
End Property
Public Property Get Timestamp() As Date
Timestamp = this.Timestamp
End Property
Public Property Let Timestamp(ByVal value As Date)
this.Timestamp = value
End Property
Public Property Get UserName() As String
UserName = this.UserName
End Property
Public Property Let UserName(ByVal value As String)
this.UserName = value
End Property
Public Property Get AvailableItems() As Variant
AvailableItems = this.AvailableItems
End Property
Public Property Let AvailableItems(ByRef value As Variant)
this.AvailableItems = value
End Property
Public Property Get SomeSetting() As String
SomeSetting = this.SomeSetting
End Property
Public Property Let SomeSetting(ByVal value As String)
this.SomeSetting = value
End Property
Public Property Get SomeOtherSetting() As Boolean
SomeOtherSetting = this.SomeOtherSetting
End Property
Public Property Let SomeOtherSetting(ByVal value As Boolean)
this.SomeOtherSetting = value
End Property
इंटरफ़ेस सदस्य सभी Private
, और कोड को संकलित करने के लिए इंटरफ़ेस के सभी सदस्यों को लागू किया जाना चाहिए। Public
सदस्य इंटरफ़ेस का हिस्सा नहीं हैं, और इसलिए ISomeModel
इंटरफ़ेस के खिलाफ लिखे गए कोड के संपर्क में नहीं हैं।
एक निर्माता का अनुकरण करने के लिए एक फैक्ट्री विधि का उपयोग करना
VB_PredeclaredId विशेषता का उपयोग करते हुए, हम SomeModel
वर्ग को एक डिफ़ॉल्ट उदाहरण बना सकते हैं, और एक फ़ंक्शन लिख सकते हैं जो एक प्रकार-स्तर (VB.NET में Shared
, C # में static
) सदस्य की तरह काम करता है जिसे ग्राहक कोड पहले बनाने की आवश्यकता के बिना कॉल कर सकता है। एक उदाहरण, जैसे हमने यहाँ किया:
Private Function CreateViewModel() As ISomeModel Dim result As ISomeModel Set result = SomeModel.Create(Now, Environ$("UserName")) result.AvailableItems = GetAvailableItems Set CreateViewModel = result End Function
यह फैक्ट्री विधि संपत्ति मूल्यों को पढ़ती है जो केवल ISomeModel
इंटरफ़ेस से एक्सेस किए जाते हैं, यहां Timestamp
और UserName
:
Public Function Create(ByVal pTimeStamp As Date, ByVal pUserName As String) As ISomeModel
With New SomeModel
.Timestamp = pTimeStamp
.UserName = pUserName
Set Create = .Self
End With
End Function
Public Property Get Self() As ISomeModel
Set Self = Me
End Property
और अब हम ISomeModel
इंटरफ़ेस के खिलाफ कोड कर सकते हैं, जो Timestamp
और UserName
को केवल-पढ़ने योग्य गुणों के रूप में उजागर करता है जिन्हें कभी भी पुन: असाइन नहीं किया जा सकता है (जब तक कोड इंटरफ़ेस के खिलाफ लिखा गया है)।
बहुरूपता
बहुरूपता विभिन्न अंतर्निहित कार्यान्वयन के लिए एक ही इंटरफ़ेस प्रस्तुत करने की क्षमता है।
इंटरफेस को लागू करने की क्षमता पूरी तरह से यूआई से या डेटाबेस से या इस या उस वर्कशीट से एप्लिकेशन लॉजिक को डिकोड करने की अनुमति देती है।
मान लें कि आपके पास एक ISomeView
इंटरफ़ेस है जो फ़ॉर्म स्वयं लागू करता है:
Option Explicit
Public Property Get IsCancelled() As Boolean
End Property
Public Property Get Model() As ISomeModel
End Property
Public Property Set Model(ByVal value As ISomeModel)
End Property
Public Sub Show()
End Sub
प्रपत्र का कोड-पीछे ऐसा दिख सकता है:
Option Explicit
Implements ISomeView
Private Type TView
IsCancelled As Boolean
Model As ISomeModel
End Type
Private this As TView
Private Property Get ISomeView_IsCancelled() As Boolean
ISomeView_IsCancelled = this.IsCancelled
End Property
Private Property Get ISomeView_Model() As ISomeModel
Set ISomeView_Model = this.Model
End Property
Private Property Set ISomeView_Model(ByVal value As ISomeModel)
Set this.Model = value
End Property
Private Sub ISomeView_Show()
Me.Show vbModal
End Sub
Private Sub SomeOtherSettingInput_Change()
this.Model.SomeOtherSetting = CBool(SomeOtherSettingInput.Value)
End Sub
'...other event handlers...
Private Sub OkButton_Click()
Me.Hide
End Sub
Private Sub CancelButton_Click()
this.IsCancelled = True
Me.Hide
End Sub
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
If CloseMode = VbQueryClose.vbFormControlMenu Then
Cancel = True
this.IsCancelled = True
Me.Hide
End If
End Sub
लेकिन फिर, एक और वर्ग मॉड्यूल बनाने से कोई मना नहीं करता है जो उपयोगकर्ता प्रपत्र के बिना ISomeView
इंटरफ़ेस को लागू करता है - यह एक SomeViewMock
वर्ग हो सकता है:
Option Explicit
Implements ISomeView
Private Type TView
IsCancelled As Boolean
Model As ISomeModel
End Type
Private this As TView
Public Property Get IsCancelled() As Boolean
IsCancelled = this.IsCancelled
End Property
Public Property Let IsCancelled(ByVal value As Boolean)
this.IsCancelled = value
End Property
Private Property Get ISomeView_IsCancelled() As Boolean
ISomeView_IsCancelled = this.IsCancelled
End Property
Private Property Get ISomeView_Model() As ISomeModel
Set ISomeView_Model = this.Model
End Property
Private Property Set ISomeView_Model(ByVal value As ISomeModel)
Set this.Model = value
End Property
Private Sub ISomeView_Show()
'do nothing
End Sub
और अब हम उस कोड को बदल सकते हैं जो एक UserForm
साथ काम करता है और इसे ISomeView
इंटरफ़ेस से काम करता है, जैसे कि इसे ISomeView
करने के बजाय इसे एक पैरामीटर के रूप में रूप देकर:
Public Sub DoSomething(ByVal view As ISomeView)
With view
Set .Model = CreateViewModel
.Show
If .IsCancelled Then Exit Sub
ProcessUserData .Model
End With
End Sub
क्योंकि DoSomething
विधि एक इंटरफेस (यानी एक अमूर्त) और नहीं एक ठोस वर्ग (जैसे एक विशिष्ट पर निर्भर करता है UserForm
), हम एक स्वचालित इकाई परीक्षण लिख सकते हैं कि यह सुनिश्चित करता है कि ProcessUserData
निष्पादित नहीं है जब view.IsCancelled
है True
, बनाने के द्वारा हमारे परीक्षण एक बनाने SomeViewMock
उदाहरण है, इसकी स्थापना के IsCancelled
को संपत्ति True
, और करने के लिए इसे पारित DoSomething
।
परीक्षण योग्य कोड सार पर निर्भर करता है
VBA में राइटिंग यूनिट टेस्ट किए जा सकते हैं, वहाँ ऐड-इन्स हैं जो इसे IDE में एकीकृत करते हैं। लेकिन जब कोड को किसी वर्कशीट, डेटाबेस, फॉर्म, या फाइल सिस्टम के साथ कस कर जोड़ दिया जाता है, तो यूनिट टेस्ट के लिए वास्तविक वर्कशीट, डेटाबेस, फॉर्म या फाइल सिस्टम की आवश्यकता होती है - और ये निर्भरताएँ नई आउट-ऑफ-कंट्रोल विफलता हैं परीक्षण योग्य कोड को अलग करना चाहिए, ताकि यूनिट परीक्षणों को वास्तविक वर्कशीट, डेटाबेस, फॉर्म या फाइल सिस्टम की आवश्यकता न हो।
इंटरफेस के खिलाफ कोड लिखकर, एक तरह से टेस्ट कोड को स्टब / मॉक SomeViewMock
(जैसे कि SomeViewMock
उदाहरण) को इंजेक्ट करने की अनुमति देता है, आप एक "नियंत्रित वातावरण" में परीक्षण लिख सकते हैं, और अनुकरण कर सकते हैं जब 42 में से हर एक संभव होता है। फ़ॉर्म के डेटा पर उपयोगकर्ता के इंटरैक्शन की अनुमति, एक बार भी एक फॉर्म प्रदर्शित करने और मैन्युअल रूप से नियंत्रण पर क्लिक किए बिना।