You are on the Home/Publications & Training/Case Studies/Property procedures in a standard module page
Google
Web This Site

Case Study – Property procedures in a standard module

Those readers familiar with the use of objects in VB(A) know about the three Property procedures (Get, Let, and Set).  The typical use of these procedures is to create a property in the class.  The consumer of the class can then interrogate or assign a value to the property.  What most people may not be aware of is that one can use property procedures in standard modules!  As in the case of a class module, a property behaves like a variable but in a far more complex manner.

The major advantage of a property over a public variable is that one can do a lot more than provide the consumer of the variable unfettered access to the raw value.  For example, suppose one wants to create a variable that holds one of the three primary computer colors – red, green, or blue – and no other color.  In a standard module named ColorManager one could create

Option Explicit

Option Private Module

Public lPrimaryColor As Long   'only legitimate values are R, G, or B

 

The major downside of this approach is that there is no way to enforce that lPrimaryColor contains only of the three legitimate values, RGB(255,0,0), RGB(0,255,0), or RGB(0,0,255).  In another module, say, ColorConsumer, code like that below would work just fine even though it violates the intent of lPrimaryColor.

Sub unfetteredVariableAccess()

    lPrimaryColor = RGB(128, 0, 0)

    MsgBox lPrimaryColor

    End Sub

Implement and use property procedures in a standard module

An alternative – and far more robust – approach would be to change the ColorManager standard module to:

Option Explicit

Option Private Module

Dim lPrimaryColor As Long

Property Get PrimaryColor() As Long

    PrimaryColor = lPrimaryColor

    End Property

Property Let PrimaryColor(uPrimaryColor As Long)

    Select Case uPrimaryColor

    Case Is = RGB(255, 0, 0), RGB(0, 255, 0), RGB(0, 0, 255):

        lPrimaryColor = uPrimaryColor

    Case Else:

        lPrimaryColor = 0

        End Select

    End Property

 

The code above declares a property PrimaryColor with a paired Get and Let procedures.  Effectively, and just as in a class module, the use of the two property procedures creates something that others can use as though it were a single variable!

This impacts consumers, such as ColorConsumer, in two related ways.  The first is because ColorManager is a private module and lPrimaryColor is no longer a public variable.  Consequently, ColorConsumer can no longer directly access the variable.  The code in the unfetteredVariableAccess subroutine above would generate a compile error: Variable not declared.  The second is that PrimaryColor can now be treated as a variable.  Suppose ColorConsumer has the following procedure:

Sub testPropInOtherModule()

    PrimaryColor = RGB(255, 0, 0): MsgBox PrimaryColor

    PrimaryColor = RGB(0, 255, 0): MsgBox PrimaryColor

    PrimaryColor = RGB(0, 0, 255): MsgBox PrimaryColor

    PrimaryColor = RGB(128, 0, 0): MsgBox PrimaryColor

    End Sub

 

The above code is perfectly valid and will execute as expected.  The last assignment (of a non-primary color to the PrimaryColor changes it to black, i.e., RGB(0,0,0).

Since the property behaves like a variable of a particular data type, one can use it just one would use a variable of that data type.  Since the PrimaryColor property is defined as a long (see the Get procedure), one can use it as though it were a simple variable of type Long as in the example below.  The rotatePrimaryColor subroutine rotates the PrimaryColor property through its three legitimate values first with a complex IF statement and then nested IIf functions.  In each instance, an uninitialized property becomes red.

Sub rotatePrimaryColor()

    If PrimaryColor = RGB(255, 0, 0) Then

        PrimaryColor = RGB(0, 255, 0)

    ElseIf PrimaryColor = RGB(0, 255, 0) Then

        PrimaryColor = RGB(0, 0, 255)

    Else

        PrimaryColor = RGB(255, 0, 0)

        End If

    MsgBox PrimaryColor

    PrimaryColor = IIf( _

        PrimaryColor = RGB(255, 0, 0), RGB(0, 255, 0), _

        IIf(PrimaryColor = RGB(0, 255, 0), RGB(0, 0, 255), _

        RGB(255, 0, 0)))

    MsgBox PrimaryColor

    End Sub

 

The use of the property PrimaryColor can – and should – be extended to the ColorManager module itself.  The developer of the ColorManager module should exhibit the same discipline that the developer of a class module must exhibit and resist the temptation to directly access variables associated with properties.  One can easily make the rotatePrimaryColor subroutine above a public sub in the ColorManager module (essentially the equivalent of a method of a class).  Of course, now that the subroutine is in the same module as the lPrimaryColor variable, it would have direct access to the variable.  However, basic defensive programming requires that the developer limit herself to the PrimaryColor property.  Put the code below in the ColorManager module

Public Sub rotatePrimaryColor()

    PrimaryColor = IIf( _

        PrimaryColor = RGB(255, 0, 0), RGB(0, 255, 0), _

        IIf(PrimaryColor = RGB(0, 255, 0), RGB(0, 0, 255), _

        RGB(255, 0, 0)))

    End Sub

Now, in the consumer module, ColorConsumer, one could have code like:

Sub testRotateColors()

    rotatePrimaryColor

    MsgBox PrimaryColor

 

    rotatePrimaryColor

    MsgBox PrimaryColor

 

    rotatePrimaryColor

    MsgBox PrimaryColor

 

    rotatePrimaryColor

    MsgBox PrimaryColor

    End Sub

 

Summary

This tip demonstrated that the use of a property is not restricted to a class module.  The same developer advantages enjoyed by the use of a property in a class module also apply to its use in a standard module.