Aquí hay un ejemplo de cómo puede abordar esto desde una perspectiva funcional, y cómo ayuda a evitar las posibles dificultades. Estoy trabajando en Haskell, lo que supongo que no sabes, así que lo explicaré en detalle a medida que avance.
data Application = Applied ApplicationDetails |
InReview ApplicationDetails |
Approved ApplicationDetails |
Declined ApplicationDetails
Esto define un tipo de datos que puede estar en uno de los cuatro estados que corresponden a los estados de su aplicación. ApplicationDetails
se supone que es un tipo existente que contiene la información detallada.
newtype UpdatableApplication = UpdatableApplication Application
Un alias de tipo que necesita conversión explícita hacia y desde Application
. Esto significa que si definimos la siguiente función que acepta y desenvuelve UpdatableApplication
y hace algo útil con ella,
updateApplication :: UpdatableApplication -> ApplicationDetails -> Application
updateApplication (UpdatableApplication app) details = ...
entonces tenemos que convertir explícitamente la Aplicación a una Aplicación Actualizable antes de que podamos usarla. Esto se hace usando esta función:
findUpdatableApplication :: Application -> Maybe UpdatableApplication
findUpdatableApplication app@(Applied _) = Just (UpdatableApplication app)
findUpdatableApplication _ = Nothing
Aquí hacemos tres cosas interesantes:
- Verificamos el estado de la aplicación (usando la coincidencia de patrones, que es realmente útil para este tipo de código), y
- si puede actualizarse, lo envolvemos en un
UpdatableApplication
(que solo involucra una nota de compilación de tipo del cambio de tipo que se agrega, ya que Haskell tiene una característica específica para hacer este tipo de truco de nivel de tipo, no cuesta nada en tiempo de ejecución) y
- devolvemos el resultado en un "Quizás" (similar a
Option
en C # o Optional
en Java; es un objeto que envuelve un resultado que podría faltar).
Ahora, para realmente armar esto, necesitamos llamar a esta función y, si el resultado es exitoso, pasarlo a la función de actualización ...
case findUpdatableApplication application of
Just updatableApplication -> do
storeApplicationInDatabase (updateApplication updatableApplication)
showConfirmationPage
Nothing -> do
showErrorPage
Como la updateApplication
función necesita el objeto envuelto, no podemos olvidar comprobar las condiciones previas. Y debido a que la función de verificación de precondición devuelve el objeto envuelto dentro de un Maybe
objeto, tampoco podemos olvidar verificar el resultado y responder en consecuencia si falla.
Ahora ... podrías hacer esto en un lenguaje orientado a objetos. Pero es menos conveniente:
- Ninguno de los lenguajes OO que he probado tiene una sintaxis simple para hacer un tipo de contenedor seguro, por lo que eso es repetitivo.
- También será menos eficiente, porque al menos para la mayoría de los idiomas no podrán eliminar el tipo de envoltura, ya que será necesario que exista y sea detectable en tiempo de ejecución (Haskell no tiene verificación de tipo de tiempo de ejecución, todas las verificaciones de tipo son realizado en tiempo de compilación).
- Si bien algunos lenguajes OO tienen tipos equivalentes a
Maybe
ellos, generalmente no tienen una forma tan conveniente de extraer los datos y elegir la ruta a seguir al mismo tiempo. La coincidencia de patrones también es realmente útil aquí.
this kind of code does not make it obvious such a rule exists
- Por eso el código tiene documentación. Los escritores de buen código seguirán los consejos de Euphoric y proporcionarán un método para que el exterior pruebe la regla antes de probar el hardware.