Tema anterior: Caso práctico 15: Métricas en clústerTema siguiente: Caso práctico 17: Funcionalidad incorporada


Caso práctico 16: Patrones de diseño de lógica de negocios

Hay varios "patrones de diseño" que se pueden utilizar en muchas lógicas de negocios. Esos patrones de diseño se prueban y, si se usan cuando corresponde, pueden ahorrar mucho tiempo, además de, en la mayor parte de los casos, crear una lógica de negocios más eficaz. Este caso práctico se centra en un patrón de diseño.

Patrón de diseño de contadores de actualización

Este patrón de diseño es útil en casi todas las lógicas de negocios, que están pensadas para medir el tiempo entre determinados eventos. Los ejemplos de dichas lógicas de negocios son las lógicas de negocios para medir la disponibilidad, el tiempo de inactividad, el tiempo medio entre errores, el tiempo medio de restauración, el tiempo medio de respuesta, el tiempo medio de resolución, el porcentaje de componentes con una disponibilidad menor que X, el número de casos no resueltos puntualmente, etc.

El elemento común de todas esas lógicas de negocios es que su resultado depende de la marca de tiempo de los diversos eventos que reciben.

Una regla general para decidir si su lógica de negocios se puede beneficiar de este patrón de diseño sería: si las lógicas de diseño dependen de la marca de tiempo de los diversos eventos que la reciben, lo más probable es que deba utilizar este patrón de diseño.

Esqueleto de este patrón de diseño

El código de la lógica de negocios que utiliza este patrón se puede dividir en dos partes: un marco y una implementación. El marco contiene el código que en la mayoría de los caso es fijo y no cambia para las diversas lógicas de negocios. Esta parte es la misma para el cálculo de disponibilidad y el número de tickets no resueltos a tiempo. La implementación contiene código que es específico de cada lógica de negocios.

Se recomienda poner estas dos partes del código en módulos de lógica de negocios separados y reutilizar el módulo del marco en una métrica diferente.

Este es el código del marco:

Dim g_PrevEventTimestamp

Sub OnLoad(time)
     g_PrevEventTimestamp = time
     InitializeStatusVariables
End Sub

Sub OnRegistration(Dispatcher)
     ' Si hay una copia separada de las variables de estado 
     ' para cada recurso registrado depende del registro
     ' recursos para los que debería fijar valores iniciales
     ' variables de estado de los recursos recientemente agregados aquí
End Sub

Sub OnPeriodStart(time)
     InitializeCounters
End Sub

Sub OnPeriodEnd(time, completePeriod)
     HandleEvent (time)
End Sub

Sub OnTimeslotEnter(time)
     HandleEvent (time)
End Sub

Sub OnTimeslotExit(time)
     HandleEvent (time)
End Sub


Function Result()
     Result = CalculateResult()
End Function

Sub HandleEvent(Time)
     Dim diff
     diff = TimeDiff( "s",Time,g_PrevEventTimestamp)
     UpdateCounters(diff)
     g_PrevEventTimestamp = Time
End Sub

A continuación, se presenta un esqueleto del módulo de implementación:

' Definir las variables de estado aquí. Puede ser una 
' variable global simple o varias variables globales complejas
' dependiendo de la lógica de negocios
Dim g_StatusVar_1, g_StatusVar_2, ... ,g_StatusVar_n

' Definir los contadores aquí. 
' Puede ser una variable global simple o varias
' variables globales complejas dependiendo de la lógica de negocios
Dim g_Counter_1, g_Counter_2, ... , g_Counter_n

Sub InitializeStatusVariables ()
     ' Establecer los valores iniciales a las diversas variables de estado
End Sub

Sub InitializeCounters ()
     ' Establecer los valores iniciales a los diversos contadores
     g_Counter_1 = 0
     g_Counter_2 = 0
     '…
     g_Counter_n = 0
End Sub

Function CalculateResult ()
     ' Calcular el resultado. El resultado debería depender
     ' del valor de los contadores. No debería depender
     ' del valor de las variables de estado. No se debería 
     ' cambiar los valores de los contadores o 
     ' las variables de estado
End Function

Sub UpdateStatus(method, eventDetails)
     ' Actualizar el valor de las variables de estado basándose en
     ' el parámetros (y posiblemente en el valor anterior de 
     ' las variables de estado)
End Sub

Sub UpdateCounters(diff)
     ' Actualizar los valores de los contadores basándose en su
     ' anterior valor, en el valor de las variables de estado
     ' y en el valor del parámetro diff.
     ' En muchos casos este cálculo está basado también en
     ' el valor de Context.IsWithinTimeslot
End Sub

Sub OnEvent_1(eventDetails)
     HandleEvent (eventDetails.Time)
     UpdateStatus(“OnEvent_1”,eventDetails)
End Sub

Sub OnEvent_2(eventDetails)
     HandleEvent (eventDetails.Time)
     UpdateStatus(“OnEvent_2”,eventDetails)
End Sub

'...

Sub OnEvent_n(eventDetails)
     HandleEvent (eventDetails.Time)
     UpdateStatus(“OnEvent_n”,eventDetails)
End Sub

Para explicar mejor este patrón de diseño, a continuación se presenta un ejemplo de la implementación de este patrón para calcular la disponibilidad. Este ejemplo supone que hay eventos de actividad e inactividad que se manejan por dos controladores de eventos separados en la lógica de negocios. La disponibilidad se define como el porcentaje de tiempo durante el cual el sistema está activo a partir del tiempo total en la ranura de tiempo. Se supone que el estado del sistema es el estado del último evento recibido (activo o inactivo).

Aquí está el código de la implementación (el código del marco no se cambia):

' Estado variable
Dim g_Status
' Contadores.
Dim g_UpTime, g_TotalTime

Sub InitializeStatusVariables ()
     G_Status = “UP”
End Sub

Sub InitializeCounters ()
     g_UpTime = 0
     g_TotalTime = 0
End Sub

Function CalculateResult ()
     If g_TotalTime = 0 Then
          CalculateResult = Null
     Else
          CalculateResult = g_UpTime/g_TotalTime*100
     End If
End Function

Sub UpdateStatus(method, eventDetails)
     If method = “OnUP” Then
          G_Status = “UP”
     Else
          G_Status = “DOWN”
     End If
End Sub

Sub UpdateCounters(diff)
     If Context.IsWithinTimeslot Then
          G_TotalTime = g_TotalTime + diff
          If g_Status = “UP” Then
               G_UpTime = g_UpTime + diff
          End If
     End If
End Sub

Sub OnUp(eventDetails)
     HandleEvent (eventDetails.Time)
     UpdateStatus(“OnUp”,eventDetails)
End Sub

Sub OnDown(eventDetails)
     HandleEvent (eventDetails.Time)
     UpdateStatus(“OnDown”,eventDetails)
End Sub

Hay varias variaciones de este patrón. Una de las variaciones más comunes es cundo un contador de tiempo separado se debería mantener para entidades diferentes. Por ejemplo, cuando se mide el tiempo de solución, debería mantenerse un contador separado para cada ticket abierto. En este caso cuando se maneja un evento que es relevante solamente para un ticket es más eficaz actualizar solamente el contador de ese ticket. Cuando un evento común se maneja (como OnPeriodEnd u OnTimeslotEnter) los contadores de todos los tickets se deberían actualizar.

Nota: Esta variación del patrón requiere mantener una copia separada de la variable global g_PrevEventTimestamp para cada ticket.

Algunos buenos ejemplos del uso de este patrón se pueden ver en nuestro contenido predeterminado. Tenga en cuenta que este patrón se utiliza de forma un poco diferente en el contenido predeterminado y que la separación entre el marco y la implementación no es tan obvia allí.