Thursday, May 2, 2013

How to Organize Constant Values

We all know that hard coding is a bad thing in programming. So we create a class’s call AppConstants.  Well, that is better than hardcoding in most of cases, but it is far from good enough. In some case, it is even worse.

First of all, let’s say what should not be treated as constants. Anything that is user (included administrator) changeable, editable or maintainable should not be treated as constant. We must understand that any change of these constant values will lead to code change and build and redeployment before the change take effect.

Now let’s move on to the topic of how to organize constants values.

The first question to ask is: is this group of values representing permissible values of certain fields?  e.g.  CreditCardType (MasterCard, Discover, Visa), Gender (Unknown, Male, Female). If the answer is “Yes” we can group them together as an enum and use enum as data type for these fields. The code example as following:
Public Enum CreditCardType
        None
        MasterCard
        Discover
        Visa
       End Enum

Public Enum Gender
         Unknown = 3
         Male = 1
         Female = 2
       End Enum

Inside your class, you use these enum as data type, for example, inside Customer class you will have Gender property defined as following:
Public Property Gender() As Gender
And inside Payment class, you might have a property indicate the CreditCardType as following:
Public Property CreditCardType () As CreditCardType

And then you can assign values to these properties with the enum values or compare the value of these properties with their respective enum values. the following are some examples:

ShoppingCart.Payment.CreditCardType = CreditCardType.Visa
If Transaction.Customer.Gender = Gender.Male


If the answer to the first question is “No”, we then process to the next question. “If the group of values representing certain attributes of certain entity and meant to be accessed by index. For example, they are position offsets of all fields of certain message, or they are length of each field of certain messages” If the answer to this question is “Yes” we could establish a static dictionary within a static constructor of a class with these group of values as value and the index you want to access them with. The following are some sample code:
Public Shared Sub SetDecalMaxLength()
            DecalMaxLength = New Dictionary(Of VehicleTypes, Integer)
            DecalMaxLength.Add(VehicleTypes.Moped, 5)
            DecalMaxLength.Add(VehicleTypes.Snowmobile, 6)
            DecalMaxLength.Add(VehicleTypes.WaterCraft, 7)
        End Sub

Public Structure Field
    Public Offset As Integer
    Public Length As Integer
    Public Sub New(ByVal offset As Integer, ByVal length As Integer)
        Me.Offset = offset
        Me.Length = length
    End Sub
End Structure

Private Shared Sub SeupUDFileds()
        UD = New Dictionary(Of LegacyCDLISUD, Field)
        UD.Add(LegacyCDLISUD.DriverLicense, New Field(0, 13))
        UD.Add(LegacyCDLISUD.SSN, New Field(25, 9))
        UD.Add(LegacyCDLISUD.FirstName, New Field(34, 30))
        UD.Add(LegacyCDLISUD.FirstNameTruncatedCode, New Field(64, 1))
        UD.Add(LegacyCDLISUD.FirstNameTransliteratedCode, New Field(65, 1))
        UD.Add(LegacyCDLISUD.MiddleName, New Field(66, 70))
        UD.Add(LegacyCDLISUD.MiddleNameTruncatedCode, New Field(136, 1))
        UD.Add(LegacyCDLISUD.MiddleNameTransliteratedCode, New Field(137, 1))
        UD.Add(LegacyCDLISUD.LastName, New Field(138, 70))
        UD.Add(LegacyCDLISUD.LastNameTruncatedCode, New Field(208, 1))
        UD.Add(LegacyCDLISUD.LastNameTransliteratedCode, New Field(209, 1))
        UD.Add(LegacyCDLISUD.DateofBirth, New Field(210, 8))
        UD.Add(LegacyCDLISUD.Gender, New Field(218, 1))
        UD.Add(LegacyCDLISUD.HeightFeet, New Field(219, 1))
        UD.Add(LegacyCDLISUD.HeightInches, New Field(220, 2))
        UD.Add(LegacyCDLISUD.Weight, New Field(222, 3))
        UD.Add(LegacyCDLISUD.EyeColor, New Field(225, 3))
        UD.Add(LegacyCDLISUD.TransactionNumber, New Field(228, 4))
        UD.Add(LegacyCDLISUD.PreviousJuridiction, New Field(232, 2))
        UD.Add(LegacyCDLISUD.PreviousDriverLicense, New Field(234, 25))
        UD.Add(LegacyCDLISUD.PreviousSSN, New Field(259, 9))
        UD.Add(LegacyCDLISUD.PreviouseName, New Field(268, 35))
        UD.Add(LegacyCDLISUD.PreviousDateofBirth, New Field(303, 8))
    End Sub

With the code above, you will get 6 by accessing DecalMaxLength(VehicleTypes.Snowmobile), if a member is defined as VehicleTypes enum type, for example, if we have Type property defined in VehicleBE as following:
Public Property Type () As VehicleTypes
We would get the respective Decal Max length with following code:

DecalMaxLength (MyVehcile.Type)

UD(LegacyCDLISUD.PreviousDateofBirth).Offset will get you 303 and
UD(LegacyCDLISUD.PreviousDateofBirth).Lenght will get you 8
If the above 2 scenario do not match the situation you are handing, suggest to defined a static class with proper name and define constants with the static class. Sometime you may want to put all these type of static class into one umbrella class. The following is an example in our AppConstant class

       Public Class TransactionNumberLength
            Public Const TransactionID = 19
            Public Const NewTransactionID = 13
        End Class

We have come out 3 different ways in treating constants, let me finish this article by addressing the naming of constant,  whither we define them as part of an enum, or define them as constants of a static class, when it comes to naming, we MUST exercise Abstraction.  The following are some naming example which did not exercise abstraction:
Public Const ENTRY_APP_TRANS_NUMBER = "ENTRY_APP_TRANS_NUMBER"Public Const SSN = "SSN"Public Const C As String = "C"Public Const PRINT = "Print"Public Const Number_0 = "0"Public Const EMPTY_STRING As String = ""
Public Enum NumberSeries
            MINUS_ONE = -1
            ZERO = 0
            ONE = 1
            TWO = 2
            THREE = 3
            FOUR = 4
            FIVE = 5
            SIX = 6
            SEVEN = 7
            EIGHT = 8
            NINE = 9
            TEN = 10
            ELEVEN = 11
            TWELVE = 12
            THIRTEEN = 13
            FOURTEEN = 14
            FIFTEEN = 15
            SIXTEEN = 16
….
End Enum
        

An easy way to verify if we have exercised abstraction is to see when we change the value of the variables without changing the name, will it cause confusion. If it does, then we did not exercise abstraction.
The following is an example, in which we did not exercise abstraction well.

<Obsolete("Please use MinimumAgeForRenewal")> Public Const AgeForRenewal = 21

In this case, anyone who knows license renewal policy would know that it is not true that you have to be at age of 21 to be able to renew your driving license rather you have to be age 21 or older in order to do so. Sure enough, this constant will be used to compare with customer’s age when doing renewal. It is kind of dangerous to name the constant after the name of the field they are used to compare with.
The guidelines on constant enum naming are exercise Abstraction and use business term to name them.