You are on page 1of 84

Fundamentos de Programación

Construyendo Mejor Software

Por Karl Seguin

WWW.CODEBETTER.COM

Fundamentos de Programación Página intencionalmente dejada en blanco

2

Fundamentos de Programación

Copyright © Karl Seguin

www.codebetter.com

Fundamentos de Programación

3

Licencia
El libro Fundamentos de Programación está licenciado bajo la licenciaAttribution-NonCommercial-ShareAlike 3.0 Unported. Básicamente usted es libre de copiar, distribuir, y mostrar el libro.Sin embargo, pido que siempre se me atribuya el libro a mí, Karl Seguin, no lo use para fines comerciales y comparta cualquier alteración que haga bajo la misma licencia. Usted puede ver el texto complete de la licencia en: http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode

Aplicación de aprendizaje descargable
Leer acerca de código es una buena manera de aprender, pero si es como yo, nada como una aplicación real. Es por esto que he creado la CanvasLearningApplication un simple (aunque completo) sitio web ASP.NET MVC que contiene muchas de las ideas y herramientas cubiertas en este libro.La aplicación es una solución de Visual Studio 2008 con documentación en línea útil para cubrir la brecha entre teoría y práctica. La CanvasApplication y este libro son independientes, así que puede hacer el acercamiento en esta ruta de aprendizaje como sea que quiera. http://codebetter.com/blogs/karlseguin/archive/2009/05/25/revisiting-codebetter-canvas.aspx

Reconocimientos
Hay incontablespersonas que merecen las gracias. Este libro es una pequeña contribución al incalculable tiempo donado y conocimiento compartido por la comunidad de software en general. Sin la calidad de los libros, foros, grupos de usuarios, blogs, librerías y proyectos open source, estaría todavía tratando de hacer que mi script ASP terminara en tiempo mientras se ciclaba en un recordset (un estúpido MoveNext). No es sorpresa que la comunidad de software ha aprovechado la aperture en internet más que otra profesión para avanzar en nuestra causa. Lo que es sorprendente es como el fenómeno parece haberse ido sin notarse. ¡Bien! Claro, hay una persona especial sin la cual esto no hubiera ocurrido. AWendy, La gente me llama suertudo porestar con alguien tan bonita e inteligente como tú. No saben ni la mitad de ello. Tú no solo eres bonita e inteligente, pero me dejas estar demasiado tiempo en mi computadora, ya sea trabajando, aprendiendo, escribiendo o jugando. Además eres por demás contenta de leer sobre mis cosas o escucharme hablar de cosas sin sentido. No te tengo el aprecio suficiente como debería.

Fundamentos de Programación

Copyright © Karl Seguin

www.codebetter.com

Fundamentos de Programación

4

Tabla of Contenido
Acerca del autor ...................................................................................................................................... 6 ALT.NET ................................................................................................................................................... 7 Objetivos ............................................................................................................................................. 8 Simplicidad .......................................................................................................................................... 8 YAGNI (You aren t going to Need It No vas a necesitarlo) .................................................................. 8 Ultimo momento de responsabilidad ................................................................................................... 9 DRY ...................................................................................................................................................... 9 Explicitud y cohesión............................................................................................................................ 9 Acoplamiento ...................................................................................................................................... 9 Pruebas unitarias e integración continúa ........................................................................................... 10 En este capitulo ................................................................................................................................. 10 . ............................................................................................................................................................. 10 Diseño dirigido por dominios DDD ......................................................................................................... 11 Diseño dirigido por Dominios/Datos................................................................................................... 11 Usuarios, Clientes e Inversionistas ..................................................................................................... 12 El objeto de dominio .......................................................................................................................... 13 Interfaz de Usuario (IU) ...................................................................................................................... 16 Trucos y pistas ................................................................................................................................... 17 Patrones de fábrica ........................................................................................................................ 17 Modificadores de acceso................................................................................................................ 17 Interfaces....................................................................................................................................... 18 Ocultar información y Encapsular................................................................................................... 19 En este capítulo ................................................................................................................................. 19 . ............................................................................................................................................................. 20 Persistencia ........................................................................................................................................... 21 La brecha ........................................................................................................................................... 21 DataMapper ...................................................................................................................................... 21 Tenemos un problema ................................................................................................................... 24 Limitaciones................................................................................................................................... 26 En este capítulo ................................................................................................................................. 27 . ............................................................................................................................................................. 27 Inyección de dependencias. ................................................................................................................... 28 No evites el acoplamiento como si fuera una plaga ............................................................................ 30 Inyección de dependencias ................................................................................................................ 31 Constructor de inyección ............................................................................................................... 31 Marcos de referencia ..................................................................................................................... 33 Una última mejora ......................................................................................................................... 34 En este capítulo ................................................................................................................................. 35 . ............................................................................................................................................................. 35 Pruebas de Unidad ................................................................................................................................ 36

Fundamentos de Programación

Copyright © Karl Seguin

www.codebetter.com

....................................................... 66 Asignar a null ............................................................................................................ 49 Configuración .................................................. 67 Finalización Determinística ................................................................... 71 Lanzar Excepciones ...... 62 Fugas de Memoria Administradas ................. 38 ¿Qué es una prueba de unidad? ....................... 67 En este capítulo ........................................................................................................................... 77 Fundamentos de Programación Copyright © Karl Seguin www................................................................................................................................................................................................ 57 El Heap ............... 69 Manejando Excepciones ..................Fundamentos de Programación 5 ¿Por qué no hacia pruebas de unidad hace 3 años? ..................... 40 Mocking .............................................................................. ................................................................................................................................................................................................................................................................................. 56 En este capítulo ...................................................................................................................................................................................................... 65 Fijamiento................................................................... 70 Limpieza ................................................................................................................................................................................................................................................................................................. 73 Creando Excepciones Personalizadas .................................................................... 57 El Stack ........................................................................................................................................................................................ 55 Descarga ............................................................................................................................. 46 El debate del infame SQL en línea vs............................................................................................................................................................................................................................................... 74 En este Capítulo ............. 44 Pruebas de la interfaz de usuario y la base de datos .......................... 58 Apuntadores ................................................................ 72 Cuándo Lanzar Excepciones .................................................................................................................................................................................................................................................................... 36 Las Herramientas ......................................................................................................................................................................................................... 56 De regreso a las bases: Memoria ............................................................. 38 nUnit ............................................................................................................................................................... 65 Fragmentación ............................................................................................................................................... 45 ............................................................................................................................................................................................................................................................... 54 Carga diferida ................com .................................................................................................... 68 De regreso a las bases: Excepciones ................. 69 Registros ........................................................................................................ 50 Relaciones ........................ ...................................................................................................................................................................................... 72 Mecanismo para generarlas ...................... 45 Object Relational Mappers ...........................................................................codebetter.......................................................................................................................................................................................... Los procedimientos almacenados ..................................... 67 ................................................................................................................................................................ 61 Boxing......... 59 Modelo de Memoria en la Práctica.................................................................................................................... 44 En este capítulo .................................................................................................. 40 Más de nUnit y RhinoMocks ........................................................................................................................................... 57 Asignación de Memoria .............................................................................................. 52 Consultas .......................................................................................................................... 46 NHibernate ............................................................................................................................................................... 61 ByRef ...................................................................................

..........................................................................com ........................ además del de numerosos profesionales distinguido........................ Ontario Canada.......................................................codebetter..........................................openmymind........com/ Fundamentos de Programación Copyright © Karl Seguin www................ ...... 80 En éste capítulo ......................................................................... 82 ........Fundamentos de Programación 6 De regreso a las bases: Proxy Esto y Proxy Aquello.codebetter................................. 79 Interception ......................................................................................... un miembro de la comunidad influyenteCodeBetter................... un antiguo Microsoft MVP.............. 78 Patrón del Dominio del Proxy............................ 83 Acerca del autor Karl Seguines un desarrollador en EpocalCorporation... está localizado en: http://www..............................Ha escrito numerosos artículos y es un miembro active de varios grupos de usuario de Microsoft públicos..................................... Su página personal es: http://www. Vive en Ottawa.........................................com y un editor para DotNetSlackers........................................................................................................ 82 Resumiendo .............................net/ Su blog......

MonoRail (ActiveRecord) así como DataSets y DataTables. los dos no son realmente comparables. Fundamentos de Programación Copyright © Karl Seguin www.NET ha puesto mucho énfasis en aprender detalles de API s.NET se centra en tópicos más abstractos mientras provee una implementación más específica.com . para mí. Como ejemplo concreto y relevante. ALT. HUBERT H HUMPHREY 1 ace algunos años fui afortunado al dar un giro en mi carrera de programación. la recompensa vale la pena. H En realidad. SI HAY INCONFORMIDAD. DÉJENLO QUE SE HAGA GRANDE.NET no es acerca de ALTernativas al estilo MSDN. aún tengo mucho que aprender y en cinco años más veré el código que escribí el día de hoy y me sentiré avergonzado. (Después de todo. BIEN. Como Jeremy Miller lo dice: La comunidad . ENTONCES DEJEN QUE HAYA IDEAS. Yo solía estar seguro de mis habilidades en programación pero solo una vez que acepte que sabía muy poco. Por supuesto. La curva de aprendizaje es considerable y recursos útiles apenas empiezan a surgir (esta es la razón por la que decidí iniciar estas series). El estilo MSDN libremente define una forma específica de construir un sistema directo a cada llamado individual de un método.Capítulo 1 ALT.codebetter. y probablemente así era. Fundamentos de Programación es una colección de publicaciones orientadas en ayudar a programadores entusiastas a que se ayuden ellos mismos.NET 7 ALT. Siempre he visto dos fuerzas dominantes en el mundo de .NET requiere un gran compromiso así como un conocimiento más amplio. el estilo MSDN favorece fuertemente el uso de DataSets y DataTables para toda la comunicación con bases de datos. A lo largo de la serie veremos un número de temas discutidos profundamente que serán muy útiles para cualquiera a excepción de los que ya los conozcan. En un periodo de pocos meses mis habilidades en programación crecieron exponencialmente y a través de los últimos años he continuado refinando mi arte. marcos de referencia y no suficiente énfasis en diseñar y fundamentos de codificación. ALT. ESTOY SATISFECHO. En otras palabras. a pesar de lo que mucha gente piensa. Sin embargo.NET sin embargo. SI HAY ALBOROTO. desajuste en impedancia de objetos relacionales así como implementaciones específicas tales como NHibernate (Mapeo O/R). MUCHO MEJOR.Net). pensando en la descripción anterior es claro pensar que usar la ruta ALT. PENSAMIENTO CRÍTICO Y TRABAJO ARDUO. ¿no es solamente la documentación de referencia de API la única razón por la que visitamos MSDN?). Sin duda. sino que una creencia de que los desarrolladores deben saber y entender soluciones y aproximaciones alternativas de la cual el estilo MSDN forma parte. empecé a entender. centra su discusión en patrones de diseño persistentes. SI EL HOMBRE SE SIENTE PEQUEÑO. Mientras que ALT. una fuertemente conducida por Microsoft con una progresión natural de VB6 y ASP clásico (comúnmente referido como el estilo MSDN) y la otra fuertemente conducida por prácticas fundamentales de orientación a objetos e influenciados por los mejores proyectos/conceptos de Java (conocidos como ALT.NET. mi éxito profesional me ha llevado una felicidad personal mayor. Mi serie. La oportunidad de un mentor solido se presentó por sí sola y la aproveche al máximo.NET SIHAY UNA INCONFORMIDAD CON EL STATUS QUO.

pueda fácilmente leer tu código. un sistema debe de ser pre construido para acomodar cualquier solicitud de cambio posible. Por ejemplo. es necesario ser muy cuidadoso y con el tiempo las cosas empiezan a ser más naturales. una solución de fácil mantenimiento no solo reduce tus costos. pero hay una buena razón por la que hablamos de la capacidad de mantenimiento tan seguido es la llave para ser un gran desarrollador de software. YAGNI (You aren t going to Need It No vas a necesitarlo) No vas a necesitarlo es una creencia de programación extrema que dice que no deberías construir algo ahora porque crees que lo vas a necesitar en un futuro. tomando la aplicación de alguien más o incluso tratando de arreglar algo que escribiste unos meses atrás.codebetter. cada decisión de programación que hago está en gran parte basada en la capacidad de mantenimiento. hay una buena oportunidad de que ya te hayas formado opiniones acerca de lo que es o no es una solución de fácil mantenimiento basado en tu experiencia trabajando con otros. Primero.com . los estudios y experiencia de primera mano nos dicen que los sistemas gastan una considerable cantidad de tiempo (Más del 50%) en mantenimiento. sino también incrementa el número y la calidad de las características que vas a ser capaz de entregar. Como puedes imaginarte. para que tú. Lectores frecuentes de CodeBetterestán cansados de escuchar acerca de esto. aquellos que hemos gastado años programando en ASP clásico. saben que la gran integración entre código y HTML no era lo ideal. busca en internet para obtener más información a detalle y lo más importante. tomate el tiempo para analizarlas más profundamente. u otro desarrollador. tus clientes probablemente continúan solicitándote hacer todo tipo de cambios. Simplicidad La mejor herramienta para hacer código de fácil mantenimiento es conservarlo tan simple como sea posible.NET 8 Objetivos Aunque simplista. hay algunas ideologías con las que te debes de ir familiarizando. Estos sistemas no solo tienden a tener serias limitaciones técnicas (el desempeño puede verse reducido) pero casi siempre fallan para lo que están hechas (Veremos más de esto cuando hablemos acerca de YAGNI). la creciente adopción de desarrollos iterativos significa que los cambios y características son constantemente hechos para código existente (incluso si no has adoptado desarrollo iterativo tal como Agile.Capítulo 1 ALT. Segundo. el camino real para la flexibilidad es mantener el sistema tan simple como sea posible. La capacidad de mantenimiento es la piedra angular del desarrollo empresarial. En mi experiencia. Una creencia común es que para que sea de fácil mantenimiento.). no somos los primeros en escribir sobre crear código de fácil mantenimiento. solución a problemas o soporte. Al inicio. que son pensadas para manejar cualquier cambio que los clientes le pidan al equipo. Una de las cosas más importantes que puedes hacer es tomar nota concienzudamente cuando algo no se vea correcto y buscar en internet por una mejor solución. He visto sistemas construidos en metarepositorios (tablas con una columna llave y una columna Valor) o configuraciones complejas en XML. ¿Para qué construir un máquina de reglas configurable cuando todo lo que quieres que se haga es verificar que el nombre de usuario es de la longitud correcta? más adelante veremos como un desarrollo basado en pruebas nos puede ayudar a alcanzar altos niveles de simplicidad asegurándonos que nos enfocamos en lo que nuestro cliente nos pagó para hacer. cambios. Conforme avancemos. trata de ver como se podrían aplicar a algún proyecto en el que hayas trabajado recientemente. En corto. La experiencia nos dice que probablemente no Fundamentos de Programación Copyright © Karl Seguin www. Incluso si eres relativamente nuevo en la programación. Crear código de fácil mantenimiento no es la cosa más trivial. entenderlo. y hacer los cambios necesarios. Hasta aquí.

Esto te da a ti y a tu cliente tiempo para estar seguro de que realmente lo necesitas después de todo y probablemente puedas reducir el número de cambios que tendrás que hacer mientras y después del desarrollo.com . tus clases y métodos deben ser altamente cohesivos es decir. Las clases responsables de una multiplicidad de componentes distintos llegan a ser rápidamente inmanejables. Reducir o incluso eliminarel acoplamiento es más fácil de lo que la mayoría de las personas piensan. Una clase de Productores debe hacer exactamente lo que tú.NET 9 lo necesitarás o que necesites algo completamente diferente. Justo el otro día empecé a trabajar en un sistema de reporteo abierto hasta darme cuenta que entendí mal un correo electrónico y lo que el cliente realmente quería era un reporte simple diario que termino tomándome 15 minutos para construirlo. código. el último momento de responsabilidad es muy pronto en la fase de desarrollo. pero es importante cerciorarse de que tu código haga exactamente lo que dice que va a hacer. No solo hacen difícil el cambio de código (debido a que tienes que encontrar todos los lugares que hace lo mismo). diseño. El truco es identificar el acoplamiento indeseable. en detalle más adelante. otros desarrolladores en el equipo y tu cliente piensen que debe ser. En el capítulo siguiente. Cubriremos el acoplamiento. Además. vas a querer reducir el acoplamiento con el fin de reducir al mínimo el impacto causado por cambios y aumentar la capacidad de prueba del código. Ten en mente que el concepto va más allá de copiar y pegar y apunta por eliminar funcionalidad/comportamiento duplicado en todas las formas. debido a que incluso si realmente lo necesitas. deben tener un propósito único. Esto significa que las funciones y las variables deben ser nombradas apropiadamente y usar casos estandarizados y cuando sea necesario proporcionar la documentación apropiada.codebetter. Encapsulación de objetos y código altamente cohesivo puede ayudarnos a reducir la duplicación. unidades de prueba y documentación) terminaras con un código más limpio y mucho más fácil de mantener. Este concepto está fuertemente ligado con YAGNI. DRY La duplicación de código puede provocar dolores de cabeza a los programadores. Si estas escribiendo una clase Cliente que esté comenzando a manejar datos de Órdenes hay una muy alta posibilidad de que necesites crear una clase Orden. deberías de esperar a escribirlo hasta que no puedas esperar más. Al seguir el principio No te repitas a ti mismo (DRY Don tRepeatYourself) a través del ciclo de vida de un sistema (historias de usuarios. Acoplamiento El acoplamiento se produce cuando dos clases dependen unade la otra.Capítulo 1 ALT. Fundamentos de Programación Copyright © Karl Seguin www. Último momento de responsabilidad La idea detrás del último momento de responsabilidad es que difieres construir algo hasta que lo necesites absolutamente. además también tiene el potencial de introducir serios errores y hacerle la vida innecesariamente difícil para nuevos desarrolladores que se unan al desarrollo. Explicitud y cohesión Suena sencillo. en algunos casos. Puedes gastar un mes construyendo un sistema sorprendente y flexible para un cliente que tenga 2 líneas simples de correo que sea totalmente inútil. Cuando sea posible. existen estrategias y herramientas que te ayudarán. En verdad. veremos las capacidades de la programación orientada a objetosen lo que se refiere a crear código explícito y cohesivo.

Hay dos cosas que son importantes y debes de conocer de antemano. piensa en proyectos actuales y recientes y trata de listar cosas que no trabajaron tan bien como los que si funcionaron.codebetter. Gran parte de lo que trataremos está encaminada a mejorar la capacidad de prueba de nuestro código. nos zambulliremos primero en código real a partir de aquí.NET 10 Pruebas unitarias e integración continúa Pruebas unitarias e integración continua (comúnmente referido como CI) es otro tema que tenemos que aplazar para más adelante.En segundo lugar. . Como quiero que esto sea más experiencia de primera mano que teoría.com . Dado que la mejor herramienta es tu propia experiencia. Las Pruebas Unitarias permiten a los desarrolladores crear código con un increíble nivel de confianza. Es impresionante la cantidad de cambios en características y derefactorización que son capaces (o que estás dispuesto) a hacer cuando tienesuna red de seguridad de cientos o miles de pruebas automatizadas que validan que estén funcionando bien. pruebas unitarias. está perdiendo tu tiempo leyendo esto. nos las hemos ingeniado para cubrir varios temas. En primer lugar. Hasta entonces. Espero que ya hayamos aclarado algunas palabras comunes que has estado escuchando mucho últimamente. Los siguientes capítulos crearan los fundamentos para el resto de nuestro trabajo cubriendo OOP (programación orientada a objetos) y persistencia a un nivel alto. espero que inviertas algo de tiempo investigando algunas de las palabras claves que he lanzado. si no estás dispuesto a adoptar. En este capítulo Aunque este capítulo no tenía nada de código. ambos son primordiales para lograr nuestro objetivo de código sumamente fácil de mantener. o al menos intentar.Capítulo 1 ALT. Fundamentos de Programación Copyright © Karl Seguin www.

La razón de que este sea tan popular entre los desarrolladores . sino todo el entorno. el evento ItemDataBound siempre útil y habilidades para navegar con DataRelations.NET es que Microsoft ha invertido mucho tiempo automatizando el proceso del imitar con DataAdapters. Seguramente ese no es el caso de hecho ha sido muy trivial presentarlo así. que es la regla para los desarrolladores de Java y que rápidamente ha ido ganando terreno en la comunidad . Esto no lo hace mejor que el dirigido a datos esto simplemente hace al dirigido a dominios mejor que al dirigido a datos en Fundamentos de Programación Copyright © Karl Seguin www. pero eso haría que ambos.Existe un número limitado de maneras prácticas para diseñar el núcleo de su sistema. La atención se centra totalmente en los datos lo cual es una buena idea en muchos casos.. Un enfoque muy común para los desarrolladores de . El Dominio del Problema es una forma de expresar el negocio para el cual se está construyendo un sistema. El DDD simplemente se adapta mejor a la gestión de sistemas complejos de forma más fácil de mantener por una serie de razones que trataremos en los capítulos siguientes. columnas y relaciones de llaves foráneas. La herramienta que empleamos es programación orientada a objetos el emplear un lenguaje orientado a objetos como C# o VB. EL 2 MITCHELL KAPOR ra de esperarse que iniciara hablando acerca de diseño dirigido por dominios y programación orientada a objetos. nos desanimáramos.com .codebetter. A este enfoque se le llama algunas veces desarrollo dirigido por datos. Todos sabemos que al tener una tabla con datos.NET. Es el enfoque generalizado de primero modelar la base de datos creando todas las tablas. Las descripciones anteriores son algo engañosas de cierta forma implican que si utilizara DataSets no necesitaría preocuparse de.NET no significa que necesariamente se esté usando programación orientada a objetos (OOP). Entonces no sólo nos enfocamos en el hecho de que un empleado tiene un Nombre. En un principio pensé que podría evitar el tema al menos por un par de artículos. sino que en que él puede tener un incremento de sueldo. o estar preparado para ofrecer el comportamiento para incrementar el sueldo de un empleado. Un sistema data céntrico no está desprovisto de comportamientos ni los trata como una idea posterior. diseño dirigido por dominios (DDD). DataSets y DataTables. Es muy probable que usted ya sea un experto en este enfoque repeticiones anidadas dominadas.NET que consiste en utilizar un modelo centrado en datos. favorece al enfoque centrado en dominios. tanto ustedes como yo. Otra solución.Capítulo 2 Diseño Dirigido por Dominios DDD 11 Diseño dirigido por dominios DDD ¿QUÉ ES EL DISEÑO? ES EL LUGAR DONDE SE ESTÁ CON UN PIE EN DOS MUNDOS MUNDO DE LA TECNOLOGÍA Y EL MUNDO DE LA GENTE Y DE LOS PROPÓSITOS HUMANOS Y TRATA DE MANTENER A AMBOS JUNTOS. podemos generar una aplicación web o una forma de Windows corriendo en menos de cinco minutos con sólo unas cuantas líneas de código. El diseño dominio-céntrico o como es nombrado generalmente. se centra en el dominio del problema en general lo cual no sólo involucra datos. E Diseño dirigido por Dominios/Datos ¿Qué quiero decir al hablar de enfoque centrado en dominios? Datos-céntrico generalmente significa que enlaza su sistema entorno al conocimiento de los datos con los que estará interactuando. y después imitarlos en C#/VB.NET.

etc. Ellos eran molestos. colaborativa y positiva y no sólo se obtuvieron resultados en gran medida mejores. en ocasiones. Los usuarios. patrocinadores o expertos en el dominio. ellos harán decisiones equivocadas. sino una sola entidad: el equipo. tal vez porque usted cometa un error en la información que le Fundamentos de Programación Copyright © Karl Seguin www. Ellos deben priorizar de forma objetiva las características que todos desean. yo no veo un equipo de desarrollo y clientes. pero solo en extrañas ocasiones son los únicos usuarios. el comprometía su dinero y mi trabajo era permitirle obtener el máximo provecho de él. debe realizar las decisiones finales acerca de características y prioridades. Sin embargo me puse a pensar.) Usuarios. No obstante de que los verdaderos ALT. por ejemplo. tanto dentro como fuera de la comunidad de .Capítulo 2 Diseño Dirigido por Dominios DDD 12 algunos casos y lo contrario también es cierto. usuarios registrados. cuando se utilizan incorrectamente. Los clientes tienen un trabajo muy duro. tal vez porque no entienden plenamente la necesidad de un usuario. simplemente tiene que dar un salto de fe y tentativamente aceptar lo que predicamos al menos lo suficiente para que usted pueda juzgar por sí mismo. y me di cuenta de que yo no era tan inteligente como lo creía. en tanto me es posible. moderadores y administradores. Ya sea que tenga la fortuna o no de estar en tal situación (algunas veces los abogados se interponen. Evidentemente.NET podría resumirse como una batalla entre desarrollar dirigiendo por datos o desarrollar dirigiendo por dominios. sino que la programación se convirtió divertida nuevamente. El cliente es quien paga y como tal.NETerosdeberían de apreciar que el diseño dirigido a datos es sin duda una elección adecuada en algunas situaciones. (Esto puede ser un tanto rudo y contradictorio a lo que dije en mi introducción. aún aquellas que ellos mismos desean de manera objetiva y además deben hacer frente a su presupuesto finito. Clientes e Inversionistas Algo que tomo muy en serio del desarrollo ágil es la interacción cercana que el equipo de trabajo mantiene con el cliente y los usuarios.com . los inversionistas es cualquier persona que invierte el sistema. El mismo sitio web podría tener un sitio padre o hermano. probablemente se están rascando sus cabezas tratando de comprender por qué Microsoft insiste en ir en contra de la sabiduría convencional y mantener torpemente lo que siempre ha tenido. El cliente sabía mucho más acerca de su negocio que yo. Muchos programadores.) Es importante entender lo que le corresponde a cada quien. algunas veces los clientes no están disponibles para acuerdos. Por último. Los clientes. Un sitio Web.codebetter. frecuentemente hubiera querido emborracharme con mis clientes.NET. ciertamente.NET están haciendo (desarrollo empresarial) y. Así que cambiamos la relación por una más activa. Probablemente ha leído todo esto antes y al final. De hecho. No sólo eso. podría tener usuarios anónimos. produce menos código que es más fácil de mantener. pero el debate entre el camino MSDN y el de ALT. son usuarios. no sabía lo que querían y siempre realizaban eleccioneserróneas. Creo que gran parte de la hostilidad entre los "campamentos" es que Microsoft favorece desproporcionadamente el diseño dirigido a datos a pesar del hecho de que no se ajusta bien con lo que la mayoría de los desarrolladores . En el pasado. utilizan el sistema.

No trataremos persistencia (hablar a la base de datos) aún. Al principio se sentirá abrumado por sus conocimientos le hablará de lo que usted nunca ha oído hablar y será sorprendido por su aspecto atónito. yo le recomiendo leer historias de usuario un buen punto de partida es el excelente libro UserStoriesApplied1 de Mike Cohn. confiaremos en que el poder de las clases y de la encapsulación. Aquí es donde entran los expertos en el dominio en acción ellos lo ayudarán a comprender cómo funciona el sistema actualmente (incluso si se trata de un proceso manual con papel) y cómo debería trabajar. nos quedaremos sin trabajo. Expertos en el dominio son las personas que conocen todos los pormenores sobre el mundo en el que vive su sistema. Ya sea que esté construyendo un sistema comercial o no. al dominio. Cualquier persona que pase por lo anterior sabe que comprender un nuevo negocio es la parte más complicada de cualquier trabajo de programación. Si usted y su cliente toman en serio la creación de sistemas para los usuarios.com . incorrectamente.codebetter. y si el experto en el dominio aprende a programar. examinaremos la persistencia en mayor profundidad.Capítulo 2 Diseño Dirigido por Dominios DDD 13 proporcione o tal vez porque ellos darán. incluso usted. Su dependencia de expertos en los dominios crece con la complejidad de los sistemas. un inversionista y. Ya sabe cómo programar. y en los capítulos siguientes. Cualquier persona puede ser un experto en su dominio un cliente. hay beneficios reales al hacer que nuestro código se parezca. un usuario. la medida definitiva de su éxito consistirá en cómo se sienten los usuarios con él. En el siguiente capítulo trataremos los fundamentos de la persistencia. En este capítulo nos concentraremos en los conceptos básicos referentes a las clases y en algunos trucos para empezar a usarlas muchos desarrolladores ya conocerán todo lo que se cubre aquí. se verá a usted mismo preguntándose constantemente sobre la base de datos y sobre el código para el acceso a datos. Por último. En especial. esto es el verdadero propósito de un desarrollador empresarial comprender el dominio del problema. es de esperar que ambos se preocupen en las necesidades de los usuarios. Esencialmente estoy hablando de 1 URL en Amazon: http://www. Si es nuevo en este tipo de diseño. Intente no preocuparse demasiado. Hace poco formé parte de un proyecto de desarrollo muy grande para un Instituto financiero y hubo literalmente cientos de expertos en el dominio de los cuales la mayoría eran economistas o contadores. pero ¿sabe cómo programar el sistema de inventario para que haga lo que deben hacer? Alguien tiene que aprender el mundo de la otra persona. eventualmente. la OOP es la herramienta que utilizaremos para darle vida a nuestro diseño dominio-céntrico. están los expertos en el dominio.mayor prioridad a sus propias necesidades que a cualquier otra. Utilizará tantas siglas y palabras especiales que podrá comenzar a preguntarse si usted está o no a la altura. es su trabajo apoyarlos para que cumplan con su función. Como desarrollador. en la medida de lo posible.amazon.com/User-Stories-Applied-Development-Addison-Wesley/dp/0321205685 Fundamentos de Programación Copyright © Karl Seguin www. En última instancia. La idea detrás del DDD es construir el sistema de manera que refleje el dominio del problema real que está tratando de resolver. y como razón principal de la existencia de esta pequeña sección. Se trataba de personas que están tan entusiasmadas con lo que hacen así como usted lo está acerca de la programación. El objeto de dominio Como he dicho antes. Así que mientras esté trabajando estrechamente con su cliente. Por ello.

Por ahora. La idea es que un único idioma compartido entre los usuarios y el sistema es más fácil de mantener y tiene menor probabilidad de ser mal interpretado. cuando exploremos el desarrollo dirigido por pruebas tomaremos un enfoque diferente sobre la creación de un sistema que se adapta muy bien con DDD.codebetter. Lo primero que haremos será crear cuatro clases: public class Carro{} public class Modelo{} public class Paquete{} public class Actualización{} Después agregaremos un poco de código basado en algunas cosas que hemos asumido de forma correcta y segura: public classCarro { privateModelo _modelo. Si se estuviera creando un sistema para un concesionario de automóviles y hablara con un vendedor (que es probable que sea un usuario y un experto en el dominio). paquetes y actualizaciones. Hacer DDD no significa que necesariamente tiene que iniciar con el modelado del dominio (¡aunque es una buena idea!). privateList<Actualización> _Actualizaciones. significa que algunas de las ambigüedades y gran parte de la mala interpretación están borradas. creemos que una buena forma de iniciar es comenzar con los sustantivos claves que utilizan sus expertos en el negocio y los usuarios.Capítulo 2 Diseño Dirigido por Dominios DDD 14 comunicación. modelos. Si los usuarios están hablando de resultados estratégicos. Ya que son el núcleo de su negocio. es lógico que sean el núcleo de su sistema. Cómo iniciar exactamente es algo que realmente tiene que decidir usted. public voidAgregar(Actualizaciónactualización){ //todo } } public classModelo { private int _id. Muchas personas. private string _nombre. //todo Fundamentos de Programación Copyright © Karl Seguin www. pagos y así sucesivamente. incluyéndome a mí. Al principio muy bien puede iniciar con su modelo de datos. publicReadOnlyCollection<Actualización> ActualizacionesDisponibles() { return null. que ha llegado a ser conocida como el idioma ubicuo (ubicuosignifica presente en todas partes).com . supongamos que hemos hablado con nuestro cliente y con unos vendedores y hemos obtenido que el punto neurálgicoesmantener un seguimiento de la interdependencia entre las opciones de actualización. lo cual hace un mes no significaba nada para usted y ahora su código habla de resultados estratégicos. private int _año. sin duda hablará de clientes. carros. no obstante. Más allá de los sustantivos está la convergencia en el lenguaje de la empresa. más bien significa que debe centrarse en el dominio y dejarlo que dirija sus decisiones.

private string _nombre. foreach (Actualizaciónactualiza in _actualizaciones) { foreach (Actualización actualizaDependenciain actualización.codebetter. Actualizaciones). //todo } } } Esto es algo un tanto simple.AsReadOnly().Capítulo 2 Diseño Dirigido por Dominios DDD 15 } } public classActualización { private int _id. public classCarro { privateModelo _modelo. } } Primero. Después hemos implementado un método que nos permite obtener todas las actualizaciones faltantes. Nuevamente.Agregar(actualización). nombre). } publicReadOnlyCollection<Actualización>DependenciasPorActualizar() { Lista<Actualización>porActualizar = newLista<Actualización>(). el siguiente paso podría ser rastrear qué actualizaciones son las responsables de las actualizaciones Fundamentos de Programación Copyright © Karl Seguin www. este es sólo el primer paso. hemos implementado el método Agregar.Contiene(actualizaDependencia)) { porActualizar. Hemos añadido algunos campos tradicionales (id. y hemos añadido una función a la clase Carro. publicReadOnlyCollection<Actualización> ActualizacionesRequeridas { get {return null.Contiene(actualizaDependencia) && !porActualizar.Agregar(actualizaDependencia). algunas referencias (Carro y Modelo.com . Por ahora podemos olvidarnos de las modificaciones y empezar a escribir un poco sobre el comportamiento real.ActualizacionesRequeridas) { if (!_actualizaciones. } } } return porActualizar. public voidAgregar(Actualización actualización) { _actualizaciones. //todo ¿Dónde comenzar? privateLista<Actualización> _actualizaciones.

en general. Esto es porque el dominio es independiente de la capa de presentación se puede utilizar para alimentar un sitio Web.NET y WinForms tratan con código dominio-céntrico.NET está diseñado para controlar la interfaz de usuario de ASP. En segundo lugar.NET. pero la legibilidad y capacidad de mantenimiento siempre los son). al hacer uso real de la capa de dominio se van a revelar algunos errores y deslices. En mi experiencia. simplemente porque es difícil reutilizar el código dentro de un archivo aspx. por ejemplo: usted debe seleccionar 4 X 4 que es el valor que corresponde a Tracción. así como con clases data-céntricas. esto es exactamente lo que hacen muchos desarrolladores ASP. muchos de nosotros consideramos que es innecesariamente complicado y frágil. lo cual significa que no puede hacer la funcionalidad de dominio. usted no puede esperar demasiado para trabajar en la interfaz de usuario.vb con nuestras clases. También le alegrará saber que ASP. Usted puede enlazar datos a cualquier colección . mostrar mensajes de error o solicitar información adicional. no sólo obtendré como resultado código difícil de cambiary difícil de probar. El propósito era sólo señalar cómo podríamos comenzar y cómo se puede ver al inicio. sino que también será imposible reutilizar nuestra lógica en múltiples interfaces de usuario (lo cual podría no ser preocupante. El Framework de las páginas ASP. Interfaz de Usuario (IU) Habrá notado que aún no hablamos de lasIUs.NET no para implementar el comportamiento. Recuerde que desea escribir código cohesionado. Dudo que ellos se queden impresionados si le enviamos un montón de archivos . Lamentablemente.NETerostambién creen que usted debe mantener su mente abierta cuando se trata de su motor de presentación. Por supuesto.NET debería centrarse en hacer una cosa y hacerla bien cualquiera estará de acuerdo en que es administrar la página. Ante todo. la naturaleza desconectada de la web podría significar que tenemos que hacer pequeños cambios a nuestro mundo orientado a objetos puro con el fin de lograr una mejor experiencia de usuario. Tal vez debería redirigir al usuario a otra página. queremos obtener retroalimentación de nuestros clientes y usuarios tan pronto como sea posible.NET según los resultados de la capa de dominio.NET no es necesariamente la mejor herramienta para el trabajo. las pruebas unitarias son demasiado estrechas para detectar estas peculiaridades. el impacto en la interfaz de usuario es probablemente el menos significativo. De hecho.NET combinar su capa de interfaz de usuario y la de dominio. Lo último que usted desearía hacer es combinar su lógica de presentación y dominio. no debería sorprenderle saber que los ALT. Si lo hace.com . sin embargo. Incluso yo diría que es común verlas mezcladas en el código de los eventos de dar clic a un botón ASP. hasta aquí llegaremos por ahora. Por ejemplo. Hablaremos más sobre esto en un capítulo posterior. Su lógica de ASP. afectar la base de datos directamente).cs. pero si está Fundamentos de Programación Copyright © Karl Seguin www.cs o . A pesar de lo dicho anteriormente.codebetter. El Framework de las páginas ASP.Capítulo 2 Diseño Dirigido por Dominios DDD 16 faltantes. una aplicación Windows o un servicio de Windows. su propósito más bien es modificar la página ASP. El evento clic del botón Guardar no debe validar las reglas del negocio complejas (o peor. la lógica localizada en código oculto viola el principio de No lo Repitas . También. sesiones de uso y cachés como usted lo hacen normalmente y a hacer cualquier otra cosa a la que esté acostumbrado.NET o al cargar una página.

por lo que realmente es una cuestión de gusto y de sentimiento interior. El primer ejemplo que me viene a la mente es cuando quiere crear una instancia de una clase Usuario. string clave). A decir verdad.NET) o el marco MVC recientemente publicado por Microsoft. privateLista<Actualización> _actualizaciones. le sugiero que revise Mono Rieles (que es un marco de guías para . siempre me tomo un rato en la difícil tarea de elegir cual utilizar. pero esperando que la información le ayude a levantarse con el pie derecho. lo cual es imposible con un constructor esto podría o no ser útil en su caso en particular. En primer lugar. Es una buena idea mantener esta API Fundamentos de Programación Copyright © Karl Seguin www. Usuario._modelo = modelo. return carro. Patrones de fábrica ¿Qué hacemos cuando un cliente compra un carro nuevo? Obviamente necesitamos crear una instancia nueva del Carro y especificar el modelo. volvamos sobre el tema. En segundo lugar. así que por ahora.Capítulo 2 Diseño Dirigido por Dominios DDD 17 interesado en obtener más información. Un enfoque diferente es utilizar una fábrica que cree la instancia: public class Carro { privateModelo _modelo. podemos regresar un objeto nulo. pero rara vez con la misma claridad. Lo último que quiero es desalentar a quien sea con tantos cambios. Trucos y pistas Terminaremos este capítulo revisando algunas cosas útiles que podemos hacer con las clases.codebetter. una rica API emergerá para ser consumida por su interfaz de usuario. } publicstaticCarro CrearCarro(Modelo modelo) { Carro carro = new Carro(). } } Hay dos ventajas en este enfoque. private Carro() { _actualizaciones = newLista <Actualización>().CrearPorId(int id) y Usuario. La forma tradicional de hacerlo es utilizando un constructor y simplemente instanciar un nuevo objeto con la nueva palabra clave. si tiene muchas formas de crear un objeto. a usted le agradaría tener Usuario.ObtenerUsuariosPorRol (string rol).CrearPorCredenciales(stringnombredeusuario. Sólo cubriremos la punta del iceberg. Puede realizar la misma funcionalidad con la sobrecarga del constructor. tendrá la oportunidad de proporcionar nombres de función más significativos. carro. Modificadores de acceso En cuanto usted se dé a la tarea de escribir clases que encapsulen el comportamiento del negocio.com .

El método más sencillo para ello es mantener la API pequeña y ocultar todo a excepción de los métodos más necesarios.Capítulo 2 Diseño Dirigido por Dominios DDD 18 limpia y comprensible. } internalclassAccesoADatos { internalstaticIAccesoADatos CrearInstancia() { returnnewAccesoADatosSqlServer (). List<Actualización>actualizaciones = da. Las emplearemos tanto para desacoplar nuestro código como para crear clases simuladas en nuestras pruebas unitarias. Algunos métodos obviamente requieren ser públicos y otros privados. Una interfaz es un contrato al cual cualquier clase implementada debe adherirse. pero si aún no está seguro. } } Fundamentos de Programación Copyright © Karl Seguin www.com . Este código altamente acoplado causa problemas para realizar cambios o pruebas (no podríamos realizar una prueba a UnMétodoSimple sin tener el método ObtenerActualizaciones totalmente funcional.ObtenerActualizaciones(). } Puede ver que el código de ejemplo tiene una referencia directa a AccesoADatosSqlServer como podría hacerlo con muchos otros métodos para comunicarse con la base de datos. seleccione el modificador de acceso más restrictivo y cámbielo sólo cuando sea necesario. Interfaces Las interfaces jugarán un papel muy importante para crear código fácil de mantener. usted podrá minimizar considerablemente su API. Digamos que queremos encapsular toda la comunicación con la base de datos dentro de una clase llamada AccesoADatosSqlServer así: internal class AccesoADatosSqlServer { internalList<Actualización>ObtenerActualizaciones() { returnnull. El acoplamiento puede ser solucionado haciendo uso de una interfaz: internalinterfaceIAccesoADatos { List<Actualización> ObtenerActualizaciones().Yo hago buen uso del modificador interno en muchos de mis métodos y propiedades. //todo implement } } publicvoid UnMétodoSimple () { SqlServerDataAccess da = new AccesoADatosSqlServer (). Los miembros internos sólo son visibles para otros miembros en el mismo ensamblado por lo que si usted separa físicamente sus capas a través de varios ensamblados (que generalmente es una buena idea).codebetter.

no hay paradigma más adecuado para esta tarea que la programación orientada a objetos. Por lo general es una buena idea ocultar tanto como sea posible al crear clases y componentes para que los cambios a la aplicación no afecten a otras clases y componentes.codebetter.Capítulo 2 Diseño Dirigido por Dominios DDD 19 internalclassAccesoADatosSqlServer : IAccesoADatos { publicLista<Actualización> ObtenerActualizaciones() { returnnull. Esto es sólo un ejemplo sencillo de cómo podemos utilizar las interfaces para que ayuden a nuestra causa. Hasta la fecha. nos aseguramos de implementar la interfaz y de cambiar la clase de ayuda para devolverla en su lugar. Básicamente significa sus objetos de datos (los campos) y los de la aplicación. Todavía puede ser difícil ver el valor a largo plazo del diseño dirigido por dominio. Ocultar información y Encapsular Ocultar información corresponde al principio de que las decisiones de diseño deberían estar ocultas a otros componentes de su sistema. //todo implementar } } publicvoid UnMetodoSimple () { IAccesoADatos da = AccesoADatos.com . La encapsulación es una forma de implementar la información oculta en OOP.ObtenerActualizaciones(). Podemos fortalecer el código al crear una instancia de nuestra clase a través de la configuración dinámica de datos o introduciendo un frameworkespecialmente diseñado para el trabajo (que es exactamente lo que vamos a hacer). además de tener una mayor capacidad para realizar pruebaspodrían no parecer necesarios. Esperemos que a medida de que avance a través de los capítulos restantes y que Fundamentos de Programación Copyright © Karl Seguin www. El ejemplo más habitual es hacer campos privados con propiedades públicas. Si queremos cambiar nuestra implementación. simplemente tendremos que hacer uno. Y así. por lo que. la OOP fue diseñada con el objetivo específico de permitir a los desarrolladores modelarlas cosas de la vida real. simplemente creamos la nueva clase de Oracle. no deberían ser accesibles a otras clases. } Hemos introducido la interfaz junto con una clase auxiliar para devolver una instancia de dicha interfaz. List<Actualización>actualizaciones = da. por decir un AccesoADatosOracle. si no está familiarizado con ellas. A menudo podremos ser favorecidos al programarcon interfaces en lugar de hacerlo con clases reales. Simplemente hay demasiados requisitos extraños o entrelazadas y demasiadas reglas de negocio. Compartir un lenguaje común con el cliente y para los usuarios.CrearInstancia(). De hecho. en vez de tener que hacer múltiples cambios (posiblemente cientos). En este capítulo La razón por la que existe el desarrollo empresarial es que no existe un solo producto estándar que pueda resolver con éxito todas las necesidades de un sistema complejo. le sugiero hacer alguna lectura adicional. Aún mejor es preguntarse a sí mismo si incluso el campo de _id necesita ser una propiedad pública.

. Fundamentos de Programación Copyright © Karl Seguin www.Capítulo 2 Diseño Dirigido por Dominios DDD 20 experimente por su cuenta. empiece a adoptar algunos de los conceptos que se ajusten a sus necesidades y a las de sus clientes.codebetter.com .

como los Datasets. En este capítulo discutiremos sobre cómo lidiar con persistencia usando DDD.com .Persistencia 21 Persistencia LA INTENCIÓN DE CODD ERA LIBERAR A LOS PROGRAMADORES DE LA OBLIGACIÓN DE SABER LA ESTRUCTURA FÍSICA DE LOS DATOS. eso es calculando que podemos cerrar la brecha. entonces has de tener varias dudas de como funcionaria esto. Hoy en día la solución favorita es una base de datos relacional. tu programa trabaja en la memoria y requiere un lugar para guardar (o persistir) información. hasta donde yo sé. Si lo pensaste. NUESTRA INTENCIÓN ES LIBERARLOS AÚN MÁS EVITANDO QUE TENGAN QUE SABER LA ESTRUCTURA LÓGICA.codebetter. podemos.Miremos un ejemplo Fundamentos de Programación Copyright © Karl Seguin www. y problemas para darle mantenimiento. hace el trabajo difícil por nosotros. Con respecto a Programación Orientada a Objetos. Si estás acostumbrado a programar con Datasets. En secciones posteriores veremos alternativas más avanzadas (dos diferentes formas de mapeo O/R) que. Escribiremos código para unir la brecha entre nuestros Objetos C# y nuestras tablas SQL. Microsoft básicamente trata de ignorar este problema y simplemente hizo una representación de relación dentro del código de Objetos-orientados una estrategia inteligente pero con sus pros y contras como el pobre desenvolvimiento.Capítulo 3 . Básicamente esto significa que los datos de relación no se distribuyen perfectamente en objetos y los objetos no se distribuyen perfectamente cuando se archivan datos de relación. La brecha Como sabes. Pero ¿qué brecha exactamente? ¿Qué es esta disparidad de impedimento? Probablemente estás pensando que no puede ser tan difícil invocar datos relacionales en objetos y de vuelta a las tablas. generalmente. Claro.. (Por otro lado son bases de datos de orientadas a objetos que.. no han despegado tampoco). descontrol de abstracciones. mi sugerencia siempre ha sido escribir manualmente un código que conecte estos dos mundos. al reto se le ha asignado un término: Disparidad de Impedimento Relación-Objeto. es una cosa muy difícil de llevar a cabo. sin la ayuda de patrones y herramientas. Antes que tratar de ignorar el problema. La intención de este capítulo es traer solución a las discusiones previas tratando sobre patrones de persistencia más avanzados. Actualmente la persistencia es un tema de suma importancia en el campo de desarrollo de software porque. por ahora asumamos que siempre será un proceso simple) DataMapper Para proyectos pequeños con un puñado de clases de dominio y tablas de base de datos. LAZY SOFTWARE 3 E n capítulos anteriores pudimos platicar ampliamente de DDD sin tener que hablar mucho de base de datos. Los Datasets son grandiosos y te han resuelto demasiados problemas. entonces ¡tienes toda la razón! (bueno. y debemos afrontarlo. Debemos afrontarlo para que podamos nivelar lo mejor de ambos mundos reglas complejas de negocios implementadas en OOP y archivado & recuperación de datos vía base de datos relacional. dificultad para someterlo a prueba.

} set { _name = value. o persistirá. privateList<Actualización> _actualizacionesRequeridas.Capítulo 3 . } } publicstring Name { get { return _name. Fundamentos de Programación Copyright © Karl Seguin www. la información de Actualización.codebetter. } set { _descripción = value. } } } Hemos incluido los campos básicos que esperabas ver en la Clase. A continuación crearemos la tabla que almacenará. } } publicdecimalPrecio { get { return _precio. privatestring_descripción.com . Descripción VARCHAR(512) NOTNULL. } } publicList<Actualización>ActualizacionesRequeridas { get { return _actualizacionesRequeridas. privatestring _nombre. [Nombre] VARCHAR(64) NOTNULL.Persistencia 22 simple: La primera cosa que hacemos es expandir nuestra Clase de Actualización (nos estamos enfocando solamente en las porciones de datos de nuestra Clase (los campos) desde que es donde reside la Persistencia): publicclassActualización { privateint _id. CREATETABLEActualiaciones ( Id INT IDENTITY(1. Precio MONEY NOTNULL. } } publicstringDescripción { get { return _descripción.1) NOTNULLPRIMARY KEY. privatedecimal _precio. } internalset { _id = value. publicint Id { get { return _id. } set { _precio = value.

Capítulo 3 . comenzamos a construir nuestra capa de acceso de datos.codebetter. Fundamentos de Programación Copyright © Karl Seguin www.CrearActualización(dataReader)).ExecuteReader(CommandBehavior. mostrado a continuación. Descripción. command. } } privateSqlDataReader ExecuteReader(SqlCommand command) { SqlConnection connection = newSqlConnection(_connectionString).Connection = connection.Add(DataMapper. Precio FROM Actualizaciones". using (SqlCommand command = new SqlCommand(sql)) using (SqlDataReader dataReader = ExecuteReader(command)) { List<Actualización>actualizaciones = new List<Actualización>(). Nombre.com .Read()) { upgrades. Es directo porque el modelo del dominio y el modelo de los datos son similares. Ahora viene la parte interesante (relativamente hablando).CloseConnection) } } ExecuteReaderes un método de ayuda que ligeramente reduce el código redundante que tenemos que escribir. que se sitúa entre el dominio y el modelo relacional (se excluyeron las interfaces por propósitos de brevedad) internalclass SqlServerDataAccess { privatereadonlystaticstring _connectionString = "FROM_CONFIG" internalList<Actualización>ObtenerTodoslosUpgrades() { //use a sproc if you prefer string sql = "SELECT Id. connection.Persistencia 23 ) Sin sorpresas todavía.Open(). CreateUpgrade. while (dataReader. es un código re-utilizable que usamos para bosquejar la información de la actualización archivada en la base de datos dentro de nuestro dominio. } return upgrades. return command.CreateUpgrade. RetrieveAllUpgrades es mas interesante ya que selecciona todas las actualizaciones y las carga en una lista vía la función DataMapper.

512). actualización.Value = actualización. Esta diferencia es una constante que se inclina del lado de los desarrolladores. parameters[1]. todavía caemos en la temida Disparidad de Impedimento.1 o más).Value = actualización.Price = Convert.Price. donde el mundo relacional usa claves externas. parameters[2] = new SqlParameter("Descripción".codebetter.VarChar. actualización. } Tenemos un problema A pesar de que hemos tomado un ejemplo muy común y simple. returnactualización.com .VarChar. Fundamentos de Programación Copyright © Karl Seguin www. aquí te damos una posible solución: internalstaticSqlParameter[] ConvertirActualizaciónAParametro(Actualización actualización) { SqlParameter[] parameters = newSqlParameter[4]. En el mundo de los dominios estas son referencias (o una colección de referencias) hacia otros objetos.Id = Convert. return parameters.ToString(dataReader["Nombre"]). Por ejemplo. parameters[0].ToDecimal(dataReader["Precio"]).Capítulo 3 . 64). Primero agregaremos una tabla de unificación que asociará las actualizaciones con otras actualizaciones que sean requeridas (puede ser 0.Name = Convert. } } Si lo necesitamos.Id. La solución no es tan difícil. parameters[3] = newSqlParameter("Precio". podemos re-utilizar CrearActualizacióntantas veces como sea necesario.Money).Description. SqlDbType. parameters[3]. SqlDbType. SqlDbType.Value = actualización.Descripción = Convert. parameters[1] = newSqlParameter("Nombre".Persistencia 24 internalstaticclass DataMapper { internalstaticActualizaciónCrearActualización(IDataReader dataReader) { Actualizaciónactualización = newActualización().ToString(dataReader["Descripción"]). Note que nuestra capa de acceso de datos (SqlServerDataAccesso DataMapper) no maneja la tan necesaria colección ActualizacionesRequeridas. nos gustaría tener la habilidad de recuperar Actualizaciones por ID o por Precio ambos serán nuevos métodos en la Clase SqlServerDataAccess Obviamente.Int).Name. parameters[0] = newSqlParameter("Id".ToInt32(dataReader["Id"]). parameters[2]. Esto es porque una de las cosas más difíciles de manejar son relaciones.Value = actualización. actualización. podemos aplicar la misma lógica cuando queramos archivar objetos Actualizaciónde regreso en la tabla. SqlDbType. actualización.

actualizaciones. Precio FROM Actualizaciones.Capítulo 3 .CrearActualización(dataReader). obtenemos las actualizaciones apropiadas de la búsqueda en el diccionario y las adherimos a la colección. Descripción. while (dataReader. ActualizaciónRequeridaId INT NOT NULL.NextResult(). localCache. } returnactualizaciones. Nombre.TryGetValue(actualizaciónId. Dictionary<int. outrequerida)) { //sería buena idea lanzar una excepción continue. outactualización) || !localCache. Lo siguiente es que repetimos através de la tabla de unificación. using (SqlCommand command = newSqlCommand(sql)) using (SqlDataReader dataReader = ExecuteReader(command)) { List<Upgrade>actualizaciones = new List<Actualización>().Read()) { intIdActualización = dataReader. actualización). ActualizaciónRequeridaId FROM DependenciasActualizaciones". } } Llamamos la tabla de unificación extra junto con nuestra solicitud inicial y creamos un diccionario local de búsqueda para acceso rápido a nuestras actualizaciones por su ID. while (dataReader. } actualización.codebetter. ) A continuación modificamos RecuperarTodaslasActualizacionespara cargar las actualizaciones requeridas: internalList<Actualización>RecuperarTodaslasActualizaciones() { string sql = @"SELECT Id.TryGetValue(actualizaciónRequeridaId.GetInt32(1). SELECT IdActualización.Id.ActualizacionesRequeridas.com .Persistencia 25 CREATE TABLE DependenciasActualizaciones ( IdActualización INT NOT NULL. Fundamentos de Programación Copyright © Karl Seguin www. Actualización>(). Actualización> localCache = newDictionary<int. } dataReader. if (!localCache.Add(actualizaciónRequerida).GetInt32(0).Add(actualización. Actualización requerida.Read()) { Actualizaciónactualización = DataMapper.Add(actualización). Actualizaciónactualización. intActualizaciónRequeridaId = dataReader.

en vez de cargar todas las actualizaciones requeridas al inicio. Si se llama a Guardar en tu Modelo. En este caso no es un problema mayor desde que es solo una referencia de 32bit. debemos distinguir entre las instancias de Actualización. Una vez que continúe el camino de escribir manualmente esta clase de código fácilmente se puede escapar de las manos. Actualizaciónactualización1b = da. pero trabaja muy bien! Tendremos la oportunidad de re-factorizar las funciones un poco más para hacerla un poco más entendible. es demasiada repetición de código. El asunto más importante tiene que ver con la identidad. Un mejor ejemplo sería el Modelo Relacional de Actualización.RecuperarTodaslasActualizaciones()[0]. Esto es. A menudo deseará tener relaciones de carga-fácil.codebetter.Precio = 2000. Es relativamente sencillo implementar las cargas fácilmente. Sin embargo. Si queremos agregar métodos de Filtro/Orden nos conduciría a escribir SQL dinámica o tendríamos que escribir demasiados métodos. actualiza objetos con referencia. La solución más simple es utilizar Guardar seguido por cada acción individual pero esto puede ser ambas.Guardar(). En algunos casos eso no sería un problema. Actualización actualización1a = da. Terminaríamos escribiendo un número exagerado de métodos RecuperarActualizacionesPorX que luciría tediosamente similar uno del otro. Si llamamos RecuperarTodaslasActualizaciones dos veces. El cambio de precio de la primera actualización no será reflejado en la instancia señalada por actualización1a. pero por ahora y para este simple caso. hará el trabajo. quizá solo queremos cargarlas cuando sea necesario. Limitaciones Aunque solo estamos haciendo una observación inicial al bosquejo. tienes que asegurarte que cuando se persiste un objeto tú también persistes. en muchas situaciones desearías que tu capa de acceso de datos lleve registro de la identidad de las instancias ya que crea y fortalece alguna clase de control (puedes informarte mejor vía Google buscando Identificar Mapas de Patrones) Existen posiblemente más limitaciones. es deberá por ejemplo crear una nuevo Modelo y agregar una nuevaActualización. dado que: SqlServerDataAccess da = newSqlServerDataAccess(). pero la última de la que hablaremos tiene que ver con unidades de trabajo (infórmate más usando Google para buscar el patrón Unidades de Trabajo ) Esencialmente cuando creas tu código manualmente para tu capa de acceso de datos. si es necesario.com .Capítulo 3 . esto puede desembocar en inconsistencias. se necesita asegurar que tu Actualización sea también grabada. difícil (relaciones Fundamentos de Programación Copyright © Karl Seguin www. actualización1b. actualización1b.RecuperarTodaslasActualizaciones()[0]. Si se está trabajando en la sección administrativa de nuestro sistema de auto ventas.Persistencia 26 No es la solución más elegante. pero como los mencionamos anteriormente. Vale la pena observar las limitaciones a las que nos hemos sometido.

Persistencia 27 pueden tener varios niveles de profundidad) e ineficiente. Desde que bosquejar de esta manera es totalmente directo. Cabe recordar que para sistemas pequeños. lo más importante es que entiendas las limitaciones que tiene este método. pero cuando sea necesario re-examinaremos . nos encontramos con varios inconvenientes.com . es casi imposible codificar manualmente (además es preferible invertir en la funcionalidad que el cliente solicitó. es importante ver la función de bosquejar en acción y aun así que decidimos practicar con un ejemplo simple.codebetter. que perder tu tiempo tratando de implementar tu propia Unidad de Trabajo). pero para proyectos grandes. Fundamentos de Programación Copyright © Karl Seguin www. o que rápido se expandiría tu capa de acceso de datos cuando se incrementen los requisitos. esto no representa gran problema. Sin embargo. no dependeremos de bosquejar manualmente no es lo suficientemente flexible y terminaríamos desperdiciando demasiado tiempo escribiendo código que es inútil para el cliente. En este capítulo Al final de cuentas. No revisaremos la persistencia por algunos capítulos todo el potencial que contiene este método. Igualmente deberás cambiar solamente algunas propiedades y entonces tener que decidir entre volver a grabar todos los campos. Imagínate que podría pasar si dos instancias distintas de la misma información están presentes flotando en tu código. o de alguna manera llevar registro de los cambios de propiedades y actualizarlas.Capítulo 3 .

LA VERDADERA PREGUNTA ES CÓMO ESCOGER INTELIGENTEMENTE A QUE ACOPLARSE. es la habilidad de cambiar la capa de acceso a datos para conectarte a una base de datos distinta. Irónicamente. YO DIRÍA QUE LA INGENIERÍA DE SOFTWARE MODERNA ES EL REFINAMIENTO EN 4 CURSO DE LOS SIEMPRE CRECIENTES NIVELES DE ACOPLAMIENTO. Hoy veo el desarrollo basado en capas como algo natural por producto con código de alta cohesión con al menos algunas ideas alrededor de acoplamiento. Yo sigo creyendo que el desarrollo en capas es importante.Por si acaso pero que me dices de mantén las cosas simples y No vas a necesitarlo (YAGNI por sus siglas en inglés) DESARROLLADORES SOLAMENTE PUEDEN AGREGAR VALOR ACOPLANDO COSAS. pero mi razonamiento ha cambiado. Si tú sabes que algo va a ser difícil de implementar después. creando una implementación de Fundamentos de Programación Copyright © Karl Seguin www. El ejemplo más común.codebetter. debería de salir automáticamente tu desarrollo en capas. LOS EL ACTO DE ESCRIBIR CÓDIGO POR SÍ MISMO ES ACOPLAR UNA COSA CON OTRA. Para prevenir los dolores de cabeza que sufrí. Es decir.Capítulo 4 Inyección de dependencias 28 Inyección de dependencias. aplicaciones Windows y Servicios Web. rara vez he tenido que escribir múltiples capas de presentación para una capa de dominio. TAMBIÉN SUGIERE QUE EL ACOPLAMIENTO ES INEVITABLE. Mientras varios desarrolladores consideran que es una regla dura. la más obvia es tu propia experiencia. o al menos poner las bases. MALO. yo normalmente pienso en ella como una guía. SIN EMBARGO. JUVALLÖWY s común escuchar a desarrolladores promover el uso de capas como un método para proveer extensibilidad. E Yo estaba acostumbrado a escribir sobre la importancia de las capas de dominio para tener reusabilidad a través de diferentes capas de presentación: Sitios web. MIENTRAS LA HISTORIA DEL SOFTWARE NOS MUESTRA QUE EL ACOPLAMIENTO ES UNA APLICACIÓN TOTALMENTE DESACOPLADA ES TOTALMENTE INÚTIL YA QUE NO AGREGA VALOR. de seguro tú ya sabes qué base de datos vas a utilizar y no vas a tener que cambiarla. si haces las cosas correctamente. Si tus proyectos no se parecen a los míos. (Algo más sobre no vas a necesitarlo YAGNI. Hay muy buenas razones por las cuales quieras ignorar YAGNI. No fue hasta que comencé con pruebas unitarias que me di cuenta lo enredado y frágil que era mi código. De seguro podrías darle esa flexibilidad por adelantado . Esto es algo que yo hago frecuentemente con el almacenamiento en caché.com . y uno que usé en el Capítulo 2 cuando estábamos revisando las interfaces. sería una buena idea implementarloahora. La verdadera razón por la cual estamos invirtiendo un capítulo completo a desacoplamiento(el desarrollo en capas es una implementación de alto nivel de desacoplamiento) se debe a que es un ingrediente clave para poder escribir código que se puede probar. vamos a cubrir acoplamiento y después hablaremos sobre pruebas unitarias en el siguiente capítulo. Rápidamente me quedé frustrado porqué el método X dependía de una función en la clase Y que necesitaba una Base de datos activa y funcional.

) Un vistazo a las pruebas unitarias Hablando de acoplamiento con respecto a las pruebas unitarias es muy parecido como el problema del huevo y la gallina. } } privatebool EsValido() { //Hacer: asegurarse que el objeto esta en un estado válido returntrue.Por esta razón pruebas unitarias hace uso de clases simuladas o clases fingidas. DRYy Paso sostenibleson fácilmente las tres que considero más importantes.CrearInstancia(). } else { AccesoaDatos. de las numerosas implementaciones de directrices que existen. No se enfocan en pruebas de principio a fin. Lo más importante de las pruebas unitarias es la unidad. a excepción de darme las bases necesarias para una implementación real en el futuro. YAGNI.com . Veamos un ejemplo. hay 3 cosas que debemos hacer: Fundamentos de Programación Copyright © Karl Seguin www.Capítulo 4 Inyección de dependencias 29 ICacheProvider y NullCacheProviderque no hacen nada.Guardar(this). Dicho esto. Yo creo que es mejor continuar con acoplamiento y después hablaremos de los fundamentos de pruebas unitarias. esto te da un sistema sólido.Guardar(this). publicvoid Guardar() { if (!EsValido()) { //Hacer: crear un mejor manejador de Excepciones thrownewInvalidOperationException("El auto debe estar en un estado válido"). } if (_id == 0) { _id = AccesoaDatos.codebetter. guardar el estado de un auto: publicclass Auto { privateint _id. Esto es complicado ya que el método que quieres probar unitariamente puede tener dependencias con otras clases que no pueden ser ejecutadas fácilmente dentro del contexto de una prueba (por ejemplo una base de datos o un componente del navegador). sino más bien en el compartimiento individual. } } Para probar efectivamente el método Guardar.CrearInstancia(). La idea es que puedas probar cada comportamiento de cada método exhaustivamente y probar su interacción con otro.

cualquier código que genera salidas aleatorias (generación de contraseñas. generadores de claves).codebetter. y 3. Lo que no está bien hacer es acoplarse a un componente externo (Base de datos. me he dado cuenta que supuesto lo primero que tienes que saber es que en la gran la ventaja más inmediata esta en mayoría de los casos. Ya que siempre es una buena idea desacoplar tu base de datos del dominio. pero los marcos de referencia de simulación de clases nos van a permitir interceptar las llamadas a GuardaryActualizar. Esto puede sonar como una descripción vaga. estado del servidor. No evites el acoplamiento como si fuera una plaga Fundamentos de Programación Copyright © Karl Seguin www. en adición a los acoplando la clase System. Asegurarnos que la excepción adecuada es producida cuando intentamos guardar un Auto en estado inválido. pero después de este capítulo y el siguiente y después de que hayas jugado con las pruebas unitarias por ti mismo. yo estoy tentado a dar mi voto a favor del desacoplamiento como la necesidad más grande para las aplicaciones modernas.String Si esta cambia es beneficios citados de las pruebas muy probable que tu código deje de funcionar.Es difícil encapsular tu cabeza con los conceptos de clases simuladas sin un ejemplo concreto. y cuáles no. es probar la funcionalidad de EsValido o de las funciones de AccesoadatosGuardar y Actualizar (otras pruebas se encargarán de validarlas). asegurar que los argumentos adecuados son enviados. acoplamiento es simplemente lo que llamamos cuando una clase requiere otra clase para poder funcionar. usaremos este ejemplo durante este capítulo. cache del servidor. Está bien usarla para la clase Auto para mantener una referencia directa a la clase Actualizar en este punto sería muy complicado introducir una interface IActualizar. incluso las más pequeñas líneas de código tienen dependencias con otras clases.Capítulo 4 Inyección de dependencias 30 1. Asegurarnos que el método de AccesoadatosGuardar es llamado cuando es un Auto nuevo. cualquier código que requiera una configuración extensiva (esquemas de bases de datos) y. Como se hizo mención de Juval al principio de este capítulo. tendrás una buena sensación sobre qué cosas deberás evitar. nuestras clases.com . No aprender los patrones del buen y queremos crear interfaces y proveedores para cada una de mal acoplamiento. El último punto es importante todo lo que tenemos que hacer es asegurarnos que estas funciones son llamadas con los parámetros adecuados y que los valores de retorno (en caso de tenerlos) son manejados de manera adecuada. En caso de que hayas olvidado el Capítulo 1. como yo aprendí en mi último proyecto. 2. incluso un ejemplo tan tonto como el ayudar a los desarrolladores a de la cadena de texto el acoplamiento no es algo malo. Por unitarias. Asegurarnos que el método Actualizar es llamado cuando es un Auto existente. Asi es que si escribes stringsite = ³MejorCódigo´. estas De hecho. servicio web). Todo. y forzar que se regrese el valor que queremos los marcos de referencia de simulación son efectivos y divertidos a menos que tu código este altamente acoplado. Lo que no queremos hacer (que es tan importante como lo que queremos hacer). Es esencialmente una dependencia.

ID es diseñado específicamente para este tipo de situaciones. Para poder lograrlo. Primero. una creada manualmente.Add(auto). es un patrón que cambia una dependencia escrita en código compilado en algo que puede ser inyectado en tiempo de ejecución. solo necesitamos hacer una pequeña modificación a la clase Auto: publicclass Auto { privateint _id. pero por ahora nos va a ayudar a mantener las cosas simples): internalinterface IAccesoaDatos { int Guardar(Auto auto). vamos a utilizar un patrón llamado Inyección de Dependencias (ID).Count. privateIAccesoaDatos _ProveedordeDatos. por el momento nos funciona bien. } } Aunque nuestra función simulada de Actualizar. } publicvoid Actualizar(Auto auto) { _auto[_auto. y otra que mejora una librería de terceros. ya que como su nombre lo indica. veamos nuestra interface de AccesoaDatosotra vez y creemos una implementación (simulada) falsa (no te preocupes.Capítulo 4 Inyección de dependencias 31 Inyección de dependencias En el capítulo 2 vimos como las interfaces pueden ayudar nuestra causa sin embargo.com . no tienes que crear implementaciones simuladas de cada componente. Ya que tenemos esta clase falsa. este código no nos permite dinámicamente proveer una implementación simulada de IAccesoaDatosque sea regresada por la fábrica de clases AccesoaDatos. Constructor de inyección La forma más simple de ID es el constructor de inyección lo que hace es inyectar las dependencias vía un constructor de clases. Fundamentos de Programación Copyright © Karl Seguin www. void Actualizar(Autoauto).codebetter. podría ser mejorada.IndexOf(auto)] = auto. } internalclass AccesoaDatosSimulado : IAccesoaDatos { privatereadonlyList<Auto> _auto = newList<Auto>(). return _auto. Vamos a revisar dos formas de ID. publicint Guardar(Auto auto) { _auto.

Actualizar(this). if (auto. Fundamentos de Programación Copyright © Karl Seguin www.Capítulo 4 Inyección de dependencias 32 public Auto() : this(new SqlServerDataAccess()) { } internal Auto(IAccesoaDatos ProveedordeDatos) { _ProveedordeDatos = ProveedordeDatos. } if (_id == 0) { _id = _ ProveedordeDatos. la implementación inicial es utilizada. } else { _ ProveedordeDatos.codebetter. Por otro lado.Guardar(this). como una instancia de AccesoaDatosSimulado podemos hacerlo así: publicvoid CasiunaPrueba() { Auto auto = new Auto(newAccesoaDatosSimulado()).Id != 1) { //algo falló } } Hay variaciones menores disponibles Pudimos haber inyectado un IAccesoaDatosdirectamente en nuestro método Guardar o asignar el campo privado _AccesoaDatos a través de una propiedad interna Lo que uses depende más de tu gusto personal. auto.com . } publicvoid Guardar() { if (!EsValido()) { //Hacer: crear un mejor manejador de Excepciones thrownewInvalidOperationException("El auto debe estar en un estado válido"). } } } Vamos a revisar el código y sigámoslo paso a paso.Save(). si queremos inyectar una implementación específica. Date cuenta en el uso de la sobrecarga del constructor la cual al introducir ID no tiene ningún impacto en el código existente si decides no inyectar una instancia IAccesoaDatos.

NET seleccionando los archivos.El resto de este capítulo nos vamos a enfocar en StructureMap.Foundations" PluggedType="CodeBetter. La configuración más sencilla para echar a andar StructureMap es algo así: <StructureMap> <DefaultInstance PluginType="CodeBetter.Foundations. uno para la base de datos y otro para un servicio web. y los constructores). y la mayoría de las personas que usan pruebas unitarias aman las herramientas de código abierto.net/) Antes de utilizar StructureMap debes configurarlo usando un archive de XML (llamado StructureMap.Foundations.Capítulo 4 Inyección de dependencias 33 Marcos de referencia Hacer ID manualmente funciona perfecto en algunos casos. CodeBetter. no es sorpresa que exista un buen número de marcos de referencia para ayudar a automatizar la ID. Si te interesa aprender más sobre esto.sourceforge. Ya que ID es crítico para las pruebas unitarias.com . y después ve a Propiedades y configurar el atributo Copiar al directorio destino para que quede como CopiarSiempre. Puedes automatizarlo en VS. el método Guardar ahora se ve así: publicclass Auto { privateint _id. Ya que lo tenemos configurado. un marco de referencia para Inyección de Dependencias creado por mi compañero en CodeBetter Jeremy Miller(http://structuremap. es importante que sepas que el archivo XML se debe encontrar en la carpeta/bindentro de tu aplicación. publicvoid Guardar() { if (!EsValido()) { Fundamentos de Programación Copyright © Karl Seguin www.SqlDataAccess.Foundations"/> </StructureMap> Ya que no quiero gastar mucho tiempo hablando de configuración. Solamente necesitamos pedírsela a StructureMap. te recomiendo que visites el sitio de StructureMap). La configuración básicamente dice esta es la interfaz que quiero programar y esta es la implementación inicial. podemos deshacer todos los cambios que hicimos a nuestra clase Auto para permitir la inyección del constructor (quita el campo _ProveedordeDatos. Un proyecto en el que trabajé recientemente tenía muchos componentes fundamentales que tenían que ser inyectados uno para el manejo del cache.config) o agregando atributos a tus clases.IDataAccess.codebetter. Para obtener la implementación correcta de IAccesoaDatos. pero sería desastroso en situaciones más complejas. (Hay una gran variedad de opciones de configuración disponibles. Las clases se contaminaron con múltiples constructores sobrecargados y demasiados tenían que ver con configurar la clases para pruebas unitarias. uno para registro. CodeBetter.

Pudimos haberlo mejorado aúnmás. newAccesoaDatosSimulado()).Guardar(this). Los marcos de referencia como StructureMap son fácil de utilizar y además de mucha utilidad.Save(). disminuyes de manera considerable el acoplamiento lo que incrementa la facilidad para ejecutar pruebas.ResetDefaults(). Con un par de líneas de configuración y unos cambios menores en tu código.GetInstance<IAccesoaDatos>(). } Usamos InjectStub para que las siguientes llamadas a GetInstance regresen nuestra simulación. solamente tenemos que inyectar la simulación dentro de StructureMap: publicvoid CasiunaPruea() { ObjectFactory. if (auto. incluso a un punto donde pueda hacernos más daño que ayudarnos. if (_id == 0) { _id = AccesoaDatos. auto. hemos logrado remover la mayoría del mal acoplamiento que se encontraba en nuestro ejemplo. He llevado StructureMap en proyectos muy grandes y lo he implementado en cuestión de minutos el impacto es menor.InjectStub(typeof(IAccesoaDatos). Una última mejora Con la introducción de la clase IAccesoadatosasí como del uso del marco de referencia de ID. } IAccesoaDatos AccesoaDatos = ObjectFactory. Auto auto = new Auto(). y nos aseguramos que todo regrese a su estado original usando ResetDefaults.Actualizar(this).com .Id != 1) { //AlgoFalló } ObjectFactory. } } } Para usar una simulación en lugar de la implementación original.codebetter.Capítulo 4 Inyección de dependencias 34 //Hacer: crear un mejor manejador de Excepciones thrownewInvalidOperationException("El auto debe estar en un estado válido"). } else { AccesoaDatos. Existe una última dependencia que me gustaría ocultar nuestros objetos de negocio estarían mejor que no Fundamentos de Programación Copyright © Karl Seguin www.

tenemos la posibilidad de hacer cambios masivos (seleccionando diferentes marcos de referencia de ID) de manera adecuada en el desarrollo de nuestra aplicaciónfácilmente. dominio y datos son los 3 más obvios). . Todo lo que se requiere es un poco de conocimiento y disciplina por supuesto las herramientas no hacen daño. } } } De nuevo. le vamos a agregar un nivel más de indireccionamiento: publicstaticclass DataFactory { publicstaticIAccesoaDatos CreateInstance { get { returnObjectFactory.com . Debería de ser obvio por qué querer disminuir las dependencias entre componentes de nuestro código especialmente entre los componentes que son responsables de diferentes aspectos del sistema (interfaz de usuario. te recomiendo que cheques el artículo de DotNetSlackers al respecto.GetInstance<IAccesoaDatos>(). Fundamentos de Programación Copyright © Karl Seguin www. En el capítulo siguiente veremos lo que son las pruebas unitarias que van a incrementar los beneficios de la inyección de dependencias.Capítulo 4 Inyección de dependencias 35 supieran de nuestra implementación especifica de ID. gracias a un cambio menor. En lugar de llamar ObjectFactory de StructureMap directamente. Si estas teniendo problemas en entender lo que son la inyección de dependencias. En este capítulo Reducir el acoplamiento es una de esas cosas que es fácil de implementar y que genera grandes resultados enfocados a nuestra búsqueda por la facilidad de mantenimiento.codebetter.

Uno de los grandes beneficios de escribir pruebas para nuestro sistema es la habilidad de entregarle al cliente un producto de mejor calidad. sé que alguien en nuestro equipo escribió código que es reusable para otros proyectos. al igual que nuestros clientes. de velocidad. etc. tal vez nos rehusaríamos a hacerlos.com . A ¿Por qué no hacía pruebas de unidad hace 3 años? Para los que hemos descubierto la alegría de hacer pruebas de unidad. reorganizar el código y construir funcionalidades en las que no pensamos hace un año sin tener que preocuparnos demasiado. El punto es que puedo hacer una cambio menor o realmente fundamental en el sistema. El tener más de 700 pruebas de unidad que corren en un par de minutos. es difícil entender por qué no están haciéndolotodos.Capítulo 5 Pruebas de Unidad 36 Pruebas de Unidad ESCOGEMOS NO HACER PRUEBAS DE UNIDAD PORQUE NOS DA MÁS GANANCIAS EL NO HACERLO! . pero mucho más importante es su habilidad mágica de revelar defectos fatales o partes invaluables en el diseño del sistema. refactorizar. la principal razón por la que yo escribo pruebas de unidad es porque. Me emociono siempre que encuentro un método o funcionalidad que es increíblemente difícil de probar ya que quiere decir que probablemente he encontrado un defecto en alguna parte fundamental del sistema que pudo haber sido ignorada hasta que nos pidieran un cambio inesperado. Para los que no las han adoptado. siendo un sistema relativamente grande. la mejor forma de facilidad el mantenimiento y evolución de nuestro sistema es teniendo un set pruebas de unidad bien escritas. también he sido responsable de errores mayores causados por cambios que parecían de poco riesgo. probablemente desearías que nos calláramos. pero los detectaremos inmediatamente. De igual forma. Constantemente escuchamos a los promotores de las pruebas de unidad hablar de qué tanta confianza les ha dado tener este tipo de pruebas. y eso es exactamente de lo que se trata. Las pruebas de unidad no sólo son para mitigar el riesgo de cambios peligrosos. deberían existir cambios que nos aterroricen. Si bien esto es cierto también para las pruebas de unidad. Pero sabemos. nuestros cambios pueden introducir errores. cuando escribo una prueba en un par de segundos para algo que creía que iba a ser difícil de probar. ¿Es posible hacerlo? ¿Tendrá efectos secundarios extraños? ¿Qué errores serán introducidos? Sin nuestro set de pruebas de unidad. estamos constantemente haciendo arreglos y mejoras al sistema (mejoras funcionales. Tenemos confianza en que nuestras pruebas de unidad están completas (pruebas completamente nuestro sistema) y sabemos que es poco probable que introduzcamos errores en nuestro ambiente de producción.(COMPAÑÍA DE CONSULTARÍA ALEATORIA) 5 lo largo de este libro hemos hablado de la importancia de la habilidad de probar nuestro código y hemos visto algunas técnicas que nos facilitan el probar nuestro sistema.codebetter. correr el set de pruebas de unidad desde el IDE y en menos de 2 minutos saber en dónde estoy parado. nos permite romper los componentes. ayudan a encontrar errores y a validar que mi código hace que lo debe hacer. No puedo enfatizar demasiado en la importancia de las pruebas de unidad. que los cambios riesgosos son los que tienen mayor potencial de éxito. En mi vida de programador.). si. Fundamentos de Programación Copyright © Karl Seguin www. En un proyecto en el que estoy trabajando. Sí.

realmente no es nuestra idea de diversión. Honestamente hacer pruebas de unidad SÍ toma bastante tiempo (especialmente cuando empiezas). Como ya lo dije. 4. pero realmente es acerca de facilitar el hacer cambios y mantener el sistema.Capítulo 5 Pruebas de Unidad 37 Por muchos años he leído blogs y platicado con colegas que realmente estaban involucrados en hacer pruebas de unidad. Si estás interesado en saber más sobre el tema. pero ahora creo que es sólo una excusa que utilizan los malos programadores.codebetter. Al final de todo. A veces. sugiero que identifiques las partes más críticas de tu código y las pruebes lo más posible. 2. las pruebas de unidad mejoran la calidad de un sistema. capturando datos y asegurándose que todos esté bien. Tal vez los desarrolladores no son buenos en encontrar errores en su propio código. estas son las razones por lo que me tomó tanto tiempo subirme al tren: 1. hacer pruebas de unidad parecía algo complicado y misterioso que era usado únicamente en proyectos más avanzados. Probar el sistema es el proceso tanto de encontrar errores como de validar que el sistema hace lo que debe hacer. Tenía un malentendido acerca del objetivo de las pruebas de unidad. pero los beneficios se notaron casi inmediatamente. Parafraseando a Scott Bellware. solía pensar que los desarrolladores no deben escribir las pruebas. Pero hacer pruebas de unidad es programar. Como muchos. En retrospectiva. En estas situaciones. Los promotores te dirán que hacer pruebas de unidad no toma tiempo. Además. Estar sentado frente al monitor. Es posible que no tengas suficiente tiempo para hacer las pruebas de unidad o que el cliente no sienta que el costo inicial se justifique. es poco comparado con el tiempo que te ahorrarás en modificaciones y corrección de errores. es un poco mundano. un par de horas escribiendo las pruebas de unidad pueden tener un gran impacto. si sigues en ese camino y adoptas el siguiente paso lógico utilizando Desarrollo Guiado por las Pruebas (Test DrivenDevelopment o TDD) las pruebas de unidad realmente se convierten en diseño. lo que quiere decir que hay muchas métricas y parámetros para medir tu éxito. TDD no es acerca de probar el sistema porque no estamos pensando como probadores del sistema cuando hacemos TDD. el beneficio parecía inalcanzable y los tiempos no parecían permitirlo. estamos pensando como diseñadores de software.com . pero a fin de cuentas no es diferente al tipo de programación que haces cada día. simplemente están siendo irresponsables. los programadores que no creen que deban probar su propio código. pero yo no lo hacía. No sé la historia detrás de esta creencia. 3. Fundamentos de Programación Copyright © Karl Seguin www. Esto es cierto en el sentido que el tiempo que pasas escribiendo las pruebas de unidad. Resulta que tomó mucha práctica (me costó trabajo aprender qué probar y cómo hacerlo). como programar. te sugiero que investigues sobre pruebas de aceptación (acceptancetesting y FitNesse). Eso me parece un poco descabellado. Aunque las pruebas de unidad no sean exclusivamente acerca de probar el sistema. AHORRA tiempo. Toma tiempo. Probar un sistema no es divertido. pero son los que mejor pueden asegurarse que el sistema hace lo que creemos que hace y los clientes son los que mejor se pueden asegurarse de que trabaja como debería.

es algo que hago típicamente) Hay dos cosas que debes saber de nUnit. sólo necesitamos añadir dos frameworks y una herramienta más para tener nuestra todo lo necesario para hacer pruebas de unidad: nUnit.com . Primero. Por ejemplo si mi capa de dominio está localizada en CodeBetter. nUnit Lo primero que debemos hacer es añadir la referencia a nunit.Mocks. nUnit tiene su propio ejecutor de pruebas.cs y añade[assembly: InternalsVisibleTo(³CodeBetter. Esto es como se vería una clase de prueba: using NUnit. El atributo TestFixtureAttribute es aplicado a la clase que contiene las pruebas y métodos de inicialización y finalización. Me gusta crear un nuevo proyecto llamado CodeBetter. lo que era limitado y nos tomó tiempo. el atributo TearDownAttribute es aplicado al método que quieras que se ejecute después de cada prueba. En .0+ podemos usar el atributo InternalsVisibleToAttribute para permitir al proyecto de pruebas acceder a los métodos con visibilidad interna (abre el Properties/AssemblyInfo.Framework.NET. pero estos 4 son los más importantes.codebetter.dll.Capítulo 5 Pruebas de Unidad 38 Las Herramientas Con StructureMap ya configurado del capítulo anterior. Existen otros atributos. RhinoMocks nos generará automáticamente las clases mock basadas en una interfaz y nos permitirá verificar y controlar la interacción entre el objeto que estamos probando y estos objetos mock. RhinoMocks y TestDriven. nUnit es el framework de pruebas que estaremos usando. RhinoMocks es mockingframework que estaremos usando para crear objetos falsos.NET es una extensión (add-in) de ejecución de pruebas para Visual Studio que añade la opción Run Test en el menú de contexto (botón derecho). El atributo SetupAttribute es aplicado al método que quieres que se ejecute para cada prueba (no siempre necesitarás esto). La licencia personal de TestDriven.Foundations.Foundations.framework. se configuran las pruebas utilizando atributos. TestDriven. Pero no te preocupes si el licenciamiento no te parece. sólo que no está integrado en VS.Tests.NET es válida únicamente para proyecto de código abierto (open source) y para uso de evaluación.Foundations. pero yo no sé tanto de estas como debería. y finalmente el atributo TestAttribute es aplicado a las pruebas en sí.Tests´)].NET (Usuarios de Resharper también pueden utilizar la funcionalidad incluida). En el capítulo anterior creamos nuestros mocks manualmente. pero no perderemos tiempo hablando de eso. Mi preferencia personal es poner las pruebas de unidad en su propio proyecto.NET 2.dll y a Rhino. [TestFixture] publicclass CarTests { [SetUp] publicvoid SetUp() { //todo } Fundamentos de Programación Copyright © Karl Seguin www. El inconveniente de esto es que no podremos probar métodos privados (más de esto en un momento). Existen otras alternativas como mbUnit.

Assert.IsTrue. Assert. MathUtility. como por ejemplo Assert. MathUtility.IsFalse. Assert. La segunda cosa que hay que saber de nUnit.MaxValue.IsInstanceOfTypey otros más. Assert. pero la clase Asserttiene más de una función.MaxValue)).17)).AreEqual(10. MathUtility. } [Test] publicvoid MathUtilityReturnsValueWhenPassedOneValue() { Assert.Add(10.codebetter.IsNull. Assert. es que para confirmar que tu prueba se ejecutó como se esperaba utilizas la clase Assert y sus métodos.2.AreEqual(29.Capítulo 5 Pruebas de Unidad 39 [TearDown] publicvoid TearDown(){ //todo } [Test] publicvoid SaveThrowsExceptionWhenInvalid(){ [Test] publicvoid SaveCallsDataAccessAndSetsId(){ //more tests } //todo } //todo } Como puedes ver. MathUtility. } [Test] publicvoid MathUtilityReturnsValueWhenPassedMultipleValues() { Assert. y ya que las prueba nunca deben hacer demasiado. Assert.AreSame. nuestra prueba de unidad de vería así: [Test] publicvoid MathUtilityReturnsZeroWhenNoParameters() { Assert.AreEqual(-2.IsNotNull. Fundamentos de Programación Copyright © Karl Seguin www. } No se ve en el ejemplo anterior.Add()).Add(10)). pero si tuviéramos un método que tomara como parámetro un paramint[] numbers y regresara la suma de los números.com . Assert.Add(int.AreNotEqual.AreEqual(0. } [Test] publicvoid MathUtilityWrapsOnOverflow() { Assert. Ya sé que estos no es correcto.Greater. int. rara vez tendrás nombres excesivamente largos. cada prueba tiene un nombre muy explícito. es importante expresar exactamente lo que la prueba va a hacer.

Capítulo 5 Pruebas de Unidad 40 ¿Qué es una prueba de unidad? Una prueba de unidad es un método que prueba el comportamiento de una parte del código a un nivel muy granular. Creo que la razón más importante para no probar métodos privados es que nuestro objetivo no es probar métodos o líneas de código. tienden a permitir que el alcance de sus pruebas crezca demasiado. Ya que queremos mantener nuestras pruebas lo más granular y ligeras posible (las pruebas deben poder ejecutarse rápidamente y muy seguido para que tengamos retroalimentación instantánea). debes empezar a probar funcionalidades simples. Empezaba con pruebas que creaban un objeto. Mocking Para empezar a utilizar pruebas de unidad. Ya hablamos de la importancia de esconder la información. por ejemplo. llamando a nuestro método Save de nuestra capa de datos y asignando el identificador y llamando el método Update de nuestra capa de datos. Esto nos lleva al tema de probar métodos privados. Pero siempre decía. Si googleas esto encontrarás varias discusiones al respecto. Otro argumento en contra de probar métodos privados es que rompe el encapsulamiento de tu clase. realmente no queremos tener que inicializar una base de datos con Fundamentos de Programación Copyright © Karl Seguin www. Esto es algo que siempre debes recordar. más bien haríamos una prueba para cada uno de los funcionalidades o comportamientos esperados que contiene nuestro método: fallando cuando el objeto está en un estado inválido. pero rápidamente querrás probar métodos más complejos que tienen dependencias con otros componentes (como con la base de datos).codebetter. pero el consenso general parece ser que no se deben probar los métodos privados directamente. Si escribiéramos pruebas para un método Save de una clase Car. La mayoría de las pruebas de unidad siguen el mismo patrón: ejecutar código del sistema y asegurarse que se comporte como debe. por qué no añadir unos cuantos asserts más para asegurarme que estos campos tengan los valores que deben tener . porque un cambio en el código podía romper varias pruebas que no tenían relación con el cambio definitivamente esto es una señal de que no has enfocado tus pruebas lo suficiente. Estoy seguro que algunos pensarán que 4 pruebas para cubrir el métodoMathUtility. es posible que algunos cambios en la implementación de la clase romperán nuestras pruebas. si probamos métodos privados directamente. ejecutaban alguno de sus métodos y se aseguraban que funcionara como quería. y los métodos privados contienen detalles de implementación que queremos poder cambiar sin romper el código que utiliza nuestra clase. no escribiríamos una prueba que abarcara todo el funcionamiento del método. pero.Add es algo excesivo. Es importante que nuestras pruebas sean lo suficientemente granulares para identificar alguna falla con precisión. Esto es muy peligroso. Los desarrolladores que son nuevos en pruebas de unidad. ya que estoy aquí.com . Podrán pensar que estas 4 pruebas podrían ser agrupadas en una sola (y para este caso podría decir que es como ustedes prefieran). cuando empecé a hacer pruebas de unidad. El objetivo de las pruebas de unidad es validar un comportamiento específico. los métodos privados deberían también estar probándose automáticamente. sino el comportamiento de una parte del código. querrás añadir pruebas para completar la cobertura del método Save de nuestra clase Car. caí en el mal hábito de dejar que el alcance de mis pruebas creciera demasiado. Si pruebas exhaustivamente la interfaz pública de tu código. lo que no ayuda a tener código que sea fácil de mantener.

Esto.CreateMock<IDataAccess>(). junto con la facilidad de uso.com . En realidad lo que queremos asegurarnos es que el método Save interactúe de forma correcta con nuestra capa de datos. Si nuestro método Save funciona correctamente.Save().cs. mocks. empieza en modo de registro. Antes de empezar. car. es exactamente el problema que resuelve RhinoMocks.codebetter. Car car = newCar(). nuestra capa de datos funciona correctamente y los dos interactúan correctamente. ObjectFactory. Usar esta herramienta es muy sencillo. Estábamos usando una clase falsa omock creada manualmente que tenía algunas limitaciones importantes. después podremos añadir otras pruebas para nuestra capa de datos.InjectStub(typeof(IDataAccess). le dices que es lo que quieres representar (una interfaz o clase. lo que quiere decir que todas la operaciones que se le Fundamentos de Programación Copyright © Karl Seguin www. necesitamos darle acceso a RhinoMocks a nuestros tipos internos.ResetDefaults(). lo que tomó una línea. dataAccess. La más significativa era nuestra inhabilidad para confirmar que las llamadas a nuestro objeto mock ocurrieran como esperábamos. y le pides que verifique que estas expectativas se cumplieron.VerifyAll(). En el capítulo anterior. IDataAccess dataAccess = mocks. Ahora podemos empezar a programar nuestra prueba que cubre el camino de actualización de nuestro método Save cuando ya existe el Car en la base de datos: [TestFixture] publicclass CarTest { [Test] publicvoid SaveCarCallsUpdateWhenAlreadyExistingCar() { MockRepository mocks = newMockRepository(). ObjectFactory. dataAccess). car. le dices que métodos y con qué parámetros esperas que sean llamados.Capítulo 5 Pruebas de Unidad 41 datos falsos y asegurarnos que se mantenga en un estado predecible entre prueba y prueba. Cuando se crea un mockutilizando RhinoMocks. mocks. vimos el principio de probar con mocks.Update(car).Id = 32. lo inyectamos en nuestro framework de inyección de dependencias (StructureMap en este caso).ReplayAll(). } } Una vez que el objeto mock ha sido creado. preferentemente una interfaz). tendremos una buena parte del camino avanzado para hacer pruebas más tradicionales. Esto se hace añadiendo [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] a nuestro archivoProperties/AssemblyInfo.

mocks. RhinoMocks se asegurará de que la llamada se comportó como esperábamos.VerifyAll().Call(dataAccess. dataAccess). Cuando llamamos VerifyAll después de llamar Save en nuestro objeto Car.codebetter. Para el caso cuando el Car no existe en la base de datos.Update(car). car. IDataAccess dataAccess = mocks.Update(car).CreateMock<IDataAccess>(). Expect. dataAccess. mocks.CreateMock<IDataAccess>(). Car car = new Car(). ya que dataAcces es simplemente un objeto mock que no tiene ninguna implementación real).Id = 32.InjectStub(typeof(IDataAccess). Estaes la prueba: [Test] publicvoid SaveCarCallsSaveWhenNew() { MockRepository mocks = newMockRepository(). tenemos que asegurarnos que el valor que regresa el método Save del objeto dataAccess. ObjectFactory.Update): [Test] publicvoid SaveCarCallsUpdateWhenAlreadyExistingCar() { MockRepository mocks = newMockRepository(). Salimos del modo de registro llamando el método ReplayAll.InjectStub(typeof(IDataAccess). es manejado correctamente. En otras palabras. pero sólo una ocurrió.com .Capítulo 5 Pruebas de Unidad 42 hagan. ObjectFactory. la interacción con el método Save de la capa de datos es un poco más compleja. car. } Nuestra prueba falla con un mensaje de RhinoMocks que nos dice que esperábamos dos llamadas al método Update. Fundamentos de Programación Copyright © Karl Seguin www. dataAccess).Save(car)). lo que le dice a RhinoMocks que estamos listos para ejecutar el código real y verificarlo contra la secuencia que registramos.Update(car).Save(). lo que hacemos antes de la llamada a ReplayAll es declarar nuestras expectativas.ResetDefaults(). lo que hacemos después es el código que ejecuta la prueba contra nuestro objeto real y al final llamamos a VerifyAllpara verificar que las expectativas se cumplieron. Podemos probar todo esto forzando a nuestra prueba a que falle (noten la llamada extra a dataAccess. como llamar al dataAccess. Car car = newCar(). será registrado por RhinoMocks (realmente no sucede nada. ObjectFactory.Return(389). dataAccess.ReplayAll(). IDataAccess dataAccess = mocks.

_mocks. Assert.Save().codebetter.VerifyAll().ReplayAll().Capítulo 5 Pruebas de Unidad 43 mocks.Call nos permite especificar el valor que queremos regresar cuando llamen a este método. ObjectFactory. } } Fundamentos de Programación Copyright © Karl Seguin www.Save(car)). privateIDataAccess _dataAccess.Call(_dataAccess. car.Id).Return(0). } Utilizando el método Expect. [SetUp] publicvoid SetUp() { _mocks = new MockRepository(). nuestra prueba se vería así: [TestFixture] publicclass CarTest { privateMockRepository _mocks. ObjectFactory. } [Test. _dataAccess = _mocks. car.CreateMock<IDataAccess>().PersistenceException")] publicvoid SaveCarCallsSaveWhenNew() { Car car = newCar().com .ResetDefaults().VerifyAll(). ExpectedException("CodeBetter.Equal para verificar que lo que regresamos como Id desde la capa de datos es lo que se asignó al objeto Car y así validar que la interacción entre los dos objetos es correcta.Save(). } [TearDown] publicvoid TearDown() { _mocks. car. También añadimos una llamada a Assert. Expect. Si cambiáramos nuestro método Save para que genere una excepción si el Id que se regresa del dataAccess es inválido. mocks.Foundations. Espero que las posibilidades de controlar el valor que regresa el método (así como los valores output/ref) te muestren lo fácil que es probar los casos extremos.ReplayAll().InjectStub(typeof(IDataAccess). _dataAccess).AreEqual(389.

NET es complicado y sufre de un acoplamiento excesivo entre sus diferentes clases. El principio para desarrolladores que utilizan TDD es rojo verde refactorizar. TDD se trata de diseño y no de pruebas. Muchas veces necesitarás un HTTPContext real. TDD va muy bien con Diseño Guiado por el Dominio (DomainDrivenDesign). Generalmente. TDD quiere decir que escribes la prueba primero y después escribes el código para hacer que la prueba pase.NET probablemente no valga la pena. entonces necesitas pensar en añadir más pruebas para cubrirlo. Les recomiendo que primero se familiaricen con pruebas de unidad en general antes de entrar en TDD. no me gusta utilizar la métrica de cobertura para medir que tan completo es mi set de pruebas. crear varios objetos mock pero únicamente registrar y verificar sobre uno de ellos o hacer mocks de algunos métodos de la clase y no de otros (mocks parciales). Más de nUnit y RhinoMocks Hasta ahora hemos visto la funcionalidad básica que ofrecen nUnit y RhinoMocks. Más bien. En mi experiencia.codebetter. inicializa y verifica el objeto mock a los métodos SetUp y TearDown. Hemos mencionado el Desarrollo Guiado por las Pruebas (Test DrivenDevelopment o TDD) brevemente en este libro. lo que requiere de mucho trabajo para inicializar. Es decir. Si haces mucho uso de HttpHandlers propios. En TDD habríamos escrito la prueba para el método Save antes de tener cualquier funcionalidad en el método en sí. el que una línea o método haya sido ejecutada no quiere decir que la prueba es correcta. Si nuestro cliente tienen muchos problemas para mantener las dependencias entre las actualizaciones del sistema. Combinado con una utilería como NCover. ya que nos hace concentrarnos en las reglas de negocio del sistema. nos enfocamos en escribir pruebas que definan el comportamiento y el API de la funcionalidad específica. Lo que quiere decir que el primer paso es tener una prueba que falle. Básicamente. entonces escribiríamos la funcionalidad específica para hacer que la prueba pase. la métrica de cobertura de dice qué porcentaje del código de un assembly/namespace/clase/método fue ejecutado con tus pruebas. el ejecutar una línea de código no quiere decir que realmente la probaste. Pruebas de la interfaz de usuario y la base de datos Añadir pruebas de unidad a tus páginas de ASP. también sacamos el código repetitivo que crea. Por ejemplo. RhinoMocks puede ser configurado para ignorar el orden en que se llaman los métodos. después hacerla pasar y después modificar el código como sea requerido. Después de todo. pero si una línea de código o método no ha sido ejecutado. para lo que me gusta usar NCover es para identificar el código que no he probado. Claro que la prueba fallaría.Capítulo 5 Pruebas de Unidad Ademásde mostrar 44 cómo podemos probar una excepción (utilizando el atributo ExpectedException). NCover tiene un visor de código que resalta en rojo las líneas que no fueron ejecutadas por las pruebas. pero hay mucho más que se puede hacer con estas herramientas. El framework de ASP. Fundamentos de Programación Copyright © Karl Seguin www. Como mencioné antes. deberías poder probar esos como cualquier otra clase (claro dependiendo de que hagas exactamente en esas clases).com . también puedes obtener reportes de la cobertura de tus pruebas.

Este tema quizá sea para un artículo futuro en mi blog. más desacoplado y en general más fácil de trabajar con él. El mejor consejo que les puedo dar es empezar con algo pequeño. Seguro que mis primeras pruebas no fueron las mejores (a veces escribía pruebas que no tenían ningún sentido. aprendí mucho de qué funcionaba y qué no. pero mi forma de hacerlos es mantener todos mis CREATE Table / CREATE Sprocs en archivos de texto con mi proyecto. y por supuesto. no esperes a terminar el proyecto para hacer pruebas de unidad. Puede ser que haya mejores métodos.Capítulo 5 Pruebas de Unidad 45 Por otro lado. Una cosa que me quedó clara inmediatamente. fue qué tan limpio y claro quedaba mi código. También me di cuenta que si algo era difícil de probar. En este capítulo Hacer pruebas de unidad no fue tan difícil como me imaginé en un principio. crear una base de datos de prueba y utilizar los métodos SetUp y TearDown de mi clase de prueba para crear y mantener la base de datos en un estado conocido en cada prueba. como probar que una propiedad de una clase funcionaba como debía) y otras veces eran demasiado complejas y se salían del alcance definido. al reescribirlo para que fuera posible probarlo. quedaba mucho más legible. no tener miedo a fallar y aprender de tus errores.codebetter. por lo que por ahora se los dejo a su creatividad.com . probar la capa de datos es posible y lo recomendaría. ¡escríbelas conforme escribas el código! . Fundamentos de Programación Copyright © Karl Seguin www. experimentar con diferentes técnicas. pero después de mi primer proyecto.

Agregar más objetos y funcionalidad sobrecargaría nuestro Capa de Acceso a Datos (DAL) en una enorme violación inmanejable del principio que dicta No te repitas a ti mismo (DRY. por sus siglas en inglés). no preocuparnos con cuestiones de interconexión (y si te hace sentir mejor. suena como que será algo lento. debido a que cuando la gente escucha SQL en línea. //todo } Fundamentos de Programación Copyright © Karl Seguin www. Este debate ha sido pobremente soportado. especialmente debido a que probablemente imaginaste que se tendría que usar SQL en línea. algo como esto: string sql = @"SELECT UserId FROM Users WHERE UserName ='" + userName + "' AND Password = '" + password + "'". Específicamente veremos el popular marco de trabajo de código abierto llamado NHibernate.Capítulo 6 . Este enfoque resultó ser más bien limitado y requirió una cantidad significativa de código repetitivo (aunque fue útil para demostrar las bases). es el problema de la persistencia. Básicamente se te pedirá que cambies tu conocimiento de un método probado por algo que parece de un poco mágico. tienes que admitir que podría ahorrarte mucho tiempo y tener como resultado un número mucho menor de defectos. Puede ser requerida algo de fe ciega.Object Relational Mappers 46 ObjectRelationalMappers LA OTRA OPCIÓN DISPONIBLE A LA ESCRITURA DE MUCHO SQL CHRIS KOCH 6 n el capítulo 3 hicimos nuestro primer esfuerzo para unir el mundo de los objetos con los datos mediante la escritura a mano de nuestra propia capa de acceso a datos y su definición de conversión. una buena definición de conversiones entre estructuras relacionales y objetos te proveerá formas sencillas de desactivar la generación automatizada de código y ejecutar tu propio SQL o tus procedimientos almacenados). using (SqlCommand command = newSqlCommand(sql)) { return 0. queremos enfocarnos en construir el comportamiento. E La única y más grande barrera que impide a la gente adoptar el diseño guiado por el dominio (DDD por sus siglas en inglés). Los procedimientos almacenados A lo largo de los años. En este capítulo veremos un marco de trabajo real para la definición de conversiones entre Objetos y Entidades Relacionales (O/R Mapping) que haga todo el trabajo pesado por nosotros. Recuerda.com . inseguro e inflexible. La primer cosa con la que hay que llegar a un acuerdo es con que las definiciones de conversión generan tu SQL por ti. El debate del infame SQL en línea vs. piensan en código mal escrito. ha existido un poco de debate entre usar SQL en línea o procedimientos almacenados. Mi propia adopción de las definiciones de conversión entre estructuras relacionales y los objetos (O/R Mappers) inicio con gran confusión y duda. Pero sí puedes quitarte esos miedos de tu mente por un segundo. lo sé.codebetter.

Capítulo 6 - Object Relational Mappers

47

Por supuesto que formulado de esta manera, el SQL en línea realmente apesta. Sin embargo, si te detienes y lo piensas y realmente comparas manzanas con manzanas, la verdad es que ninguna de las 2 es particularmente mejor que la otra, Examinemos algunos puntos de interés.

Los procedimientos almacenados son más seguros El SQL en línea debe ser escrito usando consultas parametrizadas de la misma forma en que lo haces con los procedimientos almacenados. Por ejemplo, la forma correcta de escribir el código de arriba para eliminar un posible ataque de inyección de SQL es:
string sql = @"SELECT UserId FROM Users WHERE UserName =@UserName AND Password = @Password"; using (SqlCommand command = newSqlCommand(sql)) { command.Parameters.Add("@UserName", SqlDbType.VarChar).Value = userName; command.Parameters.Add("@Password ", SqlDbType.VarChar).Value = password; return 0; //todo }

De ahí en adelante, no hay mucha diferencia - se pueden usar vistas o configurar roles / usuarios de la base base datos con los permisos apropiados. Los procedimientos almacenados proveen abstracción al esquema subyacente Sin importar si estas usando SQL en línea o procedimientos almacenados, la pequeña porción de abstracción que puedes poner en una sentencia de SELECT es la misma. Si algún cambio substancial es realizado, tus procedimientos almacenados dejarán de funcionar y lo más probable es que tendrás que cambiar el código que los invoca para resolver este problema. Esencialmente, es el mismo código, solamente que reside en un lugar diferente, por lo tanto no puede proveer un grado más alto de abstracción. Las definiciones de conversión por otro lado, generalmente proveen una mejor abstracción ya que son configurables e implementan su propio lenguaje de consulta. Si hago un cambio, no tengo que recompilar el código

Como he dicho antes, creo que es generalmente mejor errar en el lado de la simplicidad, siempre que sea posible. Escribir un montón de tontos procedimientos almacenados para realizar cada operación de base de datos que creas que vas a necesitar, definitivamente no es a lo que yo considero simple. Ciertamente no intento descartar el uso de los procedimientos almacenados, ¿pero comenzar con procedimientos? Parece un caso bastante extremo de optimización prematuro para mí. -Jeff Atwood, codinghorror.com

En algún lugar, de alguna forma, la gente se metió en la cabeza que las compilaciones de código deben evitarse a toda costa (tal vez esto venga de los días cuando los proyectos podrían tardar días en compilar). Si cambias un procedimiento almacenado, aun tendrás que ejecutar nuevamente tus pruebas

Fundamentos de Programación

Copyright © Karl Seguin

www.codebetter.com

Capítulo 6 - Object Relational Mappers

48

unitarias y de integración y liberar el cambio a producción. Realmente me espanta el que los desarrolladores consideren que un cambio a un procedimiento almacenado o a un XML es algo trivial comparado con un cambio similar al código. Los procedimientos almacenados reducen el tráfico en la red ¿A quién le importa? En la mayoría de los casos tu base de datos esta soportada por una conexión GigE con tus servidores y tú no pagas ese ancho de banda. Estás hablando de fracciones de nanosegundos. Más relevante aún, una definición de conversión bien configurada puede ahorrarte viajes de ida y vuelta gracias a la identificación de las implementaciones de los mapas, el cache y la carga diferida. Los procedimientos almacenados son más rápidos Esta es la excusa más frecuentemente utilizada, Escribe una sentencia común y razonable de SQL en línea y después escribe lo mismo con un procedimiento almacenado y cronometra el tiempo de ejecución. Adelante hazlo. En la mayoría de los casos no habrá diferencia o esta será muy poca. En algunos casos los procedimientos almacenados serán más lentos ya que el plan de ejecución no será eficiente con algunos parámetros. Jeff Atwood catalogó el uso de procedimientos almacenados en busca de una mayor velocidad de ejecución como un caso extremo de optimización prematura. Tiene razón. El enfoque adecuado es tomar la estrategia más simple (permite que una herramienta genere el SQL por ti), y optimiza consultas especificas en caso de que identifiques cuellos de botella. Me tomo un tiempo, pero después de un par de años, me di cuenta que el debate entre el SQL en línea y los procedimientos almacenados era tan trivial como el que se tiene con C# y VB.NET. Si tan solo se trataba de elegir uno u otro, entonces selecciona el que prefieras y continua con tu próximo reto. Si no hay nada más que decir sobre el tema, yo escogería procedimientos almacenados. Sin embargo, cuando añades una definición de conversión Objeto/Estructura relacional a la mezcla, de forma repentina obtendrás ventajas significativas. Dejas de participar en discusiones bizantinas para simplemente decir quiero eso . Específicamente, hay tres grandes beneficios con las definiciones de conversión entre estructuras relacionales y objetos: 1.- Terminarás escribiendo mucho menos código mantenible, lo que obviamente resulta en un sistema más

2.- Obtendrás un nivel real de abstracción del origen de datos subyacente por una parte porque estarás consultando la definición de conversión para obtener los datos directamente (y esta a su vez convertirá eso en el SQL apropiado), por otra parte porque estarás proveyendo información de la definición de conversión entre los esquemas de tablas y los objetos de dominio, 3.- Tu código se vuelve más simple si tu nivel de discrepancia entre los objetos y las estructuras relacionales es bajo, escribirás mucho menos código repetitivo. Si tu nivel de discrepancia entre los objetos y las estructuras es alto no tendrás que comprometer el diseño de la base de datos y el diseño

Fundamentos de Programación

Copyright © Karl Seguin

www.codebetter.com

Capítulo 6 - Object Relational Mappers

49

del dominio puedes construir ambos de una manera optimizada, y dejar que la definición de conversión maneje la discrepancia. Al final, todo se traduce en la construcción de la solución más sencilla desde el inicio. La optimizaciones deberían dejarse para después de que el código ha sido perfilado y se han identificado los cuellos de botella reales. Como la mayoría de las cosas, podría no sonar tan sencillo por la complejidad en el aprendizaje de hacerlo por adelantado, pero esa es la realidad de nuestra profesión.

NHibernate
De los marcos de trabajo y herramientas que hemos visto hasta el momento, NHibernate es la más compleja. Esta complejidad ciertamente es algo que deberías tomar en cuanta cuando te decidas por alguna solución de persistencia, pero una vez que encuentres un proyecto que te permita algún tiempo para investigación y desarrollo, el beneficio valdrá la pena en proyectos futuros. Lo mejor acerca de NHibernate, y una de las principales metas del diseño del marco de trabajo, es que es completamente transparente - tus objetos de dominio no son forzados a heredar ninguna clase base específica y no tienes que usar un montón de atributos de decoración. Esto Recuerda que nuestro objetivo es hace posible que tu capa de dominio pueda ser sometida a ampliar nuestra base de pruebas unitarias si estas usando un mecanismo diferente de persistencia, digamos datasets con tipos definidos, el nivel de conocimientos viendo diferentes acoplamiento tan estrecho entre el dominio y los datos hace muy formas de construir sistemas con el difícil sino es que imposible el efectuar apropiadamente las fin de proveer un mayor valor a pruebas unitarias.

En capítulos previos nos enfocamos en un sistema para un concesionario de automóviles específicamente centrándonos en automóviles y actualizaciones. En este capítulo cambiaremos la perspectiva un poco y veremos la venta de automóviles (ventas, modelos y personal de ventas). El modelo de dominio es simple un vendedor(SalesPerson) tiene 0 o más Ventas(Sales) las cuales hacen referencia a un Modelo (Model)específico.2 También se incluye una solución de VS.NET que contiene código de ejemplo y anotaciones - puedes encontrar un vínculo al final de este capítulo. Todo lo que necesitas para ponerlo en marcha es crear una nueva base de datos, ejecutar la secuencia de comandos de SQL provista (un mecanismo completo para la creación de tablas), y

nuestros clientes. Mientras que podríamos hacerlo hablando específicamente de NHibernate, el objetivo es introducir el concepto de Definiciones de conversión Entidad relacional / Objeto, y tratar de corregir la fe ciega que tienen puesta los desarrolladores de .NET en los procedimientos almacenados y ADO.NET

A muy groso modo, configuras Nhibernate diciéndole como tu base de datos (tablas y columnas) hacen referencia con tus objetos de dominio, usa la API de Nhibernate y el lenguaje de consulta (HQL HibernateQueryLanguage) para comunicarte con tu base de datos, y deja que el haga el trabajo de bajo nivel con ADO.NET y SQL. Esto no solo provee separación entre la estructura de tu tabla y los objetos de dominio, sino que también desacopla tu código de la implementación para una base de datos específica.

2

Nota de traducción: para guardar compatibilidad con la aplicación de ejemplo, no se traducirán los nombres de clases, variables y demás.

Fundamentos de Programación

Copyright © Karl Seguin

www.codebetter.com

Capítulo 6 - Object Relational Mappers

50

configura la cadena de conexión. El ejemplo, y el resto de este capítulo, fueron diseñados con la intención de ayudarte a comenzar a trabajar con NHibernate un tema que frecuentemente se pasa por alto. Finalmente, encontrarás que el manual de referencia de NHibernate es de excepcional calidad, el cual es tanto una herramienta útil para empezar, como también una referencia para buscar información de temas específicos. También hay un libro que está siendo publicado por Manning, NHibernate en acción , el cual estará disponible en junio, mientras tanto puedes comprar una versión en formato electrónico previa al lanzamiento del libro.

Configuración
El secreto para la sorprendente flexibilidad de NHibernate radica en su configurabilidad. Inicialmente el proceso de configurarlo puede ser más bien desalentador, pero después de un proyecto de prueba se vuelve natural. El primer paso es configurar el propio NHibernate. La configuración más simple, que debe ser agregada a tu app.config o web.config se ve así:

<configuration> <configSections> <sectionname="hibernate-configuration" type="NHibernate.Cfg.ConfigurationSectionHandler, NHibernate" /> </configSections> <hibernate-configurationxmlns="urn:nhibernate-configuration-2.2"> <session-factory> <propertyname="hibernate.dialect"> NHibernate.Dialect.MsSql2005Dialect </property> <propertyname="hibernate.connection.provider"> NHibernate.Connection.DriverConnectionProvider </property> <propertyname="hibernate.connection.connection_string"> Server=SERVER;Initial Catalog=DB;User Id=USER;Password=PASSWORD; </property> <mappingassembly="CodeBetter.Foundations" /> </session-factory> </hibernate-configuration> </configuration>

De los 4 valores, dialect es el más interesante, este le dice a NHibernate que lenguaje específico habla nuestra base de datos. Sí, en nuestro código, le pedimos a NHibernate que regrese un resultado paginado de Cars y nuestro dialecto está configurado para SQL Server 2005, NHibernate emitirá una sentencia de SELECT utilizando la función de ranking ROW_NUMBER (). Sin embargo, si el dialecto está configurado a MySql, NHibernate emitirá el SELECT con LIMIT. En la mayoría de los casos, configurarás esto una sola vez y te olvidarás del tema. Pero esto proporciona algunos conocimientos sobre las capacidades provistas por la capa que genera todo tu código de acceso a datos. En nuestra configuración, también le dijimos a NHibernate que nuestros archivos de definición de conversión estaban localizados en el ensamblado CodeBetter.Foundations. Los archivos de definición son archivos incrustados de XML que le dicen a NHibernate como debe ser persistida cada clase. Con esta información, NHibernate es capaz de regresar un objeto de tipo Car (automóvil) cuando tú se lo

Fundamentos de Programación

Copyright © Karl Seguin

www.codebetter.com

El archivo de definición para nuestro objeto Model (Modelo). built-in GPS to always find your way back home. Name.com .Description = "Great handling.Object Relational Mappers 51 solicites. Model model = query. Con el archivo de definición de conversiones configurado. o nuestro propio algoritmo) y que no hay configurador.codebetter. llamado Model. session. se ve de esta forma: <hibernate-mappingxmlns="urn:nhibernate-mapping-2. Description. y Price.Save(model).2" assembly="CodeBetter. publicvoid Sample() { //Agreguemos un nuevo modelo de carro Model model = newModel().Foundations" namespace="CodeBetter. model.xml. La convención general es tener un archivo de definición de conversión por cada objeto de dominio.Name = ?"). Hummbee2Hummbe(tm) communication". así que deberá ingresar a través del campo con la convención de nombres especificada (proveemos Id como el nombre y la estrategia de nomenclatura con minúsculas y guión bajo (lowercase-underscore). Description y Price se refieren a las columnas Id. ISession session = _sessionFactory.Foundations"> <classname="Model" table="Models" lazy="true" proxy="Model"> <idname="Id" column="Id" type="int" access="field.SetString(0. model.lowercase-underscore"> <generatorclass="native" /> </id> <propertyname="Name" column="Name" type="string" not-null="true" length="64" /> <propertyname="Description" column="Description" type="string" not-null="true" /> <propertyname="Price" column="Price" type="double" not-null="true" /> </class> </hibernate-mapping> (Es importante asegurarse de que el parámetro BuildAction para todos los archivos de definición de conversiones se configuren como EmbeddedResources) Este archivo le dice a NHibernate que la clase Model se refiere a registros en la tabla Models. model.CreateQuery("from Model model where model.Name = "Hummbee".Capítulo 6 .UniqueResult<Model>(). Fundamentos de Programación Copyright © Karl Seguin www. así como salvarlo. y que estos se localicen dentro de la carpeta Mappings. Name.00.Price = 50000. La información extra alrededor de la propiedad Id especifica que el valor es generado por la base de datos (en contraposición a NHibernate (para soluciones residentes en clústeres o grupos de servidores). "X149"). para que use el campo llamado _id. //Hagamos un descuento al modelo x149 IQueryquery= session.hbm. podemos comenzar a interactuar con la base de datos: privatestaticISessionFactory _sessionFactory. y que las 4 propiedades Id.OpenSession().

un agente de ventas puede tener múltiples ventas.NET ignorar la necesidad de usar transacciones dentro de sus aplicaciones. extraerlos y actualizarlos todo esto sin el uso directo de ADO. Sin embargo. _sessionFactory (que implementa la interfaz ISessionFactory) es un objeto global seguro para el uso en hilos de ejecución el cual es muy probable que crees en el inicio de la aplicación.OpenSession(). puedes abrir de forma segura un objeto que implemente ISession en el método BeginRequest y cerrarlo en el método EndRequest (o mejor aún. Relaciones En nuestro sistema. la clase SalesPerson tiene una colección de Sales y la clase Sales tiene una propiedad SalesPerson (Referencia). usamos un elemento property glorificado llamado many-to-one: . la creación de un ISession no necesariamente abre una conexión. no tienes que preocuparte por tener objetos ISession alrededor por un rato (aún cuando estas no sean seguras para su procesamiento en hilos de ejecución). cargar de forma diferida en caso de que la solicitud específica no requiera un ISession). de tal forma que podamos proveer algunos reportes básicos. Tal vez te estés preguntando de donde viene el objeto _sessionFactory. y así establecer una relación uno-a-muchos esto es. Ambos extremos de la relación necesitan ser configurados en el archivo de definición de conversiones apropiado. y una venta solo puede pertenecer a un único vendedor. Esto es desafortunado ya que puede llevarnos a estados inestables e incluso irrecuperables de los datos. como con la mayoría de las fabricas de objetos. es importante que rastreemos las ventas específicamente con relación a la fuerza de ventas. Se nos ha dicho que una venta solo puede pertenecer a un vendedor. la relación está representada como una columna SalesPersonId en la tabla Sales (llave foránea). A diferencia de las conexiones que deben ser abiertas tarde y cerradas de manera temprana.NET o SQL.Update(model). es crear un objeto pre configurado: un objeto ISession no tiene equivalente en ADO. } El ejemplo de arriba muestra lo fácil que es persistir nuevos objetos a la base de datos.codebetter. <many-to-onename="SalesPerson" class="SalesPerson" Fundamentos de Programación Copyright © Karl Seguin www. ISession session = _sessionFactory. session. En nuestra base de datos. Típicamente necesitaras uno por cada base de datos que tu aplicación este usando (lo que significa que típicamente necesitaras solo uno).. pero si se relaciona con un bajo nivel de cohesión a una conexión de base de datos. La interfaz ITransaction es otra pieza del rompecabezas que es creada llamando el método BeginTransaction en un ISession. En nuestro dominio. En el extremo de Sales. Un ITransaction es usado para mantener el rastro de la unidad de trabajo rastrear que cambio. en lugar de eso. y que es exactamente un ISession.NET. que se relaciona con una propiedad única. o que ha sido agregado o borrado.com . averiguar qué y cómo aplicarlo a la base de datos.Object Relational Mappers 52 model. y su trabajo.Price -= 5000. Es común para los desarrolladores de . Si estas construyendo una aplicación ASP.. y proveer la capacidad de deshacer en caso de que un paso individual falle. el objeto ISession administra de forma inteligente los objetos de tipo conexión y comando por ti.NET.Capítulo 6 .

tu de herramientas! Puedes aprender tipo de dominio debe ser un IList (o su equivalente genérico más leyendo el artículo en el que IList<T>). También estamos especificando una limitante extra. Un conjunto es una colección que no puede contener duplicados. HashSet la cual es rápida para colecciones más grandes y HybridSet la cual usa inicialmente un ListSet y automáticamente se cambia así misma a un HashSet conforme crece tu colección. Tu primera inclinación puede ser usar una lista. publicIList<Sale> Sales { get { return _sales.5 básicamente porque la terminología de NHibernate no finalmente se ha agregado una pertenece a la jerga estándar usada en . tanto si utilizas un elemento list o bag. Siendo algo confuso tal vez. En resumen.Collection.NET no tiene una colección de tipo conjunto. la colección de ventas (Sales) que tiene un vendedor (SalesPerson). que es.. para las colecciones que uses comúnmente.Object Relational Mappers 53 column="SalesPersonId" not-null="true"/> . Extrañamente. Estamos especificando el nombre de la propiedad.codebetter.NET 3.NET. es mucho más sencillo por el momento). que cuando agregamos un nuevo objeto Sales. así que declaramos nuestra colección de Sales como un IList: privateIList<Sale> _sales. trabajo.com .} } Y agregamos nuestro elemento <bag> al archivo de definición de conversiones para SalesPersons: Fundamentos de Programación Copyright © Karl Seguin www. utiliza el elemento bag y haz tu propiedad del tipo IList. Los NHibernate ve una lista como una colección donde el índice es conjuntos son colecciones muy importante. versiones bag o array. y el nombre de la columna que es la llave foránea. la propiedad SalesPerson no puede ser nula.Capítulo 6 . Idealmente.NET entienden como un considera agregarlos a tu arsenal list..NET no cuenta con un objeto Jason Smith describe los conjuntos. La otra opción interesante de colección es el set (conjunto). .un escenario común para una aplicación empresarial (aunque rara vez se afirma explícitamente). Para nuestro sistema usaremos un objeto bag (aún cuando no podemos tener ventas duplicadas. y por lo tanto debe ser especificado. pero NHibernate requiere que tengas una columna que futuras agregaran otro tipo de especifique el índice. es un poco más complicada Con la liberación de . NHibernate lo llama una bolsa (Bag). SortedSet que puede ser ordenada. el tipo/clase. ListSet que es realmente rápida para colecciones muy pequeñas (10 elementos o menos). ¡así que mayoría de los desarrolladores de .ISet. En otras palabras. Lo que la útiles y eficientes. Existen 4 implementaciones específicas. Para configurar colección HashSet al marco de una colección usamos un elemento de tipo set. El otro lado de la relación. map. IBag. el equipo de conjuntos con un OrderedSet. así que NHibernate usa la interfaz Iesi. list. Esto es debido a que .

UniqueResult<SalesPerson>().Object Relational Mappers 54 <bagname="Sales" access="field. pero en una línea y el apellido como variable SalesPerson p = session.OpenSession().Sales) < 5"). HQL funciona a partir de la interfaz IQuery. pero perderá portabilidad al hacerlo).com . no es tan complicado como podría verse al principio.lowercase-underscore" table="Sales" inverse="true" cascade="all"> <keycolumn="SalesPersonId" /> <one-to-manyclass="Sale" /> </bag> De nuevo. where. colecciones.SetString(0. Con IQuery puedes regresar entidades individuales. lo que significa que obtienes portabilidad total lo único que necesitas hacer para dirigirse a una base de datos diferente es cambiar la configuración de su dialecto. orderby.CreateQuery("from SalesPerson person where size(person. lastName). Ambos métodos de consulta son abstracciones arriba de SQL. cambios a las ventas existentes) será automáticamente persistido.CreateQuery("from SalesPerson p where p. Consultas NHibernate soporta dos diferentes tipos de esquemas para realizar consultas: HibernateQueryLanguage (HQL) y Consultas de Criterios (también puedes realizar consultas en SQL convencional. que se crea con la invocación del método CreateQuery en tu sesión. aggregates. La actualización en cascada puede ser un buen ahorrador de tiempo conforme tu sistema crece en complejidad.List<SalesPerson>(). Identificamos el nombre de nuestra propiedad.UniqueResult<SalesPerson>(). etc. SalesPerson p = query. HQL es la forma más sencilla de las 2 ya que se parece mucho a SQL usas From. la tabla y la columna que contienen la llave foránea. escribes consultas contra tu dominio . Aquí hay algunosejemplos: string lastName = "allen". También hemos configurado el atributo cascade a all lo que significa que cuando nosotros invoquemos la actualización (update) en un objeto del tipo SalesPerson. ISession session = _sessionFactory. Fundamentos de Programación Copyright © Karl Seguin www. //gente con pocas ventas IList<SalesPerson> slackers = session. si observas a cada elemento/atributo.lo que significa que HQL Soporta los principios de la orientación a objetos tales como la herencia y el polimorfismo. //Obtener un agente de ventas por el apellido IQuery query = s.codebetter.LastName = ?"). así que hay que indicarle que use el campo con nuestra convención de nombres). parámetros substitutos y más.LastName = 'allen'"). cualquier cambio que sea hecho a su colección de ventas (Sales) (adiciones. groupby. Sin embargo en vez de consultar directamente contra tus tablas.Capítulo 6 . y el tipo/clase de los elementos en la colección. //Lo mismo que lo anterior.CreateQuery("from SalesPerson p where p. especifica la estrategia de acceso (no tenemos un configurador. remociones.

pero no queremos cargar cada propiedad / lo único que queremos es obtener el Id para poder guardarlo en la columna ModelId de la tabla Sales. salesPerson. estrategia de carga implementada por NHibernate está en las entidades en sí mismas. Por ejemplo. haciendo algo como esto: SalesPersonperson = session. Podemos invalidar este comportamiento cambiando la configuración lazy= false en el elemento bag. necesitamos especificar el modelo (Model). 46000.00). pero ningún dato será extraído de la base de datos hasta la primera vez que lo solicitas. Carga diferida Cuando cargamos un vendedor.Object Relational Mappers 55 Esta es solo una muestra de loque se puede hacer con HQL (el ejemplo que puede descargarse tiene algunos ejemplos ligeramente más complicados). DateTime.SaveOrUpdate(salesPerson). Esto es. session. la colección Sales no será cargada. por defecto. Fundamentos de Programación Copyright © Karl Seguin www. Hasta donde puede importarte. Frecuentemente querrás agregar una referencia a un objeto sin tener que cargar el objeto real desde la base de datos.Load<T> (id)NHibernate cargara un proxy del objeto actual (a menos de que especifiques lazy= false en el elemento clase).AddSales(sale). cuando agregamos una venta (Sales) a un vendedor (SalesPerson). Podemos acceder a la propiedad Sales). que no tocaremos la base de datos hasta que la información sea específicamente solicitada (ej.com . Esto hace posible escribir el siguiente código: Sale sale = newSale(session.Get<SalesPerson>(1). las colecciones son cargadas de manera diferida. más interesante.codebetter.Now. La otra.Capítulo 6 .Load<Model>(1). el proxy se comporta exactamente igual que el objeto real. Esto es porque. Cuando usas session. Sin tener que tocar siquiera la base de datos para cargar el objeto Model.

¡Estas mas allá de la mitad del camino! Espero que estés disfrutando y aprendiendo mucho. Fundamentos de Programación Copyright © Karl Seguin www.Capítulo 6 . o capacidades nativas de SQL. Es difícil abandonar el SQL escrito a mano. puedes tratar de descargarlo de esta dirección alterna: http://openmymind.Foundations. El código esta extensamente documentado para explicar varios aspectos del uso de NHibernate. ir más allá de lo que es cómodo.zip). Más allá de la herramienta de NHibernate. Este puede ser un buen momento para tomar un descanso de la lectura y poner manos a la obra con la aplicación gratuita de aprendizaje CanvasLearningApplication.Object Relational Mappers 56 Descarga Puedes descargar un proyecto con más ejemplos del uso de NHibernate en: http://codebetter. filtrado de colecciones.NET.net/CodeBetter.com . y soluciones alternativas de la limitada cesta incluida dentro de .aspx. registro de bitácoras. (Si el vínculo de arriba no funciona.codebetter. optimización del rendimiento. En este capítulo Solo hemos tocado un poco de lo que puedes hacer con NHibernate. afortunadamente es muy probable que hayas aprendido más acerca de cómo definir relaciones entre objetos y estructuras relacionales. es imposible ignorar los beneficios de las definiciones de conversión entre objetos y estructuras relacionales. No hemos visto las consultas por criterio (que es una API de consulta más íntimamente ligada con tu dominio) sus capacidades de cache.com/files/folders/codebetter_downloads/entry172562.

Tan importante como es para desarrolladores adopter varios patrones y técnicas de alto nivel. ES DEMASIADO FUNDAMENTAL. los valores en el stack son automáticamente administrados aún en un mundo sin colector de basura (como en C).Capítulo 7 De regreso a las bases: Memoria 57 De regreso a las bases: Memoria NO ENTENDER ÁLGEBRA NO ES ACEPTABLE PARA UN MATEMÁTICO. Estos son dos espacios separados asignados en la memoria de sistema que sirven un propósito distinto. Y NO ENTENDER APUNTADORES NO ES ACEPTABLE PARA PROGRAMADORES. Lo que va donde está predeterminado: tipos de valor van en el stack. Por ejemplo. enum y cualquier estructura (ya sean definidas por.La única excepción a esta regla son los tipos de valor que pertenecen a tipos de referencia por ejemplo la propiedad Idde una claseUserva en el heap junto con la instancia de la clase Usermisma. Asignación de Memoria En .) Cúmulo.NET son lenguajes administrados y que el CLR provee la recolección automática de basura. Mucha de la confusión sobre la memoria nace del hecho de que tanto C# y VB. long. es igualmente importante comprender el ecosistema en el cual su programa se ejecuta. Esta es la razón por la que un stack es sinónimo a LIFO . byte.NET o por usted) van en el stack. un marcador es puesto en el stack y 3 4 Pila (trad. En otras palabras. cada variable que se defina está almacenada en el stack3 o en el heap4.NET: NullReferneceException. nos encontramos con la memoria. todos los tipos de sistema. int.Puede pensarlo en este modo: cuando se crea un nuevo alcance.com . el CLR y el sistema operativo. Todos los programas hacen uso extensivo de la memoria del sistema e interaccionan con ella en maravillas maneras. por ejemplo un método. mientras que los tipos de referencia va en el heap.NET).last-in first-out (último en entrar primero en salir). como en muchos otros lenguajes.WARD CUNNINGHAM 7 P or más que se intente.NET.Mirando por encima de las capas provistas por el compilar de C# (o VB. es difícil ser un buen programador sin comprender esta interacción fundamental. podemos asumir que usted se ha encontrado con las siguientes excepciones . como char. aunque complementario. los lenguajes modernos de programación no pueden abstraer completamente los aspectos fundamentales de los sistemas computacionales.codebetter. El Stack Aunque estamos acostumbrados al mágico colector de basura.) Fundamentos de Programación Copyright © Karl Seguin www. .Esto es porque cuando sea que entramos a un nuevo alcance (como un método o una sentencia If) los valores son empujados al stack y cuando salen del stack los valores son liberados. StackOverflowExceptionyThreadAbortException. OutOfMemoryException. montón (trad.Esto ha causado que muchos desarrolladores asuman erróneamente que no necesitan preocuparse por la memoria.

En lugar de ello. la fragmentación de memoria puede causar todo tipo de caos. debe asegurarse también de remover del heap cuando ha terminado con él. Esto significa que ha usado todo el espacio disponible del stack. Hay muchos incidentes horribles que pueden molestar a los desarrolladores mientras trabajan con el heap.Cuando se deja ese alcance. seguramente ha oído que las cadenas son inmutables esto es. El compilador averigua cuanta memoria necesitaremos (lo cual no es tan difícil. 99. la única manera real de meterse en problemas con el stack es con StackOverflowException. parcialmente pueden sacar la vuelta a esto usando buffers internos. La mayoría de la asignación de memoria basada en el heap ocurre cuando creamos un objeto new. y creamos una nueva cadena con el valor de Hola Mundo . y varios problemas de rendimiento pueden generarse gracias a Fundamentos de Programación Copyright © Karl Seguin www.NET usa un Recolector de Basura Generacional que está brevemente descrito en la Wikipedia). esto indica una llamada recursiva interminable (una función que se llama a sí misma ad infinitum).codebetter. la mayoría son referencias profundamente anidadas de otros objetos referenciados. la misma reasignación ocurre y algún tipo de algoritmo de crecimiento es usado para determinar el nuevo tamaño (el más simple siendo antiguoTamaño * 2).Esto funciona en cualquier nivel de anidado. y cualquier cambio en el tamaño subyacente requerirá una nueva asignación. si cada carácter en una cadena toma 2 bytes. y por ello la recomendación general es usar un StringBuilderpara cualquier manipulación de cadenas significativa. En lenguajes administrados. A diferencia del stack donde el último alcance puede simplemente liberarlo. El ejemplo más sencillo es una cadena. Recolectar basura del heap es una tarea no trivial. cuando un programador causa que la memoria sea asignada al heap. En teoría esto puede ser causado por un muy muy mal diseño de sistema. Hablando de cadenas. junto con algunas colecciones. entonces el CLR necesitará asignar 22 bytes (11x2) más cualquier adicional necesitado.com .9% del tiempo.Capítulo 7 De regreso a las bases: Memoria 58 los valores son añadidos como se necesiten. Hasta que veamos la interacción entre el heap y el stack. entonces una nueva cadena se crea. Esto realmente puede tener implicaciones de rendimiento negativas. el motor en tiempo de ejecución se encarga de limpiar los recursos (. todos los valores son liberados incluyendo el marcador del método. toma un apropiado montón de memoria y regresa el apuntador a la memoria asignada (más acerca de esto en un momento). El StringBuilder. los objetos del heap no son locales a un determinado alcance. El Heap La asignación de memoria en el heap no es tan simple como el stack. aunque nunca he visto una llamada no recursiva usando todo el espacio del stack. Siempre que sea posible es buena idea especificar la capacidad inicial de dichos objetos para evitar este tipo de reasignación (el constructor para tanto el StringBuildery el ArrayList (entre muchas otras colecciones) le permiten especificar capacidad inicial). En lenguajes como en C. La verdad es que cualquier objeto almacenado en el heap es inmutable con respecto a la asignación de tamaño. Fugas de memoria no solo son posibles sino muy comunes. si se modifica esa cadena (cambiando el valor o concatenando otra cadena a ella). una vez que ha sido declarada una cadena y asignado un valor. aún con objetos con referencias anidadas). Una vez que el buffer se llena.

Los apuntadores representan el nexus del modelo de memoria de un sistema esto es.NET asigna un bloque de memoria al heap y regresa un apuntador al inicio de este bloque de memoria. Representan la verdaderamente real indirección que existe entre código y hardware. aprender sobre apuntadores en la escuela fue una experiencia dolorosa. pero esto significa que ultimadamente. Aquí una representación gráfica: Fundamentos de Programación Copyright © Karl Seguin www. el entero 5 y el apuntador a nuestra cadena. } Del código de arriba. todos los objetos heap están enraizados al stack (posiblemente a través de numerosos niveles de referencias). Esta indirección es transparente en Java o . así como también precisamente el valor en el heap. y así arbitrariamente cambiar a donde está apuntando (y seguramente hacer tronar el programa debido a esto).codebetter. Por lo tanto.Capítulo 7 De regreso a las bases: Memoria 59 comportamiento de asignación extraño o interacción con código sin administrar (lo cual . Veamos primero este ejemplo simple: staticvoid Main(string[] args) { int x = 5.com". Esto es todo lo que un apuntador es: la dirección de inicio para el bloque de memoria que contiene un objeto. terminaremos con 2 valores en el stack. es más bien tonto no entender como son usados.NET donde está el objeto mismo en memoria. cuando instancia un objeto new. Como los apuntadores son el mecanismo con el cual todos los lenguajes almacenan valores en el heap.NET hace muchodebajo del agua). pero no en C o C++ donde se puede manipular la dirección de memoria directamente con un apuntador aritmético.NET. Ellos en realidad siguen las mismas reglas descritas arriba: como enteros son almacenados en el stack al menos. los apuntadores son el mecanismo donde el stack y el heap trabajan juntos para proveer el subsistema de memoria requerido por su programa. claro.saltaron directamente a programar en un lenguaje que no los expone directamente. . Puede no ser claro aún. Donde se pone interesante es donde el apuntador está realmente almacenado. Apuntadores Para muchos desarrolladores. La verdad sin embargo es que cualquiera que diga que C# o Java son lenguajes sin apuntadores es simplemente un error. Muchos más desarrolladores nunca tuvieron la experiencia de aprender sobre ellos . En C o C++ se puede tomar un apuntador y agregar 1 a él. string y = "codebetter. un apuntador no es nada más que un número único que le dice a . Como discutimos anteriormente. generalmente representado en formato hexadecimal. La dirección no es nada más que un número único. que ellos formen parte de una referencia a un objeto y entonces estarán en el heap con el resto del objeto.com .

_subordinado. pero hemos perdido toda referencia a ella (no hay algún apuntador apuntándola). nuestro confiable recolector de basura detectará el objeto sin referencia y lo liberará.Capítulo 7 De regreso a las bases: Memoria 60 Cuando salimos de nuestra functionmain (olvidémonos del hecho de que el programa se parará). _subordinado = newEmpleado(2). } } Fundamentos de Programación Copyright © Karl Seguin www. publicint EmpleadoId { get { return _empleadoId.Gerente = _jefe. } } publicEmpleado Gerente { get { return _gerente. es básicamente el mismo. } } public class Prueba { private Empleado _subordinado. Esto es significativo porque la memoria asignada en el heap todavía contiene nuestra cadena. } } public Empleado(int empleadoId) { _empleadoId = empleadoId. privateEmpleado _gerente.com . lo que significa que tanto el valor de x como de y se perderán.codebetter. } set { _empleadoId = value. } set { _gerente = value. En C# o Java. Veremos ejemplos más complejos. que aparte de tener más flechas apuntando. En C o C++ esto resulta en una fuga de memoria sin una referencia a nuestra dirección en el heap no podemos liberarla de la memoria). nuestro stack liberará todos los valores locales. publicclass Empleado { privateint _empleadoId. void HacerAlgo() { Empleado jefe = newEmpleado(1).

Como el apuntador aritmético no está disponible en ninguno de estos lenguajes. Modelo de Memoria en la Práctica Ahora veremos al real impacto que esto tiene con nuestras aplicaciones. int primerId = (int)usuariosIds[0]. Un escenario más común donde el boxing ocurre es cuando se provee un tipo de valor a un método que acepta un objeto. no.x antes de la introducción de genéricas (generics). la variable jefe se liberará del stack. pero también lo ayudará a escribir mejores aplicaciones.Add(1). El Unboxing ocurre cuando estos tipos de valor son puestos de vuelta al stack. los apuntadores definitivamente juegan una parte importante tanto en C# como en VB. Esto es común con colecciones en . Como puede ver. La manera más simple de coaccionar un tipo de valor. los apuntadores son grandemente simplificados y con suerte fácilmente entendidos. object y = x. que está definido por el alcance padre. Esto significa que el recolector de basura no tendrá nada que limpiar porque los dos valores del heap seguirán siendo referenciados (uno directamente del stack. Tome en cuenta que entender el modelo de memoria no solo ayudará a evitar problemas. Las clases de colecciones no genéricas mayormente trabajan con el tipo de objeto. En muchos casos no notaría esta deficiencia.NET 1.NET. cuando salimos de nuestro método. usuariosIds..com . y el otro indirectamente del stack a través del objeto referenciado. pero el subordinado. usuariosIds. pero en 5 Conversiónimplícita Fundamentos de Programación Copyright © Karl Seguin www.Add(2). pero también se encargan de la deficiencia en desempeño asociados al boxing. El real beneficio de genéricas es el incremento de seguridad de tipos.codebetter. como un entero. así que el código siguiente resulta en un boxing y unboxing: ArrayList usuariosIds = newArrayList(2). en el heap es haciendo un cast5con él: int x = 5.Capítulo 7 De regreso a las bases: Memoria 61 Interesantemente. Boxing El Boxing ocurre cuando un tipo de valor (almacenado en el stack) es coaccionado en el heap.

sí que lo notará. boxing es un ejemplo primario de cómo el sistema subyacente de memoria puede tener un impacto en su aplicación. En el código de abajo vemos que tenemos dos empleados y Fundamentos de Programación Copyright © Karl Seguin www. ByRef y ByVal afectan la referencia y el tipo de valor por igual entendiendo que siempre trabajan contra el valor subyacente (que en el caso de un tipo de referencia significa que trabajan contra el apuntador y no el valor). estamos tomando el valor en el stack y duplicándolo a otra localidad del stack. es virtualmente imposible entender pasar un valor por referencia o por valor. lo que significa que una copia de contador1 es pasado a SiembraContadory los cambios hechos dentro son locales a la función. Sin importar si es algo que debería importarle. Veremos dos ejemplos. SiembraContador(ref contador2). Los desarrolladores generalmente entienden la implicación de pasar un tipo de valor.codebetter. int contador2 = 0. En otras palabras. El comportamiento de los tipos de referencia es exactamente el mismo. SiembraContador(contador1). Usando ByRef es la única situación común donde . por referencia. sin embargo no puede ser aparente al principio.WriteLine(contador1). Dado el siguiente código: publicstatic void Main() { int contador1 = 0. El primero usa una clase AdministracionPagos para cambiar las propiedades de una Empleado. ByRef Sin un buen entendimiento de apuntadores. Primero veremos como ByVal/ByRefafecta los tipos de valor. } Podemos esperar una salida de 0 precedida de 1.com . Console.Capítulo 7 De regreso a las bases: Memoria 62 algunas situaciones. } privatestaticvoid SiembraContador(refint contador) { contador = 1.WriteLine(contador2). En el Segundo caso estamos realmente pasando el valor por referencia lo que significa que ninguna copia está siendo creada y los cambios no son localizados a la funciónSiembraContador. como en las colecciones grandes. como un entero. } privatestaticvoid SiembraContador(int contador) { contador = 1. pero pocos comprenden porque pasar una referencia por referencia. La primer llamada no pasa contador1 por referencia.NET no resolverá automáticamente la indirección del apuntador (pasando por referencia o como un parámetro de salida no está permitido en Java). Console.

pero no del valor en heap. int aumento) { empleado. Console. esto parece diferente a lo que vimos con los tipos de valor. un cambio hecho en uno se ve reflejado en el otro. AdministracionPagos. a donde apunta. } En ambos casos. } } publicstaticvoid Main() { Empleado empleado1 = newEmpleado(10000). } } publicclass AdministracionPagos { publicstaticvoid DarAumento(Empleado empleado. AdministracionPagos.} set {_salario = value.com . Cuando se pasa un tipo de referencia por referencia. 2000). Console. Empleado empleado2 = newEmpleado(10000). pasamos una copia de nuestro apuntador. 2000). ¿Puede adivinar la salida? publicclass Empleado { privateint _salario.Salario). ¿cuándo realmente se pasa un tipo de referencia por referencia? La única razón para pasar por referencia es cuando se requiere modificar al apuntador mismo es decir.Salario).} } public Empleado(int salarioInicial) { _salario = salarioInicial. Esto hace preguntarnos. publicint Salario { get {return _salario. Esto puede resultar en desagradables efectos secundarios por lo Fundamentos de Programación Copyright © Karl Seguin www. int aumento) { empleado. la salida es 12000.codebetter. La única diferencia es que uno pasa el empleado por referencia mientras que el otro lo pasa por valor. } publicstaticvoid DarAumento(refEmpleado empleado.WriteLine(empleado2.Salario += aumento. A primera vista. Y como un apuntador y una copia del apuntador apuntan a la misma memoria en el heap. se está pasando el apuntador mismo en lugar de una copia del apuntador. En lugar de ello.DarAumento(empleado1.DarAumento(ref empleado2.Capítulo 7 De regreso a las bases: Memoria 63 en ambos casos estamos otorgando un aumento de $2000.WriteLine(empleado1.Salario += aumento. Lo que sucede es que pasar por referencia tipos de referencia por valor en verdad pasa una copia del valor.

Salario). Console.com . Console.Terminate(ref empleado2).Salario). En el segundo caso. Si dedujo que la llamada a empleado1.. No es muy común que se quiera cambiar la dirección apuntada por una variable desde un método separado por lo que la única vez que tal vez vea un tipo de referencia pasado por referencia es cuando quiera regresar múltiples valores desde un llamado a función (en cuyo caso sería mejor usar un Fundamentos de Programación Copyright © Karl Seguin www. publicint Salario { get {return _salario. } } publicclass AdministracionPagos { publicstaticvoid Terminate(Empleado empleado) { empleado = null. AdministracionPagos. } } publicstaticvoid Main() { Empleado empleado1 = newEmpleado(10000). Empleado empleado2 = newEmpleado(10000).codebetter. } publicstaticvoid Terminate(refEmpleado empleado) { empleado = null.Capítulo 7 De regreso a las bases: Memoria 64 cual es una buena práctica que las funciones que así lo quieran deben especificar que necesitan el parámetro pasado por referencia. publicclass Empleado { privateint _salario. AdministracionPagos. estamos pasando una copia pero con el mismo valor de stack usado por empleado2. Veamos nuestro segundo ejemplo.Terminate(empleado1). Una pista: una excepción será lanzada.} set {_salario = value. En el primer caso estamos asignando a una copia del apuntador original a null no tiene ningún impacto en lo que está apuntandoempleado1.WriteLine(empleado2.WriteLine(empleado1. } Intente averiguar qué pasará y porque. Entonces asignar al empleado a null es lo mismo que escribir empleado2 = null.} } public Empleado(int salarioInicial) { _salario = salarioInicial.Salario resultará en 10000 mientras que la segunda provocará una NullReferenceExceptionentonces está en lo cierto.

Hay dos soluciones: deregistrar de los eventos cuando hayamos terminado (el patrón IDisposable es la solución idónea). si empieza a ver OutOfMemoryException y no está usando datos inusualmente grandes. las Fundamentos de Programación Copyright © Karl Seguin www.NET tiene herramientas para ayudarlo. si ClassA (el escucha) registra un evento deClassB (la fuente del evento) una referencia es creada de ClassBa ClassA. Sin embargo. Esto es común en aplicaciones grandes con referencias profundamente anidadas. o usar un acercamiento OO más puro). y con él se irá la única manera que tenemos referencia a la memoria creada para almacenar nuestra cadena. pero repartido en una manera que lo hace inusable. Al ejecutarse su programa. Estos pueden ser difícil de identificar pues la fuga puede ser muy pequeña y la aplicación puede que no sea ejecutada por largo tiempo. el siguiente código tuviera una fuga: privatevoid HazAlgo() { string nombre = "dunas". pero seguramente querrá aprovechar un verificador de memoria comercial como dotTrace o ANTS Profiler. . fugada o no.com . en una clase. Cuando la memoria es asignada en el heap siempre es un bloque continuo.codebetter. } Nuestro valor en stack (un apuntador) será liberado. Hay una situación en específico que es conveniente mencionar como una causa común de fugas de memoria: eventos. Al estar cazando fugas de memoria estará viendo por objetos fugados (lo que es muy fácil de hacer tomando dos muestras de su memoria y compararlas). En otras palabras. Fragmentación Otra causa del OutOfMemoryException tiene que ver con la fragmentación de memoria. Bajo circunstancias normales. un tipo de fuga de memoria es todavía posible si se mantienen las referencias indefinidamente. el heap se vuelve cada vez más fragmentado (como en su disco duro) y puede terminar con mucho espacio. Fugas de Memoria Administradas Ya vimos un ejemplo de cómo se mira una fuga de memoria en C.NET porque tiene un recolector de basura que se fija en memoria sin referencia y la libera. una referencia es creada a su clase. Esto significa que la memoria disponible debe ser localizada para un bloque suficientemente grande. rastreando a través de todos los objetos que aún mantienen una referencia a ellos y corregir el problema. si C# no tuviera un recolector de basura. A menos que se deregistre del evento el ciclo de vida de sus objetos serán finalmente determinados por la fuente del evento. Al compactar memoria. Si. Básicamente. Dejándonos con ningún método de liberarla. Sin embargo. el recolector de basura compactará el heap conforme vaya liberando memoria. o usar el Patrón WeakEventouna versión simplificada. Este no es un problema en . Ultimadamente cuando su programa termina el sistema operativo reclamará toda la memoria. hay una buena posibilidad de tener una fuga de memoria. se registra un evento. El ejemplo de arriba verdaderamente señala los peligros de jugar en un ambiente donde las reglas no son comprendidas completamente.Capítulo 7 De regreso a las bases: Memoria 65 parámetro out.

Para un ejemplo y obtener más información de fijamiento. Tome como ejemplo este eficiente conversión de una cadena ASCII a entera que se ejecuta 6 veces más rápido que con int. Como muchos métodos en el . La memoria fija no puede ser compactada por el recolector de basura resultando en fragmentación.Length. i < tamanio. el uso justo de la sentencia fixed puede grandemente mejorar el rendimiento.NET se asegura de actualizar todas las referencias apropiadamente. ++i) { valor = 10 * valor + (caracteres[i] . ¿Por qué? Porque un objeto fijado puede ser manipulado directamente con aritmética de apuntador esto no es posible si el objeto no está fijado pues el recolector de basura puede reasignar su objeto en algún otro sitio de la memoria.Capítulo 7 De regreso a las bases: Memoria 66 direcciones de los objetos cambian y . Mientras que fijamiento extensivo presurisa el sistema.48). antes de interoperar primero debe fijar objetos a la memoria. Cuando el recolector de basura de . sugiero ver el apost avanzado de Greg Young acerca de fijamiento y sockets asíncronos. Sin embargo. Por lo tanto. el fijamiento puede ocurrir sin que se sepa de ello (el escenario con el que estoy familiarizado son las clases Socket .Parse.codebetter.NET Framework dependen de código no administrado. puede fijar un objeto con la sentencia fixed. Una manera común de sacar la vuelta a este tipo de fijamiento es declarar objetos grandes que no causan mucha fragmentación como los más pequeños (esto es incluso más verdadero considerando que objetos grandes son puestos en un heap especial (llamado LargeObjectHeap (LOH) que no es compactado). } } return valor.NET compacta el heap. } Fundamentos de Programación Copyright © Karl Seguin www. Por ejemplo. int tamanio = cadenaAConvertir.NET que dependen de implementaciones no administradas y buffers de pins). ¿Por qué se fijan los valores? La causa más común es porque su código está interactuando con código no administrado. algunas veces . Hay una segunda razón por la cual un objeto puede ser fijado cuando usted explícitamente lo hace. fixed(char* caracteres = cadenaAConvertir) { for (int i = 0. publicunsafestaticint Parse(string cadenaAConvertir) { int valor = 0. Fijamiento El fijamiento (pinning) ocurre cuando un objeto está anclado a una dirección específica en el heap.com . pero no tiene manera de entrar al código no administrado y hacer lo mismo. actualiza todas las referencias en el código administrado. En C# (no en VB.NET no puede mover un objeto: sobre todo cuando el objeto está fijado a una dirección de memoria específica.NET) si usted compila su ensamblado con la opción unsafe . en lugar de crear cientos de buffers de 4KB. puede crear un buffer grande y asignar pedazos de él usted mismo.

codebetter. Los beneficios de entender estos conceptos son tangibles en la programación del día a día.Parse. Todos los desarrolladores .com . No debería confiar ciegamente en este comportamiento. Para compensar. ¿debería asignar sus tipos de referencia a null cuando ha terminado con ellos? Por supuesto que no. Esto porque algunos objetos dependen de recursos vitales o limitados. es simplemente importante que entienda el rol determinístico de la finalización. si está construyendo una clase que pueda ser beneficiada de una finalización determinística encontrará que implementar el patrón IDisposable es simple. como los manejadores de archivos o conexiones de bases de datos que deben ser liberadas tan pronto como sea posible. el recolector de basura lo hará por usted (eventualmente). Puede estar preguntándose porque algunos objetos exponen métodos tanto Close y Dispose. heaps y apuntadores pueden ser abrumadores al principio. Finalmente. Esto es problemático pues no sabemos cuando el recolector de basura va a ejecutarse por naturaleza el recolector de basura solo se ejecuta cuando la memoria escasea. libera la conexión de regreso al pool para que pueda ser reusada. El código de arriba fácilmente colgarse (pase null como la cadena y vea que pasa). Si se olvida de llamar al Dispose en un objeto que implementa IDisposable. Una guía sencilla está disponible en MSDN. Dentro del contexto de los lenguajes administrados. Finalización Determinística A pesar de la presencia del recolector de basura. así como con su implementación ) la interfaz IDisposable).NET puede que sean familiares con este patrón. En este capítulo Stacks. no puede haber razón alguna para marcar su ensamblado como unsafe y tomar ventaja de la sentencia fixed.Capítulo 7 De regreso a las bases: Memoria 67 A menos que haga algo anormal. Libera recursos. No libera memoria usada por el objeto. En el caso de las conexiones con bases de datos por ejemplo. Asignar a null Así que. pues el problema de recursos limitados es muy real (es relativamente trivial intentarlo con un ciclo que abre conexiones con una base de datos). así que no ahondaremos en lo que ya sabe. En todos los casos que he visto los dos son generalmente equivalentes así que es realmente una cuestión de gusto. y en la escala de cosas a considerar es extremadamente riesgoso mientras que no provee beneficios. las clases que dependen de estos recursos pueden hacer uso del patrón Disposable. Con respecto a este capítulo. se libera del stack y la referencia es removida. tal vez necesite refactorizar su código. no hay mucho de ello realmente. Si no puede esperar a que se salga del alcance. y cuál de ellos llamar. e invaluables cuando algún comportamiento inesperado Fundamentos de Programación Copyright © Karl Seguin www. los desarrolladores deben tomar cuidado de manejar algunas de sus referencias. Personalmente la encuentro frustrante (e inconsistente) que ambas sean expuestas. no es tan rico en características como int. Sugeriría que aprovechara la sentencia using y se olvidara de Close. Una vez que la variable sale de su alcance.

Capítulo 7 De regreso a las bases: Memoria ocurre. programador que causa raros OutOfMemoryExceptions.codebetter. o el que tiene que corregirlos. .com . Puede ser el 68 NullReferenceExceptions y Fundamentos de Programación Copyright © Karl Seguin www.

¿tendrá sentido continuar como si todo estuviera bien? Seguramente no.Capítulo 8 De regreso a las bases: Excepciones 69 De regreso a las bases: Excepciones FALLA RÁPIDO . Si al convertir un valor de configuración a entero se recibe una excepción de formato. Por supuesto. si puede controlar una excepción definitivamente debe hacerlo . Si no lo hace. Sólo atrape aquellas excepciones por las cuales pueda hacer algo. así que mejor contrólelas. el cual puede llevarlo a resultados de consecuencias mucho peores. creación y lanzamiento. En este capítulo veremos tres distintos aspectos de las excepciones: manejo. ni esconderse de ellas. sino que se arriesga a poner su aplicación en un estado desconocido.NET. Por ejemplo. veamos cómo no deberíamos de tratar el IdCategoría que se pasa por una consulta en una página ASP.JIM SHORE 8 as excepciones son construcciones tan poderosas que los desarrolladores se pueden sentir agobiados y un tanto a la defensiva al lidiar con ellas.codebetter. Considerando que las excepciones son inamovibles usted no puede ni correr. sino también para errores comunes. si se emplea una base de datos de forma normal. Capturar excepciones y no controlarlas realmente se llama indigestión de excepción (prefiero llamarlo ilusiones) y es codificar mal. Usted no puede hacer nada ante la gran mayoría de las excepciones La mayoría de los desarrolladores novatos hace exactamente lo contrario a la primera regla. y 2. ¿qué puede hacer si esta se cae? Este orden de ideas no es exclusivo para bases de datos o para fallas ambientales. lo mejorar que se puede hacer es fallar en ese lugar y en ese momento. y preguntarse si realmente puede hacer algo si sucede una excepción. Si su base de datos se cae. y luchan desesperadamente contra la segunda. ¿realmente puede escribir código para recuperarla? ¿No sería mejor desplegar un amigable mensaje de error al usuario y notificar el problema? Es duro aceptarlo en un principio. En cualquier momento usted puede encontrarse escribiendo una sentencia try/catch. Fundamentos de Programación Copyright © Karl Seguin www. L Manejando Excepciones Su estrategia para manejar excepciones debería contemplar dos reglas de oro: 1. Esto es desafortunado porque las excepciones realmente representan una oportunidad clave para que desarrolladores hagan sus sistemas considerablemente más robustos. no sólo perderá información acerca de su misterioso problema. Un ejemplo que frecuentemente veo tiene que ver con la validación de las entradas.pero asegúrese de capturar únicamente el tipo de excepción que pueda controlar. Aún para sistemas de misión crítica. registrar el error y terminar. pero algunas veces es mejor colapsar. Cuando su aplicación realice algo de manera realmente excepcional y fuera de la operación normal.com .

Idealmente.QueryString["IdCategoría"]) } catch(FormatException) { IdCategoría = -1. podría centralizar su registro un algunos tipos de excepciones HttpModule de OnError es su mejor opción para una tienden a multiplicarse. se tratará del mismo modo. Si opta por aplicación ASP. } El problema con el código anterior es que independientemente del tipo de excepción que se produzca. Muchos desarrolladores aprovechan frameworks para generar registros como log4net o Microsoft´sLoggingApplication Block. try { IdCategoría = int. usted debería registrar todas y cada una de en una mala experiencia personal: ellas.sobre todo teniendo en cuenta que int.Parse(Request.Parse puede producir otros dos tipos de excepciones que nos gustaría controlar de la misma manera. Para precisar qué o agregación.QueryString["IdCategoría"]). try { IdCategría = int.0 . Una repetitivo es mejor dejar que las excepciones emerjan solución más inteligente hasta el código y registrar todas las excepciones en la implementaría algún tipo de buffer frontera de su sistema.codebetter. Esto provoca una gran cantidad de código innecesario y saturar su servidor de correo.Capítulo 8 De regreso a las bases: Excepciones 70 int IdCategoría. pero esa es otra historia).Parse(Request.com .NET 2.NET o servicio web. Registros A pesar de que no se controlen la mayoría de la Palabras de advertencia basadas excepciones. o tal vez pueda simplemente registrarla en un archivo o base de datos que revise diariamente o tal vez tenga otro proceso para enviar un resumen diario. Tal vez querrá notificar por correo electrónico tan pronto como se produzca una excepción. } (un mejor acercamiento a este problema sería utilizar la función de int. ¿Podría el valor 1 manejar una excepción de desbordamiento de memoria? En lugar del código anterior debería capturar una excepción específica: int IdCategoría. implementación de registro de excepciones utilizará deberá saber que tan críticas son.TryParse introducida en . } catch(Exception) { IdCategoría = 1. He visto a menudo a enviar correos siempre que ocurre los desarrolladores capturar excepciones donde estas una excepción podrá fácilmente producen sólo por registrarlas y volverlas a lanzar. Fundamentos de Programación Copyright © Karl Seguin www.

Dispose(). conexión. try { conexión = newSqlConnection(FROM_CONFIGURATION) comando = newSqlCommand("AlgúnSQL". comando. Las excepciones añaden complejidad a esta situación ya que su naturaleza abrupta puede causar que el método Dispose no sea llamado. conexión).ExecuteNonQuery(). Si la instrucción ExecuteNonQuery lanza una excepción. conexión.Open().Open().Dispose(). } El punto es que incluso si usted no puede controlar una excepción. } if (conexión != null) { conexión. comando.Open(). SqlCommand comando.ExecuteNonQuery().Dispose(). La solución es utilizarTry/Finally: SqlConnection conexión. ni el comando ni la conexión serán eliminados con Dispose. conexión). es necesario ser conscientes de dónde pueden surgir las excepciones .com . } } o la sentencia sintácticamente más apropiada using (que compila lo mismo): using (SqlConnection conexión = newSqlConnection(FROM_CONFIGURATION)) using (SqlCommand comando = newSqlCommand("AlgúnSQL". conexión. comando. Fundamentos de Programación Copyright © Karl Seguin www. comando. Un error al llamar a la base de datos es un ejemplo clásico: SqlConnection conexión = newSqlConnection(FROM_CONFIGURATION) SqlCommand comando = newSqlCommand("AlgúnSQL".Capítulo 8 De regreso a las bases: Excepciones 71 Limpieza En el capítulo anterior hablamos de la finalización determinista con respecto a la naturaleza perezosa del recolector de basura. y debe centralizar todos su registro.codebetter. conexión)) { conexión.especialmente en lo que se refiere a las clases que implementan IDiposable.Dispose().ExecuteNonQuery(). } finally { if (comando != null) { comando.

com . De hecho. En ocasiones necesitará redirigir una excepción porque.codebetter. necesita ejecutar código en cuanto se produzca la excepción. thrownewException("Sucede algo malo!"). Mecanismo para generarlas Usted puede tanto lanzar una nueva excepción como redirigir una excepción capturada. esto es lo que queremos el revertir nuestra transacción no es de Fundamentos de Programación Copyright © Karl Seguin www. try { transacción = session. } finally { //Limpiar } En el ejemplo anterior el sentido de la sentencia throw es hacer nuestras capturas transparente.Commit().pero la verdad es que son igual que cualquier otro objeto (excepto que heredan de System. la regla es no capture excepciones a menos que realmente las pueda controlar ).BeginTransaction(). sean o no las suyas (lo que trataremos a continuación). throw ex. generar excepciones. } throw. aún es bastante sencillo. Sin embargo.Exception.Rollback(). un controlador de la cadena de la ejecución no tendrá ninguna indicación de que capturamos una excepción. que a su vez hereda de System. simplemente cree una nueva excepción y láncela. que se basa en la instrucción throw. } catch { if (transacción != null) { transaction. Agregué el segundo ejemplo porque algunos desarrolladores piensan que las excepciones son algo especial y único . Es decir.Capítulo 8 De regreso a las bases: Excepciones 72 Lanzar Excepciones No hay una regla mágica para generar excepciones ni la hay para capturarlas (de nuevo. Para lanzar una excepción nueva. el crea una nueva excepción no significa que se tenga que lanzarla . En la mayoría de los casos. // Hacer algo transaction. //o Exception ex = newException("Sucede algo malo!"). Primero analizaremos la mecánica real de generar excepciones. El ejemplo más común es tener que deshacer una transacción cuando algo falla: ITransaction transacción = null. aunque no pueda controlar la excepción.aunque probablemente siempre lo hará. A continuación examinaremos cuándo y por qué es realmente deseable producir excepciones.Object).

codebetter. Esto casi siempre es una mala idea. el seguimiento de la pila se modifica de forma que la línea de re direccionamiento parece ser la fuente. } Al redirigir explícitamente la excepción. El primer nivel. Existen realmente dos niveles de reflexión sobre cómo se deben utilizar excepciones.una metodología que estoy adoptando más y Fundamentos de Programación Copyright © Karl Seguin www.la diferencia es sutil pero importante. ya que podría haber cambiado la API) y presentar un mensaje útil para los usuarios. sino que deberían utilizarse en cualquier situación en la que no se puede ejecutar el comportamiento esperado. Si usted encuentra en una situación donde cree que desea redirigir una excepción con el controlador como la fuente. Otro ejemplo podría ser una aplicación de Facebook que obtiene un resultado inesperado de una llamada a la API.Capítulo 8 De regreso a las bases: Excepciones 73 ayuda para nadie. Mi ejemplo favorito es el análisis de archivos de configuración. Tener código de alguien más que hizo cosas indebidas y que tiren la aplicación es una cosa. un mejor enfoque consiste en utilizar una excepción anidada: catch (HibernateException ex) { if (transaction != null) { transaction. que es universalmente aceptado. Esto está bien en algunos casos. un buen desarrollador no teme emplear inteligentemente las excepciones. Muchos desarrolladores utilizan generosamente valores predeterminados para cualquier entrada no válida. Este enfoque está relacionado con el diseño por contrato . Escribir tu propio código que haga lo mismo parece tonto. pues se pierde información vital. pero en otros puede poner el sistema en un estado poco fiable o inesperado. Usted puede pasar por alto el error. es que usted no debería dudar en plantear una excepción cuando se produce una situación realmente excepcional.Rollback(). } thrownewException("Correo electronico en uso". hay una manera de redirigir una excepción de forma que parezca que se produjo desde nuestro código: catch (HibernateException ex) { if (transaction != null) { transaction. Sin embargo.com . Cuándo Lanzar Excepciones Es importante conocer cómo lanzar excepciones. La otra creencia es que las excepciones no deberían reservarse para situaciones excepcionales. registrarla (de modo que pueda corregir.Rollback(). ex). } De esta forma la traza de la pila original es accesible mediante la propiedad InnerException expuesta por todas las excepciones. Por lo que debe tener cuidado al redirigir excepciones . Un aspecto más interesante es saber cuándo y porque debería enviarlas. Sin embargo. o podría generar una excepción. } throw ex.

En lenguajes como C#. o al menos darle una revisión. por lo que cualquier intento serio de modelar un dominio empresarial debe incluir la codificación de excepciones personalizadas. Las excepciones no deberían utilizarse para controlar una lógica como if/else. Existe también una línea que divide lo que constituye el flujo de control y lo que se considera una excepción. Esto es especialmente cierto si cree que debe utilizar excepciones siempre que un método no hace lo que dice que hará. En general. Fundamentos de Programación Copyright © Karl Seguin www. Si un estado de flujo de trabajo no es válido tiene sentido iniciar su propia excepción personalizada de WorkflowException e incluso adjuntar información específica que podría no sólo ayudarle a identificar un error potencial. pero si es tan grande el papel que desempeñan en una biblioteca. Esencialmente. Es esto lo esperado.Parse es un buen ejemplo de esto).Capítulo 8 De regreso a las bases: Excepciones 74 más cada día.el comportamiento impredecible no ayuda (si siente curiosidad de saber por qué ellos funcionan de forma diferente revise la publicación de Brad Abramsen su blog).com . probablemente los programadores deberían utilizarlas como tal (el método de int. es pensar en el usuario. este enfoque puede tener resultados diversos. Creando Excepciones Personalizadas Uno de los aspectos más olvidados del diseño dirigido por dominio son las excepciones personalizadas. debe producir una excepción. si el método de GuardarUsuario no es capaz de Guardar el usuario. sino que también podría ser de utilidad para presentar información significativa al usuario. Generalmente me hago preguntas como: Es esto excepcional.codebetter. MANTENGA PÚBLICO EL REGISTRO DETALLADO DE LOS ERRORES DE SU APLICACIÓN. La gran mayoría de los usuarios son ingenuos en comparación con los programadores y pueden fácilmente caer en pánico cuando se presentan mensajes de error. Las excepciones desempeñan un papel importante en cualquier dominio empresarial. VB. pero un diccionario produce una excepción . Una tabla Hash devuelve null cuando no se encuentra una clave.NET y Java. o al trabajar con excepciones en general. que no son compatibles con los mecanismo de diseño por contrato. me resulta fácil decidir qué debería y no debería generar una excepción. Quizás lo más importante por hacer cuando se generan excepciones. Jeff Atwood recientemente publicó en su blog la importancia de estrellarse responsablemente. Puedo continuar haciendo algo significativo en este momento y Esto es algo que debería realizarse consciente de modo que yo puedo arreglarlo. Los usuarios no deberían estar expuestos a la pantalla azul de Windows (no se piense que dado que la vara está en una posición baja está bien ser perezoso). ¡NO ES RESPONSABILIDAD DEL USUARIO AVISARLE DE LOS ERRORES! NO EXPONGA A LOS USUARIOS A LA PANTALLA AZUL DEL SISTEMA.

Text = ex. Son particularmente útiles porque permiten evitar la indigestión de excepciones. Comparo estas interfaces de marcador (o atributos de marcador). que hereda de System. Esto puede ser tan simple como un ErrorCode. es muy posible que deba hacer ajustes en sus excepciones personalizada para detener la ejecución (Recuerde. Primero (y técnicamente esto es todo lo que se necesita) crear una clase. } En el ejemplo anterior también se muestra cómo podemos extender las excepciones para proporcionar comportamientos más personalizado y específicamente relacionados con nuestras excepciones. Error. Si confía en un servicio web que devuelve un código de error.GetValidationMessage().com . } //versus try { usuario. Crear una excepción personalizada es realmente un proceso de dos pasos.es decir. no todas las excepciones están vinculadas al dominio.Capítulo 8 De regreso a las bases: Excepciones 75 Muchas de las excepciones que creo son nada más excepciones de marcado . Es común ver más excepciones orientadas a la operación.codebetter.Exception y no proporcionan nada adicional. amplían la clase base de System. Por supuesto. para información más compleja y como un PermissionException que expone el permiso del usuario y el permiso necesario que falta. no tendremos más remedio que tragarnos todas las excepciones: try { usuario.ObtenerErrores(). Error.Visible = true.Guardar(). con un nombre significativo.Guardar(). tales como la interfaz INamingContainer. } catch { Error. } catch(ValidationException ex) { Error. Si el método Guardar() no inicia una excepción personalizada cuando se produce un error en la validación.Exception.Text = usuario.Visible = true. falle rápido) y aproveche su infraestructura de registro. publicclassUpgradeException : Exception { } Fundamentos de Programación Copyright © Karl Seguin www. Tomemos el siguiente código como ejemplo.

} protectedExcepciónMejorada (SerializationInfo info.com . El propósito de la serialización es que en caso de tener propiedades personalizadas.lo que significa que también debería implementar el método GetObjectData. c) { if (info != null) { _IdMejora = info. inner) { _IdMejora = IdMejora. } } publicExcepciónMejorada (int IdMejora) { _IdMejora = IdMejora. string mensaje) : base(mensaje) { _IdMejora = IdMejora. } publicExcepciónMejorada (int IdMejora. } } publicoverridevoid GetObjectData(SerializationInfo i. } publicExcepciónMejorada (int IdMejora.AddValue("IdMejora". que desee sobrevivan a la ejecución las pueda serializar y deserializar. StreamingContext c) { if (i != null) { i. El cuarto se utiliza para admitir la serialización ya que .GetInt32("IdMejora").codebetter. A continuación se muestra el ejemplo completo: [Serializable] publicclassExcepciónMejorada : Exception { privateint _IdMejora. Exception inner) : base(mensaje. StreamingContext c) : base(info. StreamingContextcontexto) Los tres primeros permiten que la excepción se utilice en una forma esperada. publicint IdMejora { get { return _IdMejora.GetObjectData(i. } base.Capítulo 8 De regreso a las bases: Excepciones 76 El paso extra que puede aplicar es marcar su clase con SerializeAttribute y siempre ofrecer al menos cuatro constructores: y public LaExcepción() y public LaExcepción(string mensaje) y publicLaExcepción(string mensaje. c) } } Fundamentos de Programación Copyright © Karl Seguin www. string mensaje. ExceptionExcepciónReferida) y protected LaExcepción(SerializationInfoinformación.NET requiere serializar las excepciones . _IdMejora).

No se deben atrapar una excepción a menos que realmente la pueda controlar. Las excepciones no son algo que deba ser temido o de lo que debamos protegernos. Es igualmente importante hacer uso de las excepciones incorporadas y el de las excepciones propias cuando algo inesperado ocurre dentro del código. Como tal. Incluso se puede expandir este patrón para cualquier método que no hace lo que dice que hará. las excepciones no sólo son útiles para fines operacionales sino también deberían formar parte del modelado de su dominio general. Por último.Capítulo 8 De regreso a las bases: Excepciones 77 En este Capítulo Puede tardar bastante un cambio fundamental en la perspectiva para apreciar todo lo que las excepciones pueden ofrecer. Fundamentos de Programación Copyright © Karl Seguin www.codebetter. Se debe evitar la indigestión por excepciones. sino más bien deben ser tratadas como información vital sobre la salud del sistema.com . las excepciones son una parte del modelado del negocio.

incluyendo Java. La especialización no se trata exclusivamente de datos sino también de comportamiento. cambia el comportamiento al métodoto_s de la clase Song (ToString) que a su vez hereda de la clase KaraokeSong. En Java los métodos son virtuales por defecto y los desarrolladores explícitamente deben de prohibir el cambio de comportamiento u override mediante la palabra clave final) Típicamente los métodos virtuales son discutidos en el contexto de la herencia de los modelos de dominio. %s (%d)".NET). . por ejemplo: Una CanciónKaraoke hereda de Canción o un Perro hereda de Mascota." @lyrics end end P El código anteriormente mostrado ejemplifica la manera en la cual clase KaraokeSong es capaz de cambiar el comportamiento por defecto de su clase base. @artist. los métodos son virtuales por defecto. Esto es porque en muchos lenguajes. Cuando se marca un método como virtual se está permitiendo que una clase que hereda de otra pueda cambiar su comportamiento.com . @duration) end end classKaraokeSong<Song def to_s returnsuper + " . ligeramente modificado y extraído de Programming Ruby (ISBN: 978-0-9745140-5-5). Quizás te hayas dado cuenta en ese mismo código. Sin esta funcionalidad.GARY SHORT 9 ocas palabras clave son tan simples pero tan sorprendentemente poderosas como virtual en C# (overridable en VB. Esto representa una diferencia de opinión fundamental entre los diseñadores de lenguaje Java y los diseñadores de C#/VB. Estos conceptos son muy importantes pero ya están bien documentados por lo tanto examinaremos los métodos virtuales mediante un acercamiento más técnico: Los proxies Fundamentos de Programación Copyright © Karl Seguin www. PERO PARA LOGRARLA LOS PROGRAMADORES DEBEN REALMENTE DE DEJAR A OTROS PROGRAMADORES REUTILIZAR EL CÓDIGO. que el método base to_sno está marcado como virtual.se muestra a continuación classSong def to_s returnsprintf("Song: %s. En C# los métodos son finales por defecto y los desarrolladores explícitamente deben permitir el cambio de comportamiento (override mediante la palabra clave virtual).Capítulo 9 De regreso a las bases: Proxy esto y Proxy aquello 78 De regreso a las bases: Proxy Esto y Proxy Aquello UNA DE LAS BELLEZAS DE LA PROGRAMACIÓN ORIENTADA A OBJETOS ES LA REUTILIZACIÓN DE CÓDIGO A TRAVÉS DE LA HERENCIA. Un ejemplo simple. la herencia y el polimorfismo no serían de mucha utilidad.codebetter.NET. @name.

En el mundo de software el patrón de diseño de proxy es una clase que se comporta como otra clase. El cliente no tiene que saber cual fue regresado.com . Debido a que el proxy es solamente una subclase que implementa comportamiento adicional quizás te podías preguntar si un Perro es un proxy de Mascota. El servidor proxy transparentemente se comporta como el servidor real pero sin la funcionalidad adicional que tiene el servidor base que puede ser el rastreo. } publicvirtualvoid Delete() { TaskRepository. Por ejemplo si estuviéramos construyendo un sistema para hacer el rastreo de actividades podríamos decidir utilizar un proxy para aplicar transparentemente las autorizaciones a un objeto denominado Task. En el mundo de hardware un servidor proxy se coloca entre el usuario y el servidor al que realmente se está accediendo.). publicclassTask { publicstaticTask FindById(int id) { returnTaskRepository.CanDeleteTask()) { base.Delete().Capítulo 9 De regreso a las bases: Proxy esto y Proxy aquello 79 Patrón del Dominio del Proxy Un proxy es algo que se comporta como otra cosa. la autorización etc. En otras Fundamentos de Programación Copyright © Karl Seguin www.FindById(id).codebetter. } } } Gracias al polimorfismo FindById puede regresar una Task o una TaskProxy. Si lo pusiéramos en términos legales. el monitoreo o el filtrado. de una manera transparente. Los proxies tienden a implementar funciones más técnicas como el monitoreo. un proxy es alguien a quien se le da la autoridad de votar o actuar a nombre de alguien más.Current.. } } publicclassTaskProxy : Task { publicoverride void Delete() { if (User.Create()..Delete(this). } else { thrownewPermissionException(.Create(). Solo se programa a través de la API pública. de hecho tampoco necesita conocer que el objeto TaskProxy existe. el caching. Por lo tanto el proxy tiene los mismos derechos y se comporta muy parecido a la persona a la cual está representando.

si no que más bien se declararía una variable del tipo Perro.codebetter. de la misma manera que si una clase Perro implementara un método Ladrar Interception La razón por la cual estamos explorando los temas de la herencia de una manera más técnica es porque las dos herramientas que se han tratado en el contexto del libroRhinoMockseNHibernate hacen uso extensivo de los proxies aunque no sea notorio a primera vista. como heredar de una clase base o agregar atributos por todas partes) Cuando se utiliza NHibernate existen dos maneras diferentes para proveer el mecanismo de carga perezosa.com . Como consecuencia un proxy no añadiría más miembros toda vez sé que tú no estás programando en contra de su API. Esto todavía se puede considerar sin consecuencias o transparente debido a que el programador no añade elementos específicos de NHibernate a tus clases. El archivo de mapeo se vería como el siguiente: <classname="Model" table="Models"> <idname="Id" column="Id" type="int"> <generatorclass="native" /> </id> .. RhinoMocks usa un proxy es para soportar su funcionalidad base mientras que NHibernateutiliza proxies para explotar sus capacidades de carga perezosa (lazyloading). <bagname="Upgrades" table="Upgrades" lazy="true" > <keycolumn="ModelId" /> <one-to-manyclass="Upgrade" /> </bag> </class> Al asignar el atributo lazy a true en nuestro elemento bag. NHibernate puede hacerlo sencillamente debido a que regresa sus propios tipos de Colección (Todos de los cuales implementan interfaces por defecto como IList. Sin embargo para poder habilitar el mecanismo de carga perezosa todos los miembros deben de ser virtuales. El primero y el más obvio es cuando utilizamos colecciones hijas por ejemplo: Es probable que en algunas ocasiones no se quiera cargar Model'sUpgrades hasta que son necesitadas realmente. le estamos especificando a NHibrernate a que cargue perezosamente la colección Upgrades.Capítulo 9 De regreso a las bases: Proxy esto y Proxy aquello 80 palabras no se debería declarar una variable como TaskProxy.. por lo que para el programador resulta transparente) Fundamentos de Programación Copyright © Karl Seguin www. En este capítulo veremos cómo funciona NHibernate toda vez que es más fácil entender qué es lo que sucede tras bambalinas con esta tecnología sin embargo el mismo nivel de abstracción se aplica con RhinoMocks (Nota sobre NHibernate: Es un sistema de mapeo entidad relación transparente o sin consecuencias debido a que no es necesario modificar las clases del dominio para que funcione.

Save(sale).Price = 25000.Save(sale).Load <SalesPerson>(1). y por mucho.Model = session.. supongamos entonces que se ha realizado una venta (Sale).SalesPerson = session. ¿Por qué? Bueno. sale. Desafortunadamente tuvimos que hacer una conexión a la base de datos dos veces para los objetos SalesPerson y Modelaunque realmente no son usados. sale. estamos listos: Sale sale = newSale(). Realmente lo único que necesitamos es su ID (toda vez que es lo que se inserta en la base de datos).SalesPerson = session. Lo primero que se debe hacer es cambiar nuestro mapeo y permitir la carga perezosa para los modelos Models y SalesPeoples <classname="Model" table="Models" lazy="true" proxy="Model">. que ya tenemos. Si alguno no lo está NHibernate una excepción con una lista de métodos no virtuales.Get<Model>(2).com . sale. session.</class> El atributo proxy le dice a NHibernate qué tipo debe de ser utilizado con el proxy. Una vez dicho esto. session. Este tipo puede ser la clase misma a la cual se están mapeando.Get<SalesPerson>(1).codebetter.</class> <classname="SalesPerson" table="SalesPeople" lazy="true" proxy="SalesPerson ">. se utiliza la carga perezosa para los objetos individuales de dominio. Fundamentos de Programación Copyright © Karl Seguin www. La idea en concreto es que algunas veces será necesario que los objetos enteros sean cargados diferidamente. Las ventas están asociadas con una persona de ventas (SalesPerson) y un modelo de carro.Load<Model>(2).Model = session. o una interface implementada por la clase Dado que estamos usando la misma clase que nuestra interface de proxy. más interesante. Al momento de crear un proxy NHibernate nos permite cargar a un objeto de una manera perezosa solamente para este tipo de circunstancias.Capítulo 9 De regreso a las bases: Proxy esto y Proxy aquello 81 El segundo. Sale sale = newSale().Price = 25000. sale... sale.. sale. necesitamos asegurarnos de que todos los miembros están marcados como virtuales.

La mejor manera de examinar qué es lo que está sucediendo es añadir un par de puntos de interrupción al momento de ejecutar el código fuente y revisar qué es lo que está sucediendo dentro de la base de datos a través de la herramienta SQL Profiler o el log de NHibernate. Desde luego para que esto funcione se debe de generar una interface prototipo y miembros virtuales de las clases En éste capítulo En el capítulo 6 cubrimos brevemente las capacidades de carga perezosa NHibernate. El proxy tendrá un ID de 2 debido a que ya se le proporcionó el valor y todas las demás propiedades están sin y inicializar Cualquier llamada a otro miembro de nuestro proxy como sale. En su lugar el llamar Session. Aun me encuentro impresionado con la funcionalidad que se proveen en RhinoMock e NHibernate gracias al patrón de diseño del proxy. Desde luego que todo se basa en permitirles cambiar o inyectar su comportamiento en las clases de tu dominio. Esperamos que este capítulo te haga ahondar en la discusión de que métodos deberían de ser virtuales y cuáles no. . Esperamos también que te puedas imaginar cómo funcionan los proxies utilizados por RhinoMockspara grabar datos y para generar interacciones al momento de que se crea un objeto realmente está creando un Proxy. con este código ya no estamos yendo hacia la base de datos para cargas los IDs.Load<Model>(2)regresa un proxy dinámicamente generado por NHibernate. Fundamentos de Programación Copyright © Karl Seguin www. Se recomienda fuertemente que visites las ligas que se han provisto en la primera página de este capítulo para que puedas entender los pros y los contras de los métodos virtuales y finales. En este capítulo expandimos la discusión para llegar a más a las implementaciones a reales.codebetter. esto es porque su funcionalidad de realmente inspecciona los valores de los objetos al ser inspeccionados.Capítulo 9 De regreso a las bases: Proxy esto y Proxy aquello 82 En el código anterior podemos ver que estamos utilizando Load en lugar de Get. La diferencia entre ambos es que al utilizar una clase que soportar carga perezosa el método Load obtendrá el proxy mientras que el método Get obtendrá el objeto real. El uso de proxies es tan común que no solamente te encontrarás con ellos sino que es muy probable que tengas una buena razón para implementarlos tú mismo. Un objeto real es del proxy intercepta todas las llamadas y dependiendo en qué estado se encuentre as de su propia funcionalidad realiza su tarea.com .Model.Name será transparentemente interceptado y el objeto será cargado en tiempo de ejecución desde la base de datos Una nota adicional es que la capacidad de carga perezosa NHibernate puede ser difícil de monitorear al momento de hacer una depuración dentro de Visual Studio.

como yo. pero debe ser minimizado. Código mal acoplado será imposible de probar y será llanamente obvio. Sin embargo. Es por eso que es importante tener un entendimiento sólido de los fundamentos de buen diseño de software. pero. dado que leído a lo largo de todo esto. hay mejores razones para hacerlo. programar es un trabajo de retos y disfrutable que paga los recibos. Tomo con mucho orgullo y placer que construir algo que sobresale a mi nivel de calidad y aprender de las partes que necesitan ser mejoradas. aquí están mis principios esenciales de una buena ingeniería de software: La solución más flexible es la más simple.Resumiendo 83 Resumiendo TENDEMOS A LIGAR NUESTRA CONFIANZA EN NOSOTROS MISMOS FUERTEMENTE CON LA CALIDAD DEL PRODUCTO QUE PRODUCIMOS NO LA CANTIDAD DEL PRODUCTO. y nuestro trabajo duro empieza. La búsqueda de la perfección debe ser suficiente razón para escribir pruebas. a mostrar debilidades. Probar ayuda a encontrar cosas raras en su API. Nuevas características aquí o un malentendido allá.TOM DEMARCO& TIMOTHY LISTER (PEOPLEWARE) P y ara muchos. Probar ayuda a documentar comportamiento y expectativas. Creo fuertemente que la ruta para dominar bajo acoplamiento es por las pruebas unitarias. o una capa de otra capa. Lo más dependiente que una clase sea de otra clase. Viviría en un sueño si requiriera especificaciones firmadas antes de empezar a desarrollar. Usted es responsable (y de ser posible el que rinda cuentas) del código que escribe. Tome buena parte de su tiempo aprendiendo. pero a un nivel alto. Logramos cubrir muchos de los detalles reales de implementación. será más difícil que su código cambie.codebetter. No es fácil construir software de calidad. Cuestione el status quo y siempre esté al acecho de alternativas. Mis gozos serían de poco tiempo sin una colaboración del cliente constante. Es una labor artesanal. DRY. y pensar que un método o clase hace lo que se supone no es suficiente. Aprenda diferentes lenguajes y diferentes marcos de trabajo. Para muestra de este entendimiento está enYAGNI. La noción de que los desarrolladores no deberían probar ha sido la anti bala de plata de nuestro tiempo. hay una posibilidad de que. El acoplamiento no es evitable. Puedo identificar el principio de mi camino en ser mejor programador a hace unos años atrás cuando estaba muy envuelto en código fuente para un proyecto open source. Probar permite hacer cambios pequeños pero radicales con mayor confianza y mucho menos riesgo. Aprendiendo Ruby y Rails me ha hecho mucho mejor programador. Renunciaría a esta carrera sin desarrollos iterativos. He trabajado en proyectos cuya flexibilidad está construida desde el principio en el sistema. tratando de hallar sentido en él. Es posible construir software exitoso sin ser Ágil pero no será con tanta certeza y mucho menos divertido. Probar ayuda a identificar malos acoplamientos.com . PERO LA CALIDAD. y y y y Fundamentos de Programación Copyright © Karl Seguin www. y lo que crea significa más para usted que lo que cualquiera no programador pueda entender. aunque levemente. Siempre ha sido un desastre. KISSyclaridad. programar es algo más importante para usted.

Fundamentos de Programación Copyright © Karl Seguin www.com(en serio).codebetter. Y si usted tiene la oportunidad de aprender de un mentor o de un proyecto. si no está divirtiéndose. o algo significativo por el medio ambiente. Sinceramente espero que encuentre algo valioso aquí.Si está dispuesto a tomar su tiempo y tratarlo.com . Si lo desea. algún gesto amable a un extraño. tómela aunque usted aprenda más de equivocaciones que de éxitos. Y todavía no entiendo la mitad de lo que mis pares bloggean enCodeBetter. puede agradecerme haciendo algo bueno para alguien especial. Soy muy lento para aprender. Más importantemente. no lo haga. verá el progreso. más me doy cuenta de lo poco que sé. Recuerde descargar gratuitamente la CanvasLearningApplicationpara una mirada más a detalle de las ideas y herramientas representadas en este libro.Resumiendo 84 Mi último consejo es no se dé por vencido. y mientras más aprendo. Escoja un simple proyecto para invertirle un fin de semana construyéndolo usando las nuevas herramientas y principios (sólo elija uno o dos a la vez).