Buscar en este blog....

viernes, 29 de mayo de 2009

¿Validar las precondiciones?

La idea de escribir este postsurgió de una charla que esuché ayer entre un compañero de la meteria "Diseño de Sistemas" y la profesora. Él decía que las precondiciones no se debían evaluar dentro del caso de uso -o sea que no debería ser parte del flujo de ese caso de uso el validar que dichas precondiciones se cumplirian- y por el contrario, son condiciones que ya deben haberse cumplido antes de disparar ese caso de uso. La profesora, en el otro extremo le decía que sí debían validarse dentro del caso de uso.

En principio estuve de acuerdo con mi compañero, porque pensé que esa es justamente la definción de "precondición" (de hecho, la misma palabra lo dice).

Además, la definición formal de precondición es: "una operación que debe ser cierta cuando se invoca una operación". En el Manual de Referencia de UML se agrega, despues de esta definición, que el receptor no debe tener que verificar la condición.

Aunque con estos argumentos parece tener la última palabra mi compañero, la profesora tenía algo más que razón. Quizás su punto de vista no encaja exactamente con el del paradigma de orientación a objetos, o mejor dicho, con las definiciones que se proponen en UML. ¿Entonces está mal su punto de vista?

No. Si recordamos los principios básicos de la orientación a objetos, uno de ellos se refería a la capacidad de reutlizar componentes. Componentes que son de un sistema, pero sirven para otro, pueden ser reutilizados para evitar volver a construirlos. Un caso de uso, por ejemplo, puede ser reutilizado si dos sistemas tienen algun requisito en común. Este caso de uso tendrá sus propias precondiciones que el peimer sistema (llamémosle sistema A) se encarga de "poner a punto" antes de llamarlo. Con "poner a punto" me refiero a que el sistema sabe que caso de uso se llamará -previo al caso de uso que vamos compartir- de forma que el sistema queda en un estado tal que el caso de uso que vamos a compartir tiene "verificadas" esas precondiciones.
¿Pero quien me asegura que esto sea realmente asi? o peor aún, ¿quien me asegura que el nuevo sistema (sistema B) también se encargue de llamar al caso de uso compartido con las precondiciones validadas? Nadie.

Entonces, quizás sea esto lo que la profesora quería decir. Si el mismo caso de uso verificara sus precondiciones, entonces no hay problema de reutilizarlo, ya que el mismo se asegura -donde quiera que lo pongan- que las condiciones se validen. De no validarse, sencillamente no se ejecutará. Esto evita que el sistema llegue a estados no válidos, o de resultados incorrectos.

Entonces, ¿para qué están las precondiciones? Bueno, es un dato más sobre la interfaz del caso de uso. Si conozco las precondiciones, no necesito saber que hace un caso de uso -qué valida y que no- sino simplemente usarlo. Es decir, la precondición como tal es un aspecto más bien ligado al análisis y al diseño, mientras que la verificacion de ésta está más ligada a la implementación.
También ayudan a percibir los flujos posibles entre casos de uso: un caso de uso solo podrá ejecutarse si anteriormente, otros casos de uso han dejado el sistema en un estado tal que éste pueda ejecutarse.

4 comentarios:

  1. Estoy mas de acuerdo con tu compañero que con la profesora. Las precondiciones, en términos generales, deben chequearse por fuera del caso de uso. Como lo dice su propia definición, llamás al caso de uso cuando están las condiciones dadas => no es necesario verificarlas en el caso.
    Porque ese mismo caso de uso, reutilizado, puede cambiar sus precondiciones dependiendo del contexto en que se de el mismo. Ergo verificar el cumplimiento de las precondiciones fuera del caso da mayor desacoplamiento e independencia entre objetos .
    Algo aconsejable y sugerido en las técnicas denomidadas GRASP
    http://es.wikipedia.org/wiki/Grasp

    Creo que esas verificaciones deben hacerse en otra capa de la arquitectura del sistema, y por lo tanto delegada o otro tipo de objetos. De ese modo las funciones de cada clase están mas delimitadas, y por lo tanto se apegaría más al paradigma de la OOP.

    Imaginá que el caso de uso sea un préstamo en una biblioteca, donde hay diferentes tipos de socios con sus nros de id respectivos. No solo dentro de la misma biblioteca, sino entre 2 diferentes, con diferentes tipos de socios también.
    Al momento ingreso de su id, el rango del nro puede variar dependiendo de quien lo utiliza, si proviene del PAMI :p, de una universidad, o un club, etc. Por lo tanto si hacés la validación adentro, no quedaría tan reutilizable porque habría que modificarlo segun sus necesidades, cada vez que aparce un nuevo socio. (Esa validación podría hacerse a nivel de la capa de presentación, a grosso modo).

    Si hay una clase en otra capa que se encarga de eso, quedaría mejor.
    Que es como funciona Amazon, vos ingresás tu nro, lo valida, el caso de uso Compra lo hace una vez que llenás el carrito, y luego de la validación.

    Igual en esto, no hay que ser tan fundamentalista, siempre hay excepciones válidas, pero eso son, excepciones.

    Saludos, ro

    ResponderEliminar
  2. Ro, lo que decis es válido, aunque disciento en algunos puntos. Si bien mi experiencia en la práctica no es muy amplia que digamos, donde decis:

    "llamás al caso de uso cuando están las condiciones dadas => no es necesario verificarlas en el caso"

    Esto no es necesariamente así. Si vos implementas un CU y otra persona quiere usarlo, esta persona queda en total libertad de verificar o no las precondiciones para disparar el CU. Si no las verifica pero se cumplen, entonces no hay problema. Pero si las verifica y no se cumplen, probablemente se lleve al sistema a un estado incorrecto.

    Por otro lado, me parece que tampoco proporciona mayor desacoplamiento, sino que al contrario, lo disminuye porque se debe cargar a otra parte del sistema con la verificacion de un caso de uso (que perfectamente podría ser mejor intercambiar por otro) y así se crea una dependencia entre ambos componentes.

    Para el ejemplo que planteas, podría sugerir otra forma de resolver el inconveniente (por ejemplo, hablando de un rango como algo "generico" y que dependa de cual tipo de socio sea).

    Pero, si estoy de acuerdo con vos que puede ser menos flexible realizarlo de esta forma. Como dijiste al final, no hay que ser tan fundamentalista.
    La verdad, me pareción una curiosidad linda y además genera muchas dudas cuando uno empieza con estas cosas y no tiene experiencia en esto. Salutes! :)

    ResponderEliminar
  3. Tal vez no te entendí, opino al reves, el desacoplamiento existe en cuanto la clase que resuelve el caso de uso (CU), se desliga de hacer las validaciones. Hay *otra* clase, *independiente* de la del CU que lo hace, ahí necesariamente entra independencia, ergo desacoplamiento.

    Cuando tenés un CU tenés que saber en cuáles condiciones corre o no, es lo dice su contrato. O bien cuando comprás un componente de software (o de cualquier cosa), también tiene una serie de restricciones de uso.
    Si conecto algo que da más de 220V me embromo, y si pongo un regulador de voltaje, no forma parte del componente que compré, está por fuera de éste :p, (no es la misión de mi pc, si este fuera el componente, fijarse si hay una descarga mayor o no de voltaje).
    Es obvio, si violás las pre, el sistema 'cae', caso contrario no, pero eso no refuerza la idea de validarlas dentro del CU. Sino mas bien, lo contrario, NO ejecutar el CU en caso que estas no se cumplan, y la mejor forma de hacerlo, me thinks, es valirar *antes*.

    Lo ideal es no meterle impurezas al CU y desdibujarle su fin, y éste no es el de validar.
    Por eso lo que decís de ampliar el rango en lo personal no me convence, puede volverse enorme y engorroso de mantener.
    En el mejor de los casos, prefiero crear CU alternativos, ergo, otra vez hay más desacoplamiento (diferentes clases en términos de OOP)o Divide & Conquer como técnica de diseño.
    Prefiero desacoplar, resolver la validación en la capa de presentación, por así decir, y no en el CU propiamente dicho, quien ya entraría dentro de las reglas de negocios.

    ResponderEliminar
  4. Algunas puntualizaciones:
    1- "llamás al caso de uso cuando están las condiciones dadas => no es necesario verificarlas en el caso"
    No quise decir que no deban verificarse las pre condiciones, *al contrario*, DEBE hacerse, digo que es mas aconsejable por todo lo expuesto hacerlo por fuera del CU.

    2- Por desacoplamiento según el criterio GRASP es, a grosso modo, que haya clases con responsabilidades bien definidas => por c/responsabilidad hay una clase diferente, son independientes entre sí.
    No están acopladas y facilitan las modificaciones y el mantenimiento.

    3- Menor desacoplamiento (o mayor acoplamiento) no es cargar otra parte del sistema como creí entenderte, sino que algo que debe hacerse si o sí (o sea, no estás agregando/acoplando nada), se haga en el lugar menos apropiado y que interfiera con otras partes/objetos del sistema (en este caso verificar las pre en la clase que resuelve el CU).

    Desacoplar: diferentes clases para diferentes responsabilidades, afecta menos el mantenimiento/modif del sistema, por ej agregar nuevas claves.
    No toco el CU, sino su validación, hay una clase para ello.

    Podemos estar horas discutiendo esto, y si bien, puede haber excepciones a la hora de hacerlo, considero que es bueno tener una técnica teórica que respalde las buenas prácticas porque tienen su razón de ser.
    De hecho la mayoría de los grandes sistemas funcionan así, validando antes, o si querés, fuera del CU.
    Reitero ejemplos conocidos, Amazon, eBay, etc.

    ResponderEliminar

Comments are subject to moderation, only in order to avoid insults and disguising things.

Los comentarios están sujetos a moderación, solo con el fin de evitar insultos y cosas por el estilo.