patternMinor
Enforcing Type with Boiler Plates
Viewed 0 times
enforcingwithboilerplatestype
Problem
This is an implementation of Mat's List using by Pseudo-inheriting my Pythonic-List. Inheritance is not built into VBA, but it can be simulated by composing the inherited class and mimicking all it's public methods and members. It's a PITA because a modification of a base class needs to be propagated to all sub-classes.
The implementation is simple, just add a private string data member
TypedList
Private Members
Private Methods
Type Check an element that is being added to a list. If the type has not been set then let this element set the typename. Otherwise, raise an error if it's not the correct type.
Therefore
Is valid and now
We also want to check every element in a sequence before we let any of the elements enter the list. If our string list is extended with
The above two methods are boiler plate code at the beginning of any method that sets the data in our TypedList.
This is the error that is raised when it fails.
Enumeration
Note we use
The implementation is simple, just add a private string data member
pTypedName, and whenever the client code tries to add an element where TypeName(Element) <> pTypedName raise a type error.TypedList
Private Members
Private this As List
Private pTypedName As StringPrivate Methods
Type Check an element that is being added to a list. If the type has not been set then let this element set the typename. Otherwise, raise an error if it's not the correct type.
Private Sub TypeCheck(ByVal element As Variant, ByVal source As String)
If pTypedName = vbNullString Then pTypedName = TypeName(element)
If (TypeName(element) <> pTypedName) Then RaiseTypeError element, source
End SubTherefore
Dim tList as New TypedList
tList.Append "A"Is valid and now
tList is of type String.We also want to check every element in a sequence before we let any of the elements enter the list. If our string list is extended with
Array("a", "b", "C", 2) we don't want any of the elements entering the list.Private Sub TypeCheckSequence(ByVal sequence As Variant, ByVal source As String)
Dim element As Variant
For Each element In sequence
TypeCheck element, source
Next element
End SubThe above two methods are boiler plate code at the beginning of any method that sets the data in our TypedList.
This is the error that is raised when it fails.
Private Sub RaiseTypeError(ByVal badItem As Variant, ByVal method As String)
Err.Raise 13, method, "Element is of type " & TypeName(badItem) & _
", not " & TypedName & "."
End SubEnumeration
Note we use
this.NewEnum Solution
All in all I think this looks pretty good. Without proper inheritance, you're pretty much stuck with the boiler plate properties that simply call on the private untyped
The only thing I really noticed that I didn't like was the way you're using magic numbers to raise errors.
And
First, I find it in consistent to use a sub in one place, but a direct raise in another. I know you're only using error #9 in one place now, but pulling this out will certainly make it easier to use it elsewhere in the future.
But I mentioned the magic numbers didn't I? Normally, I would recommend defining some module scoped constants to make it clear that the code is raising a Subscript out of Range and Type Mismatch error (respectively), but these are built in VB errors we're raising. We're likely to use these in many places in many modules. Given that, I think it's absolutely worth the time to create a module with a public [VbErrorNumbers] Enum. It's some work, but you only ever have to do it once. In fact, I believe I'm going to do this myself later today. A full list of built in VBA errors can be found here.
List's properties. Everything in regards to that was done right, vba just kinda sucks like that. The only thing I really noticed that I didn't like was the way you're using magic numbers to raise errors.
Err.Raise 9, TypeName(Me) & ".TypedName", _
"Can only set typename of an empty list."And
Private Sub RaiseTypeError(ByVal badItem As Variant, ByVal method As String)
Err.Raise 13, method, "Element is of type " & TypeName(badItem) & _
", not " & TypedName & "."
End SubFirst, I find it in consistent to use a sub in one place, but a direct raise in another. I know you're only using error #9 in one place now, but pulling this out will certainly make it easier to use it elsewhere in the future.
But I mentioned the magic numbers didn't I? Normally, I would recommend defining some module scoped constants to make it clear that the code is raising a Subscript out of Range and Type Mismatch error (respectively), but these are built in VB errors we're raising. We're likely to use these in many places in many modules. Given that, I think it's absolutely worth the time to create a module with a public [VbErrorNumbers] Enum. It's some work, but you only ever have to do it once. In fact, I believe I'm going to do this myself later today. A full list of built in VBA errors can be found here.
Code Snippets
Err.Raise 9, TypeName(Me) & ".TypedName", _
"Can only set typename of an empty list."Private Sub RaiseTypeError(ByVal badItem As Variant, ByVal method As String)
Err.Raise 13, method, "Element is of type " & TypeName(badItem) & _
", not " & TypedName & "."
End SubContext
StackExchange Code Review Q#64004, answer score: 3
Revisions (0)
No revisions yet.