Evitar números mágicos

Cuando manipulas la siguiente tabla en VBA, ¿qué harías?

NúmeroNombreDirecciónTeléfonoProductoOpción
1SatouHokkaido090-0000-0000JugueteOpción 1
2TanakaAomori090-1111-1111DulceOpción 2
3HondaAkita090-2222-2222JuegoOpción 3
4OkamotoNiigata090-3333-3333DeporteOpción 4
5YamadaYamaguchi090-4444-4444ZapatoOpción 5

En programación, los números mágicos deben evitarse en principio.

En otros lenguajes de programación, con un poco de aprendizaje, es casi seguro que no aparecerán números mágicos, pero en VBA hay muchos casos en los que es difícil evitarlos.

Personalmente, creo que las razones principales son las siguientes:

  • La mayoría de los datos que se manejan son datos en hojas o archivos CSV.
  • La gestión de la memoria es compleja y, a menudo, la cantidad de memoria utilizada y la legibilidad son inversamente proporcionales.
  • El comportamiento es muy diferente al de los lenguajes de programación actuales (problema de competencia).

Debido a la naturaleza de los datos que se manejan, los números mágicos surgen naturalmente si se programa sin pensar. En esta ocasión, me gustaría presentar los métodos que uso con frecuencia junto con el código utilizando la tabla anterior como ejemplo.

Métodos

Recomendación personal: Collection y Dictionary

El método que personalmente recomiendo más es el patrón de matriz asociativa que combina los objetos Collection y Dictionary. Creo que es un método familiar para aquellos que han aprendido lenguajes que manejan archivos JSON, como JavaScript.

________________________________________________
'
' Convierte el rango objetivo en una matriz asociativa
'
' @param r Objeto Range a convertir
' @return Matriz asociativa
'
'________________________________________________
Public Function GetCollection(ByRef r As Range) As Collection

    Dim items As Collection, item As Object
    Set items = New Collection

    Dim row As Long, col As Long

    For row = 2 To r.Rows.Count

        Set item = CreateObject("Scripting.Dictionary")

        For col = 1 To r.Columns.Count

            item.Add r(1, col).Value, r(row, col).Value

        Next col

        items.Add item
    Next row

    Set GetCollection = items
End Function

Puede parecer redundante reemplazar un objeto Range, que se puede tratar como una matriz bidimensional, con una matriz asociativa, pero en términos de legibilidad, este método es mi favorito.

Después de la conversión, el objeto Range se convierte en un estado similar a un archivo JSON, como se muestra a continuación.

{
    "1": {
        "Número": "1",
        "Nombre": "Satou",
        "Dirección": "Hokkaido",
        "Teléfono": "090-0000-0000",
        "Producto": "Juguete",
        "Opción": "Opción 1"
    },
    "2": {
        "Número": "2",
        "Nombre": "Tanaka",
        "Dirección": "Aomori",
        "Teléfono": "090-1111-1111",
        "Producto": "Dulce",
        "Opción": "Opción 2"
    },
    "3": {...},
    "4": {...},
    "5": {...}
}

Para extraer datos, se hace de la siguiente manera.

Dim items as Collection

Set items = GetCollection(Range)

items(1)("Nombre") ' → "Satou"

Usando un iterador (for each), se vuelve aún más claro.

Dim items as Collection, item as Object

Set items = GetCollection(Range)

For Each item in items
    item("Nombre") ' → "Satou", "Tanaka"...
next

Este método tiene algunas desventajas, la primera es la velocidad. Simplemente porque los datos se recorren dos veces (creación de la matriz y procesamiento real), la velocidad de procesamiento disminuye. Otra es que no debe haber valores duplicados en la fila del encabezado. Si hay duplicados… agregar condiciones hace que el código sea más difícil de seguir, por lo que es mejor usar otro método.

La mayoría usa Enum

El método más común para evitar números mágicos es usar Enum. Es más fácil de entender para los principiantes que el método que usa Collection y Dictionary, y es el mejor en términos de mantenibilidad.

Enum TableColumn
    Número = 1
    Nombre = 2
    Dirección = 3
    Teléfono = 4
    Producto = 5
    Opción = 6
End Enum

Dado que solo se están definiendo grupos de constantes, la implementación es posible simplemente reemplazando los números con Enum. También es fácil eliminar los números mágicos de un programa ya completado.

Range(1, TableColumn.Nombre).value ' → "Satou"

Para recorrer en bucle, se hace de la siguiente manera.

Dim i as Long
For i = 1 to Range.Rows.Count
    Range(i, TableColumn.Nombre).value ' → "Satou", "Tanaka"...
Next

No creo que haya desventajas en este método. Si tienes dudas, usa este método.

El más simple: constantes

Dudé en presentarlo porque creo que todos los que están leyendo este artículo ya lo saben, pero el método más rápido en términos de velocidad de procesamiento es definir cada columna del encabezado una por una.

Private Const TABLE_COLUMN_Número As Long = 1
Private Const TABLE_COLUMN_Nombre As Long = 2
Private Const TABLE_COLUMN_Dirección As Long = 3
Private Const TABLE_COLUMN_Teléfono As Long = 4
Private Const TABLE_COLUMN_Producto As Long = 5
Private Const TABLE_COLUMN_Opción As Long = 6

Dado que se especifican activamente el alcance y el tipo, la velocidad es la más rápida. Sin embargo, creo que la parte más pesada es el procesamiento en bucle, por lo que no hay mucha diferencia con Enum. A menos que haya reglas de codificación específicas, Enum está bien.

#VBA