Rubén Bernárdez
This user hasn't shared any biographical information
Posts by Rubén Bernárdez
Refactoring Screencast – Limiting the red
19 Nov 11
El otro día, mi buen amigo Carlos Blé me invitó a participar en uno de sus screencast sobre refactoring y estuvimos haciendo un poco de pair programming juntos por Donosti y grabándolo (gracias @programania por dejarnos un hueco en tu casa!
).
Estuvimos practicando el “Limiting the red” que consiste en estar la mayor cantidad de tiempo con tus tests en verde mientras haces tareas en tu código.
Para ello usamos el código de su primer screencast, Visual Studio 2010, mstest y mucho Resharper para ayudarnos. Una fantástica herramienta que ayuda enórmemente en el desarrollo agilizando multitud de tareas cotidianas y ayudándonos a ganar tiempo y evitar errores de refactorización, entre otras tareas. Verás un uso intensivo de atajos de teclado y menús de Resharper para ayudarnos a realizar refactorizaciones evitando tocar manualmente el código y realizando gran parte del trabajo por nosotros.
Las técnicas usadas con Resharper, tengo que agradecérselas a Ángel Nuñez (@snahider), las cuales las aprendí gracias a sus magníficos screencast de refactoring.
La idea inicial de “Limiting de red” la vimos explicada a Joshua Kerievsky en un artículo de InfoQ llamado “The Limited Red Society“, el cuál os recomiendo ver encarecidamente.
Sin más dilaciones, te dejo con el resultado final de refactoring.
NOTA: Te recomiendo ver el vídeo en HD, pulsando en el botón a la derecha del volumen.
NuGet & FluentAssertions en acción
30 Oct 11
El otro día, hablando con mi buen amigo Carlos Blé, estuve comentándole algunas de las herramientas que uso en mi día a día para desarrollar software, y al final acabamos grabándolo pensando que sería de interés para algunos. En este screencast totalmente improvisado, hablamos principalmente sobre NuGet y la librería FluentAssertions.
NuGet es un complemento de Visual Studio 2010 para gestionar librerías de terceros de una manera automatizada. La propia extensión se encarga de realizar la descarga del paquete y de todo los pre-requisitos, su instalación y la inserción de las referencias en los ficheros de configuración web.config y app.config.
FluentAssertions es una librería que da una total expresividad a los Asserts de tus test unitarios y te puede ahorrar mucho código repetitivo en los mismos. Además, es totalmente extensible.
Si no conoces las herramientas, te recomiendo que veas este screencast en modo HD.
Te recomiendo también visitar la web de Podgramando, donde encontrarás podcast sobre desarrollo de software y metodologías ágiles la mar de interesantes.
En el vídeo cometí una errata. Comento que NuGet se instala con el propio service pack 1 de Visual Studio y no es cierto. NuGet hay que instalarlo a parte desde el administrador de extensiones del propio Visual Studio.
¿Para qué quiero test unitarios?
10 Jul 11
Estoy colaborando junto con Carlos Blé para evolucionar el framework pyDoubles que estamos desarrollando en iExpertos. He comenzado a colaborar con el código bastante avanzado, en su versión 1.1. Me ha tocado estudiar el código antes comenzar a añadirle nuevas características.
Este framework está diseñado desde el principio usando TDD y tiene una cobertura del código con test unitarios cercana al 100%.
Hace unos momentos he acabado de añadirle una nueva funcionalidad que estará disponible para la nueva versión. Se trata de que un spy pueda comprobar que se ha llamado a un método X veces. El código de un test con ello quedaría así:
def test_move_to_cell_severals_times(self):
server_proxy_spy = spy(ServerProxy())
when(server_proxy_spy.move).then_return(("OK", 10))
robot = Robot(server_proxy_spy)
robot.start(total_moves = 2)
assert_that_method(server_proxy_spy.move).was_called().times(2)
El uso de este framework es un API fluída, como puedes observar, por lo que dificulta algo más su desarrollo. Añadir esta funcionalidad nueva, me ha costado alrededor de ¡3 horas únicamente! Lo que sin test unitarios y un buen diseño podrían haber sido varios días.
Gran parte de este mérito lo tienen los test unitarios, surgidos en este caso a partir de un diseño con TDD. ¿Qué me han aportado los test unitarios?
- Me ayudaron a entender el funcionamiento de todo el framework. Sólo me tuve que leer los test y analizar sus implementaciones para comprender y saber usar todas las características de pyDoubles.
- Me sirvieron como documentación de uso cuando tenía dudas sobre cómo usar alguna característica.
- Añadí la nueva funcionalidad con la gran tranquilidad de que el resto de características seguían funcionando.
El resultado final han sido 3 horas para estudiar todo el código del framework y otras 3 horas para añadir la nueva funcionalidad. En menos de una jornada laboral he conseguido saber usar y comprender el código de pyDoubles y añadirle una nueva funcionalidad que no es trivial.
Programar usando TDD, creando sus test unitarios, al principio del proyecto cuesta más tiempo, eso es cierto. Pero:
- ¿Cuánto tiempo ahorrarás en añadir nuevas funcionalidades una vez tu proyecto empiece a tener cierto volumen? Y no hace falta mucho código para que esto ocurra.
- ¿Cuanto tiempo ahorrarás en que otro nuevo desarrollador entre a colaborar en el proyecto y tenga que estudiarse y comprender el código?
- ¿Cuanto tiempo ahorrarás en evitar cometer errores de regresión por no probar todo tu código en cada nueva funcionalidad que añadas? ¿o en cada refactorización?
- ¿Cuánta tiempo ahorrarás en probar todas y cada una de las características y condiciones que tiene tu código?
Estas cuatro preguntas han sido algunos de los grandes males que he sufrido durante años en los proyectos en los que he trabajado. Males que no te dejan hacer bien tu trabajo ya que te quitan tiempo, y todos sabemos que el tiempo el limitado en cualquier proyecto que desarrollas para tus clientes.
Desde mi punto de vista, el ahorro de tiempo es exponencial al tamaño del proyecto. Y no sólo ahorras tiempo, sino que ganas en una gran tranquilidad a la hora de añadir nueva funcionalidades o refactorizar tu código.
Para mi son todo ventajas y se han convertido en parte de mis principios a la hora de desarrollar software.
Puedes leer sobre los resultados reales que hemos tenido aplicando TDD en un post de Carlos Blé escrito recientemente.
pyDoubles v1.2 released! Hamcrest compatibility
8 Jul 11
cars_finder = stub(CarFinder())
when(cars_finder.count_by_brand).with_args(
equal_to_ignoring_case(“bmw”)).then_return(120)
Hamcrest comes with a library of useful matchers. Here are some of the most important ones:
- Core
- anything – always matches, useful if you don’t care what the object under test is
- describedAs – decorator to adding custom failure description
- is – decorator to improve readability – see “Sugar”, below
- Logical
- allOf – matches if all matchers match, short circuits (like Java &&)
- anyOf – matches if any matchers match, short circuits (like Java ||)
- not – matches if the wrapped matcher doesn’t match and vice versa
- Object
- equalTo – test object equality using Object.equals
- hasToString – test Object.toString
- instanceOf, isCompatibleType – test type
- notNullValue, nullValue – test for null
- sameInstance – test object identity
- Beans
- hasProperty – test JavaBeans properties
- Collections
- array – test an array’s elements against an array of matchers
- hasEntry, hasKey, hasValue – test a map contains an entry, key or value
- hasItem, hasItems – test a collection contains elements
- hasItemInArray – test an array contains an element
- Number
- closeTo – test floating point values are close to a given value
- greaterThan, greaterThanOrEqualTo, lessThan, lessThanOrEqualTo – test ordering
- Text
- equalToIgnoringCase – test string equality ignoring case
- equalToIgnoringWhiteSpace – test string equality ignoring differences in runs of whitespace
- containsString, endsWith, startsWith – test string matching
- A method called “matches” that returns a boolean value and it receives a parameter to be checked.
- Optionally, a constructor who gets what you need to make the match.
- Optionally, define value to “matcher_name” which prints a descriptive message of your matches in pyDoubles messages.
class IsTelephoneNumber(PyDoublesMatcher):
matcher_name = "is telephone number"
def __init__(self, format):
self.defined_arg = format
def matches(self, arg):
return self._is_telephone_number_in_format(arg, self.defined_arg)
def _is_telephone_number_in_format(self, telephone, format):
.... assume this would implement some check...
With a little more work, you can support your matcher with pyDoubles and Hamcrest. You can find more information in the Hamcrest wiki.
The currently supported version of PyHamcrest is v1.5.
Finally, the method: “assert_that” as been renamed to “assert_that_method” to avoid conflicts with Hamcrest.
You can download the latest version of pyDoubles here: https://bitbucket.org/carlosble/pydoubles/downloads/
Vivencias del AOS 2011
20 Jun 11
Ya ha terminado el Agile Open Spain 2011, que rápido ha pasado. Ahora toca centrarse en el AOS 2012 en el que espero volver a aportar mi granito de arena con una nueva charla.
Mi balance sobre la edición de este año ha sido positivo, un gran ambiente, buenas charlas a las que acudir y una ubicación inmejorable. Ha sido todo un acierto usar las instalaciones de Navarra Factori para el evento, sus amplios y diversos espacios donde conversar han ayudado mucho al networking. Personalmente, he tenido la oportunidad de desvirtualizar y charlar con mucha gente maravillosa.
PRE-AOS, caldeando el ambiente
Para caldear el ambiente, desde Biko2 preparamos un par de eventos previos al AOS. Por una parte, un Coding Dojo facilitado por mi compañero Alberto Rodríguez (@sharpbites) en el que personalmente me lo pasé genial. Constaba de iteraciones por parejas en las que, tras cada iteración, Alberto nos iba añadiendo nuevas restricciones a la hora de escribir nuestro código las cuales nos hacían estrujar nuestro celebro para resolver el problema. En esta ocasión, tocó escribir el conocido juego del “Tres en raya”. Por otra parte, hubo un debate titulado “La organización ágil” facilitado por Jon Setuain (@jonsetuain) donde se habló de la inclusión de valores y principios ágiles en la empresa.
Mi sesión
Ya en el evento, me lancé a proponer una charla sobre “stubs, spías y mocks”, la cuál no hubiera sido posible sin la inestimable ayuda de mi buen amigo Carlos Blé (@carlosble). Aunque en los momentos previos había nervios y ni si quiera me entraba el desayuno que se daba el AOS, una vez comencé con la charla, los nervios desaparecieron y la charla fluyó. Hubo un debate fantástico y enriquecedor (muchas gracias a todos los asistentes) y al final de la misma tuve la oportunidad de sortear una copia del libro “Diseño ágil con TDD” que me regaló Carlos. Os dejo en SlideShare las diapositivas de la sesión.
Mis sesiones destacadas
Aunque de todas las sesiones a las que pude ir me llevé nuevas ideas, me gustaría destacar una sesión de Rodrigo Corral (@r_corral) sobre la deuda técnica en los proyectos de software. Si todos estamos de acuerdo en que hacer bien las cosas hace que cueste menos tiempo hacer un proyecto, por qué aun así no se hacen? No cuesta menos tiempo? Qué ocurre en el día a día para que sigamos sin hacer test automatizados, aplicar los principios SOLID, DRY, SSOT, KISS, YAGNI, TDD y otras buenas prácticas? En definitiva, hacer bien nuestro trabajo. No existe una respuesta milagrosa, a lo largo de la sesión se dieron diferentes ideas y puntos de vista para intentar darles respuesta. Algunas de las ideas que me llevon son: eliminar deuda técnica NO es un coste, sino una inversión, analiza el ROI de cada mejora a implantar y comienza por las de mayor ROI, haz un plan de la “venta” interna de estas mejoras (utiliza ejemplos, hechos, analogías y estadísticas), ten principios y no los rompas bajo ningún concepto (y menos bajo presión), estate preocupado por hacer las cosas bien, persevera y nunca dejes de aprender. Me quedo también con una referencia que hizo al libro “The Art of Unix Programming” que aunque tiene ya unos añitos, tiene un contenido que merece la pena leer, aunque no programes para Unix.
Otra sesión que me gustó personalmente fue la de “Rompe la rutina” de Kini (@kinisoftware). Se hablaron de diferentes actividades que se pueden llevar a cabo en los equipos o departamentos de desarrollo para romper con el día a día, practicar, compartir conocimientos y hacer nuestro trabajo más ameno y entretenido. Algunas ideas que me llevo apuntadas son los desayunos tecnológicos donde hablar cada vez de una tecnología diferente, la tarde open source donde dedicarla a aportar a la comunidad, el viernes “aumenta tu cobertura” o “comenta tu código” donde aprovechar a mejorar el código de los proyectos existentes, hacer un open space dentro de la empresa con sesiones de los propios compañeros o una vez al año alquilar una casa rural y durante el finde hacer un proyecto express open-source. ¿Y qué se consigue a cambio? Equipos más contentos, más motivados, con mayor conocimiento y al final, más productivos.
Nuevas iniciativas
Y no sólo surgen sesiones en el AOS, sino también nuevas iniciativas. Para empezar, se acaba de lanzar la primera quedada del futuro grupo local ágil en Navarra, promovido inicialmente por Alberto Rodriguez. Será el próximo miércoles 22 de junio a las 19h en el Restaurante Erreleku. No faltéis!
Emma (@hell03610) también lanzó otra fántástica iniciativa que tiene que ver con el internship entre empresas. Se tiene pensando crear un portal donde las empresas podrán ofrecerse para hacer este intercambio de trabajadores por unos días y así beneficiarse mutuamente del conocimiento, experiencia e ideas de los que participen. Estad atentos a su twitter para futuras noticias!
Desde Agile Spain, se ha creado una página con referencias a todo lo referente a esta edición del evento, no olvides visitarla.
Y para finalizar, dar las gracias a Agile Spain, a todos los voluntarios y a los patrocinadores que han hecho posible este gran evento. ¡Nos vemos el próximo año!
Coding Dojo en Huesca el próximo 8 de Abril
26 Mar 11
Vuelve la diversión a Walqa en Abril con el 2º Coding Dojo organizado por los chicos de Frogtek y del que tengo el privilegio de ser el maestro de ceremonias
La kata escogida para la ocasión es la String Calculator. Una kata que personalmente me gusta para practicar TDD ya que comienza siendo muy sencilla y se va complicando por momentos.
El Coding Dojo es apto para todos los desarrolladores, independientemente de su nivel de destreza desarrollando. Si que es importante conocer de antemano conceptos básicos sobre TDD y un framework de testeo unitario (jUnit para Java, nUnit para C#, PHPUnit para PHP, etc.), ya que se darán por sentados esos conocimientos.
Si no conoces TDD, te recomiendo encarecidamente que leas el libro “Diseño Ágil con TDD” de mi buen amigo Carlos Blé, el autor principal. No tiene desperdicio alguno y es gratuito para su descarga!
Si quieres pasar un buen rato conociendo gente y practicando TDD, refactoring y buenas prácticas sólo tienes que inscribirte por correo electrónico. Tienes todos los detalles en el blog de Frogtek. El aforo está limitado a 35 personas, así que ¡apresúrate a apuntarte para no quedarte sin plaza!
El enunciado de la kata “String Calculator” es el siguiente:
1) Create a simple String calculator with a method int Add(string numbers)
1. The method can take 0, 1 or 2 numbers, and will return their sum (for an empty string it will return 0) for example “” or “1” or “1,2”
2. Start with the simplest test case of an empty string and move to 1 and two numbers
3. Remember to solve things as simply as possible so that you force yourself to write tests you did not think about
4. Remember to refactor after each passing test
2) Allow the Add method to handle an unknown amount of numbers
3) Allow the Add method to handle new lines between numbers (instead of commas).
1. the following input is ok: “1\n2,3” (will equal 6)
2. the following input is NOT ok: “1,\n” (not need to prove it - just clarifying)
4) Support different delimiters
1. to change a delimiter, the beginning of the string will contain a separate line that looks like this: “//[delimiter]\n[numbers…]” for example “//;\n1;2” should return three where the default delimiter is ‘;’ .
2. the first line is optional. all existing scenarios should still be supported
5) Calling Add with a negative number will throw an exception “negatives not allowed” - and the negative that was passed.if there are multiple negatives, show all of them in the exception message
6) Numbers bigger than 1000 should be ignored, so adding 2 + 1001 = 2
7) Delimiters can be of any length with the following format: “//[delimiter]\n” for example: “//[***]\n1***2***3” should return 6
8.) Allow multiple delimiters like this: “//[delim1][delim2]\n” for example “//[*][%]\n1*2%3” should return 6.
9) make sure you can also handle multiple delimiters with length longer than one char
¡Nos vemos en Huesca!
¡Pomodorízate! Experiencias con la técnica del pomodoro
3 Mar 11
¿Has estado alguna vez solucionando multitud de tareas y problemas no parando ni un momento pero quedarte al final del día con la sensación de no haber avanzado nada? Yo muchos días… Intentando averiguar los motivos de esta situación llegué a la conclusión de que no conseguía estar centrado en una tarea más de unos pocos minutos y mientras tanto respondía a un par de correos electrónicos que me acababan de llegar, a la llamada telefónica de un cliente, a un compañero que te pregunta una duda, al messenger que te ha mandado la chica de administración, revisando el twitter, etc. En mi equipo de trabajo teníamos la costumbre de interrumpirnos constantemente para preguntar dudas o comentar cualquier tontería sin darnos cuenta del daño que eso produce en la concentración de la otra persona.
A raíz de que este problema saliera en una de nuestras retrospectivas, nos pusimos como objetivo el no interrumpirnos entre nosotros a lo largo del día y el no dejar que nos interrumpieran. Evidentemente, no puedes pasarte al lado extremo y olvidarte del mundo durante toda la jornada laboral. Así que nos decidimos a probar la técnica del Pomodoro.
Es una técnica realmente simple, pero que llevada a la práctica con constancia y siguiendo una simples normas son ha dando resultados fantásticos!
Las reglas
Para darle forma a la técnica, definimos las siguientes normas:
- El pomodoro tendrá una duración de 25 min. después de los cuales se deberá descansar durante 2-3 min. Durante ese tiempo se deberá intentar dejar la mente en blanco y levantarte de la silla para estirar las piernas.
- Después de 4-5 pomodoros, el descanso será más largo, de unos 15 min.
- Durante el pomodoro deberás tener cerrado el messenger, twitter, facebook, correo y cualquier otro sistema de comunicación con el mundo exterior. Se deberá de estar centrado en la tarea a ejecutar.
- Después del descanso del pomodoro se revisará el chat de empresa para comprobar si algún compañero te necesita. Si es así, aprovecha a ayudarle antes de comenzar el siguiente pomodoro o coméntale cuándo podrás hacerlo.
A la gente que necesita de tu ayuda o presencia no le costará acostumbrarse a tus pomodoros ya que cualquier tema que te requiera puede esperar una media 10-15 min. para que lo atiendas. Rara vez hay tanta urgencia como para que necesiten romperte el pomodoro. A no ser que seas de sistemas
Trucos para coger el hábito
Un técnica como la de los pomodoros cuesta trabajo adquirirla como hábito. Al principio, cuando te encuentras agusto o emocionado trabajando no te apetece parar después de 25 min. y tu mente se resigna a ello. Muchas veces alguien te viene a interrumpir e inconscientemente te dejas interrumpir haciéndole caso inmediatamente y por ello perdiendo la concentración. En otras ocasiones te pones a trabajar sin acordarte de hacer pomodoros.
Para ayudar a adquirir el hábito, nos inventamos un par de trucos que nos han funcionado muy bien. Voy a exponer el problema que nos encontramos y cómo lo solucionamos.
Problema 1 – Interrupciones frecuentes
La gente que venía a interrumpirnos no sabía que estábamos en mitad de un pomodoro y directamente nos asaltaban con la duda, pregunta o comentario.
Solución: Mientras estamos en mitad de un pomodoro, dejamos levantada una figura, token o banderita encima de nuestra CPU o mesa dejando ver que no se nos pueden molestar. Este funcionamiento se lo explicamos a nuestros compañeros para que lo conozcan.
Resultado: El Product Owner dejó de asaltarnos en cualquier momento. El gerente de cuentas más de lo mismo (milagro!!!
). Los compañeros te mandaban mensajes instantáneos avisándote de que les fueras a ver en cuanto terminaras el pomodoro. En definitiva, 25 min. de paz y tranquilidad para concentrarse en una tarea.
Importante: Bajar la figura que avisa de tu pomodoro cuando no estás haciendo pomodoros o estás en el descanso, de lo contrario los compañeros no lo toman en serio.
Problema 2 – No se realizan los descansos
Pasaban los 25 min., la alarma te avisa pero la ignoras y sigues con la tarea sin descansar ni hacer caso a tus compañeros.
Solución: Nos construímos un elemento visual y radiador de información para llevar un control de los pomodoros. Cada vez que alguien termina un pomodoro, se levanta, va al panel de scrum y en la hoja de seguimiento apuntaba una ‘X’ en su fila de pomodoros. El objetivo de esto último no es controlar quien hace o no pomodoros o cuantos realiza cada uno al día. Lo que se busca es obligarte a levantarte de la silla y así realizar el descanso y desconectar de la tarea en la que estabas sumergido. Por otra parte puedes ver tu progresión diaria de pomodoros y darte cuenta de ello.
Resultado: Nos ayudó muchísimo a obligarnos a realizar el descanso. Al principio la gente se extrañó muchísimo de vernos todo el día paseando por los pasillos y ventanas de la oficina cada pocos minutos
Beneficios que nos aportó
Después de estar unas iteraciones aplicando e interiorizando la técnica de pomodoros, nos dimos cuenta de que nos aportó un buen conjunto de beneficios:
- El día se nos hacía más ameno y corto. Dejar que tu mente descanse cada 25 min. hace que reduzcas la tensión mental que producen tareas de codificación las cuales exigen mucha actividad mental.
- Genera un bioritmo de trabajo. El tener tiempos de concentración continuos de una duración constante te genera un ritmo de trabajo que el cuerpo agradece. Incluso ayuda a estar más activo durante el pomodoro.
- Ayuda darte cuenta rápidamente de los bloqueos en tus tareas. Si después de un pomodoro te quedas con la sensación de que no has avanzado nada, es un buen momento para analizar qué está ocurriendo y si deberías pedir ayuda. Sin los pomodoros, lo que me llegaba a ocurrir es que podría llegar a estar horas bloqueado en una tarea sin ser muy consciente de ello y perder gran parte de la jornada laboral marchándome a casa frustado por la situación.
- Aumenta tu concentración. Al no recibir interrupciones y al hacer descansos periódicos, tu mente tiene más energía para conseguir una mayor concentración durante el pomodoro.
Otras buenas prácticas que complementan los pomodoros
Si bien la técnica del pomodoro te da un gran conjunto de beneficios, desde hace unas semanas estoy comenzando a aplicar otras buenas prácticas que se las he oído comentar a Enrique Comba (@ecomba) en más de una ocasión y que son un complemento perfecto para organizarse mejor:
- Al comienzo de la jornada laboral, de todo lo que tienes por hacer decide qué tareas vas a abordar y en cuántos pomodoros vas a resolverlas. Conseguirás ponerte una meta clara, aprenderás a estimar cuánto te cuesta hacer las cosas y podrás detectar rápidamente si te desvías de tu objetivo y actuar en consecuencia.
- Al comienzo de cada pomodoro piensa en qué pequeño problema quieres abordar y cómo piensas hacerlo. Piensa una estrategia.
- Al final de la jornada laboral, reflexiona sobre cómo te ha ido el día y aprende de tus errores para no volver a cometerlos. Tómatelo como una mini-retrospectiva individual.
- Si te es posible, no leas el correo electrónico a lo largo del día, prográmate un par de pomodoros al día para responder a comunicaciones entrantes. Uno al final de la mañana y otro al final de la tarde, por ejemplo.
Herramientas para los pomodoros
Siempre puedes usar la alarma de móvil para que te avise del fin del pomodoro, pero si buscas algo más de sofisticación ahí van un par de herramientas que uso:
- Focus Booster.
Es una aplicación programada en Adobe Air para usar en tu escritorio. También tiene su versión on-line. - Pomodoro Addicted.
Es una aplicación para Android para llevar el control de la duración y cantidad de tus pomodoros.
Introducción con ejemplos a Castle Windsor, un contenedor de inversión de control para .NET (parte 3)
12 Jan 11
- Introducción con ejemplos a Castle Windsor (parte 1)
- Introducción con ejemplos a Castle Windsor (parte 2)
En este post finalizaré la introducción a Castle Windsor, al final encontrarás un enlace para poder descargarte el código fuente de los ejemplos.
Activators para la creación de componentes
Los Activators son los encargados de crear y destruir componentes dentro de un contenedor. Windsor ya trae implementado un Activator genérico, pero si necesitamos controlar la creación de componentes, podemos crearnos uno propio. La forma de asociar un Activator a un componente es la siguiente:
[Fact]
public void Registrar_y_resolver_clases_creadas_por_activador_y_lifestyle_transient()
{
_container.Register(
Component.For<IDao>().Activator<BasicDaoActivator>().LifeStyle.Transient);
IDao dao1 = _container.Resolve<IDao>();
IDao dao2 = _container.Resolve<IDao>();
Assert.IsType(typeof(BasicDao), dao1);
Assert.NotEqual(dao1, dao2);
}
Cada vez que el contenedor necesite crear un nuevo componente para le servicio IDao, creará una nueva instancia de BasicDaoActivator y la usará.
La implementación de BasicDaoActivator, quedaría:
public class BasicDaoActivator : AbstractComponentActivator
{
public BasicDaoActivator(
ComponentModel model,
IKernel kernel,
ComponentInstanceDelegate onCreation,
ComponentInstanceDelegate onDestruction) :
base(model, kernel, onCreation, onDestruction)
{
}
protected override object InternalCreate(Castle.MicroKernel.Context.CreationContext context)
{
return new BasicDao();
}
protected override void InternalDestroy(object instance)
{
//No hacemos nada en especial
}
}
El método InternalCreate sería el encargado de crear nuevos objetos y el método InternalDestroy de hacer algo con ellos para eliminarlos, no tiene más misterio. Lo único a tener en cuenta es que el método InternalCreate debe de devolver siempre una nueva instancia. No debemos de usarlo para intentar implementar un LifeStyle nuevo. Para ello, existe la posibilidad de crearnos un LifeStyle personalizado.
Nuestro Activator debe de heredar de AbstarctComponenteActivator y debemos de llamar al constructor del padre.
Factorías de componentes
Otra de las características que nos ofrece Castle Windsor es la de implementar nuestras propias factorías de componentes. Pueden ser usadas para registrar cosas como un HttpContext en el contenedor. Son muy útiles cuando quieres exponer como servicios componentes que no tienen accesible un constructor o que símplemente no se pueden instanciar. En el siguiente ejemplo, tenemos una clase Configuracion con un constructor privado y una propiedad estática que obtiene la instancia de la clase:
public class Configuracion
{
private static Configuracion _instance = new Configuracion();
public static Configuracion Instance
{
get { return _instance; }
}
private Configuracion(){}
}
La forma de crear nuestra factoría asociada a un componente es:
[Fact]
public void Registrar_y_resolver_objeto_de_constructor_privado_obtenido_por_factoria()
{
_container.Register(
Component.For<Configuracion>().UsingFactoryMethod(kernel => Configuracion.Instance));
Configuracion configuracion = _container.Resolve<Configuracion>();
Assert.NotNull(configuracion);
}
Así de simple, usamos el método UsingFactoryMethod y como parámetro un delegado que obtenga el componente. Usando el FactoryMethod para un componente, nos salimos del pipeline de resolución de componentes estándar del contenedor, por lo que ya no tenemos disponible el LifeTime, los Handlers o los Activators para este componente, ya que dejan de tener sentido.
Puedes encontrar más información en la documentación oficial de las factorías.
Extiende el funcionamiento de Windsor mediante Facilities
Por si fuera poco, Windsor es totalmente expandible a nuevas funcionalidades a partir de lo que denomina Facilities.
De base, trae un conjunto de útiles Facilities para diferentes entornos de trabajo, como puede ser la integración con NHibernate, WCF, log4net, NLog, ActiveRecord, MonoRail, ASP.NET MVC, Rhino Service Bus, WCF, Quartz.Net, Rhino Security, SolrSharp, y algunas más. También trae otras Facilities para crear objeto de factorías automáticamente, de sincronización, eventos, etc.
Puedes encontrar más información en la documentación oficial de las Facilities.
Logging Facility, gestiona los objetos de tu sistema de registro de logs
Una de las Facilities que trae de fábrica posibilita inyectar un Logger en tus componentes. Incluye integración con log4net y NLog. Si usas otra librería para generar tus logs, como puede ser System.Diagnostics, puedes implementarte tu propia clase y usarla con Logging Facility para inyectarla en tus componentes.
Veamos cómo inyectar log4net en tus componentes. Te hará falta referenciar un par de nuevos ensamblados: Castle.Facilities.Logging.dll y Castle.Services.Logging.Log4netIntegration.dll
[Fact]
public void Configuramos_facilidad_de_logueo()
{
_container.AddFacility<LoggingFacility>(
f => f.LogUsing(LoggerImplementation.Log4net).WithConfig("log4net.config"));
_container.Register(Component.For<IDao>().ImplementedBy<BasicDao>());
IDao dao = _container.Resolve<IDao>();
Assert.NotNull(dao.Logger);
}
Añadimos la Facility de Logging a través del método AddFacility del contenedor. Es este caso le indicamos que queremos usar Log4net.
La implementación del componente es la clase BasicDao tiene el siguiente aspecto:
public class BasicDao : DaoBase, IDao
{
public BasicDao(ILogger logger):base(logger)
{
Logger.Debug("Instancia de BasicDao creada");
}
public BasicDao(){}
}
La forma de usar el Logger es simplemente llamar a los métodos de la propiedad Logger, así de simple.
Y por último, la case DaoBase:
public class DaoBase
{
public ILogger Logger { get; set; }
public DaoBase() {}
public DaoBase(ILogger logger)
{
Logger = logger;
}
}
El objeto que implementa la interfaz ILogger (en este caso el de log4net) es inyectada automáticamente en BasicDao por el contenedor.
Una recomendación, para no tener que estar revisando en cada momento si la propiedad Logger es null o no a la hora loguear, puedes inicializarla a NullLogger.Instance; De esta forma, si no se inyecta una clase de Logging, la propiedad Logger no será null.
Puedes encontrar más información en la documentación oficial de Logging Facility.
Conclusión
Castle Windsor dispone aún de más características que no he detallado, para conocerlas puedes consultar la documentación oficial.
Windsor, nos permite hacer un uso muy simple de la inversión de control, pero no por ello deja de cubrir las necesidades más avanzadas e incluso las no implementadas dándonos opción a expandir este fantástico framework.
Como producto lleva varios años en el mercado, ha tenido una continuación constante en el tiempo sacando varias nuevas versiones cada año y es open source, por lo que puedes bucear por su código y conocer cómo se han resuelto cada una de las características que ofrece.
los desarrolladores en Silverlight estáis de enhorabuena ya que es una plataforma soportada.
Espero que esta introducción te haya valido para conocer un poco más sobre este framework de inversion de control.
Si quieres que profundice más en alguna de sus características, puedes pedírmelo mediante un comentario.
Código fuente de los ejemplos
Puedes descargarte el código fuente de los ejemplos: Rbp.Spike_.CastleWindsor.zip (1.24MB)
La solución tiene formato de VisualStudio 2010.
Introducción con ejemplos a Castle Windsor, un contenedor de inversión de control para .NET (parte 2)
9 Jan 11
- Introducción con ejemplos a Castle Windsor (parte 1)
- Introducción con ejemplos a Castle Windsor (parte 3)
Constructores en los componentes
Los componentes pueden tener constructores con parámetros, es la forma habitual de asociar las dependencias (o colaboradores) en un componente. Pero, además de los colaboradores, puedes necesitar una cadena de conexión o cualquier otro valor que no sea una dependencia. Para configurar los parámetros del constructor de un componente, lo hacemos de la siguiente forma:
[Fact]
public void Resolver_componente_con_parametros()
{
RegistrarContenedorUnoPorUno();
IRepository repositorio1 = _container.Resolve<IRepository>(new { nombre = "Uno" });
IRepository repositorio2 = _container.Resolve<IRepository>(new Arguments().Insert<string>("Dos"));
Assert.Equal("Uno", repositorio1.NombreInterno);
Assert.Equal("Dos", repositorio2.NombreInterno);
}
En este ejemplo, el componente que implementa la interfaz IRepository tiene en su constructor un parámetro de tipo string llamado “nombre“. A la hora de resolverlo, el valor de este parámetro es facilitado a partir del parámetro del método Resolve. Hay varias formas de pasar el parámetro. Para la variable repositorio1, generamos una clase anónima con una propiedad que debe llamarse igual que el parámetro del constructor (nombre en este caso). En cambio, para el repositorio2, sólo definimos que es un parámetro de tipo string y su valor. Internamente buscará el constructor con más parámetros que se ajuste a los argumentos facilitados. Si fueran dos los posibles constructores que pudieran usarse, Windsor no te asegura cuál es el constructor que usará.
Registrar varios componentes de un mismo servicio
¿Y si quiero tener el mismo servicio implementado por dos componentes diferentes? ¿o por el mismo componente pero con inicialización diferente? Para ello, deberás dar nombre a cada registro, fíjate:
[Fact]
public void Registrar_dos_componentes_del_mismo_tipo_con_inicializacion_y_nombre_diferente()
{
_container.Register(
Component
.For<IRepository>()
.ImplementedBy<ClientRepository>()
.DependsOn(new { nombre = "Uno" }).Named("Uno"),
Component
.For<IRepository>()
.ImplementedBy<ClientRepository>()
.DependsOn(Property.ForKey<string>().Eq("Dos")).Named("Dos"),
Component.For<IDao>().ImplementedBy<BasicDao>());
IRepository repositorio1 = _container.Resolve<IRepository>("Uno");
IRepository repositorio2 = _container.Resolve<IRepository>("Dos");
Assert.Equal("Uno", repositorio1.NombreInterno);
Assert.Equal("Dos", repositorio2.NombreInterno);
}
Registramos dos componentes del mismo servicio. Gracias al método Named les damos un nombre diferente a cada uno. A la hora de resolver el componente del servicio, simplemente le facilitamos el nombre de componente como primer parámetro. De esta forma, sabe a qué componente se refiere. Si no le facilitaríamos el nombre del componente, lo que nos devolvería sería el primer componente registrado de ese servicio.
Aquí también he añadido una novedad, el uso del método DependsOn. Lo que nos permite es pasar los valores de los parámetro del método Resolve como parámetros del constructor del componente. De esta forma, ya vienen predefinidos en el contenedor sin tener que facilitarlos en la llamada al método Resolve del contenedor.
Igual que antes, en el primer registro, uso una clase anónima y en el segundo registro uso la clase Property que nos permite sólo definir el tipo y valor del parámetro, sin tener que fijar el nombre.
Parámetros dinámicos en los constructores de los componentes
Castle Windsor nos da la posibilidad de definir parámetros para los constructores de los componentes que se “calculen” en el momento de crear la instancia, veamos un ejemplo:
[Fact]
public void Registrar_con_parametro_dinamico()
{
DateTime fecha = DateTime.Now;
_container.Register(
Component
.For<IRepository>()
.ImplementedBy<ClientRepository>()
.DynamicParameters((k, d) => d["Fecha"] = fecha));
IRepository repositorio = _container.Resolve<IRepository>();
Assert.Equal(fecha, repositorio.Fecha);
}
Usando el método DynamicParameters, le estamos definiendo un delegado con el código que deseamos que se ejecute en el momento de crear una instancia del componente registrado. En este caso, estamos asignando la fecha actual a la propiedad Fecha de ClientRepository.
Si tuviera un constructor con un parámetro llamado Fecha, lo hubiera usado, en vez de asignar valor a la propiedad. Es curioso este comportamiento y al principio puede causar desconcierto, pero es verdaderamente potente. En el siguiente punto conoceremos algo más de su potencia.
¿Dónde se definen las dependencias de mis componentes?
En ningún sitio, no es necesario. Windsor se encarga de detectarlas y asignarlas. Veamos la siguiente clase ClienteRepository usada en ejemplos anteriores:
public class ClientRepository : IRepository
{
public string NombreInterno { get; private set; }
public IDao Dao { get; set; }
public DateTime Fecha { get; set; }
public ClientRepository(string nombre, IDao dao)
{
Dao = dao;
NombreInterno = nombre;
}
}
Para poder instanciar un objeto de ClientRepository es necesario facilitar en su constructor un nombre y un objeto que implemente IDao. La configuración mínima y necesaria del registro del contenedor sería la siguiente:
_container.Register(
Component
.For<IRepository>()
.ImplementedBy<ClientRepository>()
.DependsOn(new {nombre = "Uno"}),
Component.For<IDao>().ImplementedBy<BasicDao>());
Fíjate que, en DependsOn, no le estoy definiendo el valor del parámetro dao del constructor de ClientRepository. Esto es posible porque el contenedor tiene registrado un componente para el servicio IDao, por lo que no hace falta definirlo en DependsOn de forma explícita. Windsor se encarga implícitamente de inyectarle el valor al parámetro del tipo IDao.
Estarás pensando qué ocurre cuando, por ejemplo, tengo dos componentes diferentes para el mismo servicio IDao. ¿Cuál usará entonces? Usará el primero registrado. ¿Y si no quiero que use el primero registrado? Entonces, hay que especificárselo. Veamos cómo:
[Fact]
public void Deberia_instanciarse_el_repositorio_con_dao_extendido()
{
_container.Register(
Component.For<IDao>().ImplementedBy<BasicDao>().Named("basico"),
Component.For<IDao>().ImplementedBy<ExtendedDao>().Named("extendido"),
Component.For<IRepository>().ImplementedBy<ClientRepository>()
.ServiceOverrides(ServiceOverride.ForKey<IDao>().Eq("extendido")));
IRepository antonio = _container.Resolve<IRepository>();
Assert.IsType(typeof(ExtendedDao), antonio.Dao);
}
Usando el método ServiceOverrides le especificamos el nombre del componente que queremos usar para el parámetro del constructor del ClientRepository del tipo IDao.
Handlers, selectores de componentes
Pongámonos en el siguiente escenario. Mis objetos de negocio usan el servicio IDao para almacenar los datos, pero dependiendo de la configuración quiero almacenar los datos en Sql Server o en Sql Lite. Para ello tengo dos componentes implementados. El BasicDao para SqlLite y el ExtendedDao para SqlServer. ¿Como configuro el contenedor para hacer esto posible? Una posible solución es la creación de un Handler que tome la decisión de qué componente IDao se usará en cada momento.
[Fact]
public void Deberia_resolver_dao_extendido_con_handler_configurado_para_dao_extendido()
{
_container.Register(
Component.For<IDao>().ImplementedBy<BasicDao>(),
Component.For<IDao>().ImplementedBy<ExtendedDao>());
_container.Kernel.AddHandlerSelector(new DaoHandlerSelector(TipoDao.Extendido));
Assert.IsType(typeof(ExtendedDao), _container.Resolve<IDao>());
}
Los handlers afectan a todo el contenedor y se definen mediante el método AddHandlerSelector del objeto Kernel. Para simplificar el ejemplo, se facilita al DaoHandlerSelector el tipo de componente Dao que quiero que elija. Pero los handlers pueden tener toda la sofisticación que necesitemos. La implementación de la clase DaoHandlerSelector queda así:
public class DaoHandlerSelector : IHandlerSelector
{
private TipoDao _tipoDao;
public DaoHandlerSelector(TipoDao tipoDao)
{
_tipoDao = tipoDao;
}
public bool HasOpinionAbout(string key, Type service)
{
if (service == null) throw new ArgumentNullException("service");
return service == typeof (IDao);
}
public IHandler SelectHandler(string key, Type service, IHandler[] handlers)
{
if (handlers == null) throw new ArgumentNullException("handlers");
Type tipoAImplementar = GetTipoAImplementar();
IEnumerable<IHandler> daoHandlers = handlers.Where(
handler => handler.ComponentModel.Implementation == tipoAImplementar);
if (!daoHandlers.Any())
{
throw new ApplicationException(
string.Format(
"No se ha encontrado ningún componente del tipo {0}", tipoAImplementar.Name));
}
return daoHandlers.First();
}
private Type GetTipoAImplementar()
{
Type tipoAImplementar;
switch(_tipoDao)
{
case TipoDao.Basico:
tipoAImplementar = typeof (BasicDao);
break;
case TipoDao.Extendido:
tipoAImplementar = typeof (ExtendedDao);
break;
default:
throw new NotSupportedException("Tipo no reconocido");
}
return tipoAImplementar;
}
}
Los handlers deben de implementar la interfaz IHandlerSelector y son llamados por el contenedor cuando él no es capaz de resolver por si sólo la dependencia que necesita para un componente.
El método HasOpinionAbout lo usa para saber si debe de consultar o no al Handler para una resolución concreta. El método SelectHandler, devuelve el IHandler del componente a usar el su resolución. Cada componente que registramos tiene un IHandler asociado. Cuando el contenedor intente resolver el servicio IDao que necesita y le pregunte al DaoHandlerSelector mediante el método SelectHandler, le facilitará todos los IHandlers de los componentes registrados para el servicio IDao.
Continúa con la 3ª parte de la introducción a Castle Windsor
Introducción con ejemplos a Castle Windsor, un contenedor de inversión de control para .NET (parte 1)
6 Jan 11
- Introducción con ejemplos a Castle Windsor (parte 2)
- Introducción con ejemplos a Castle Windsor (parte 3)
Castle Windsor nos brinda una multitud de interesantes características. Para hacer una introducción bastante completa y digerible, la he dividido en 3 partes. En la última parte dejaré descargable el código fuente de todos los ejemplos. Comenzemos!
Puedes descargarte el código fuente de los ejemplos: Rbp.Spike_.CastleWindsor.zip (1.24MB)
Desde hace un tiempo, intento aplicar a todos mis desarrollos los fantásticos principios SOLID. Son conjunto de principios de diseño orientado a objetos, recopilados por Uncle Bob Martin (@unclebobmartin), que hablan en términos de la gestión de dependencias. Si no los conoces aún, te recomiendo encarecidamente que leas sobre ellos.
¿Qué es la inversión de dependencias?
Uno de cinco principios SOLID es el llamado The Dependency Inversion Principle (DIP). El principio dice lo siguiente:
- High-level modules should not depend on low-level modules. Both should depend on abstractions.
- Abstractions should not depend upon details. Details should depend upon abstractions.
Coloquialmente podríamos decir que un módulo A no debe depender directamente de un módulo B, sino de la abstracción de B (una interfaz o clase que sirva de base para un conjunto de clases hijas).
Veamos un ejemplo sencillo, imaginémonos que tenemos una clase Cliente con un método Serializar que devuelve un string con el objeto serializado:
public class Cliente
{
public string Serializar()
{
SerializadorEnXml serializador = new SerializadorEnXml();
return serializador.Serializar(this);
}
}
public class SerializadorEnXml
{
public string Serializar(Cliente cliente)
{
// Código para serializar el cliente
}
}
La clase Cliente tiene una dependencia directa de la clase SerializadorEnXml. Si quisieramos hacer un test unitario del método Serializar de la clase Cliente, no podríamos simular el comportamiento del SerializadorEnXml (hacer su correspondiente Stub o Mock). Por otra parte, si más adelante quisieramos serializar en Json en vez de en Xml, deberíamos tocar el código de la clase cliente.
La forma de solucionar esto es invertir la dependencia haciendo que la clase Cliente no dependa directamente de la clase SerializdorEnXml, sino de una abstacción de la misma. Veamos como quedaría:
public class Cliente
{
private ISerializador _serializador;
public Cliente(ISerializador serializador)
{
_serializador = serializador;
}
public string Serializar()
{
return _serializador.Serializar(this);
}
}
public interface ISerializador
{
string Serializar(Cliente cliente);
}
public class SerializadorEnXml : ISerializador
{
public string Serializar(Cliente cliente)
{
// Código para serializar el cliente
}
}
Ahora, la clase Cliente no depende de ninguna clase de serialización, sino de una interfaz. El encargado de decidir qué clase será quien se encargue de serializar el cliente no será el propio Cliente, sino el responsable de crear un objeto de Cliente, por ejemplo, una factoría.
En nuestro caso, vamos a ver cómo solucionar este problema con el framework Castle Windsor.
¿Qué es Castle Windsor?
Sin entrar en mucho detalle, podríamos decir que:
- Es un framework que nos implementa la funcionalidad necesaria para conseguir la inversión de control en nuestros desarrollos.
- Es sumamente extensible y completo como iremos viendo. Si no hace lo que necesitas, seguramente podrás extenderlo e implementártelo tú mismo.
- Soporta configurarlo a través de ficheros XML o mediante una fantástica API fluida. Incluso soporta configuración por convención.
- Está preparado para ser usado en entornos web, cliente pesado, silverlight, etc.
- Soporta, de fábrica, la integración con múltitud de frameworks de terceros como son NHibernate, WCF, log4net, NLog, ActiveRecord, MonoRail, ASP.NET MVC, Rhino Service Bus, WCF, Quartz.Net, Rhino Security, SolrSharp, y algunas más.
Existen alternativas a este framework, como son Unity 2 (parte de Microsoft Enterprise Library), StructureMap y Spring.NET.
Conceptos básicos de Castle Windsor
Castle Windsor es muy sencillo de comenzar a usar, y por otra parte, cubre prácticamente todas las necesidades de inversión de control que podamos necesitar. Y si no la cubre, podremos extender el framework sin problemas.
Antes de comenzar, aclaremos unos conceptos básicos manejados por Castle Windsor:

Servicio
Es la definición de un contrato que define una unidad de funcionalidad que posteriomente deberá ser implementada por uno o varios componentes.
Componente
Es la implementación de nuestro servicio que será instanciado y gestionado por nuestro contenedor.
Dependencias
Son aquellos otros servicios que necesita un componente para funcionar.
En el ejemplo anterior, el Servicio sería la interfaz ISerializador, el componente sería clase SerializadorEnXml y no tendría dependencias.
Un ejemplo de uso
Las librerías de Castle Windsor, puedes descargártelas desde la página de Castle Project.
Para comenzar a usarlo, sólo necesitaremos hacer referencia en nuestro proyecto de las librerías Castle.Core.dll y Castle.Windsor.dll.
El ejemplo anterior, usando Castle Windsor para la inversión de control, quedaría implementado de la siguiente manera:
IWindsorContainer container = new WindsorContainer(); container.Register(Component.For<ISerializador>().ImplementedBy<SerializadorEnXml>()); Cliente cliente = new Cliente(container.Resolve<ISerializador>()); cliente.Serializar();
Responsabilidad de cada línea:
- Instanciamos nuestro contenedor. El contenedor es el encargado de resolver, crear y liberar los componentes.
- Registramos nuestro servicio definiendo qué componente lo implementará.
- Creamos nuestro cliente usando nuestro contenedor para resolver la implementación de nuestro servicio (su colaborador).
- Usamos el cliente obtenido.
Registrar los componentes
La primera tarea que debemos realizar con Windsor es la de registrar nuestros componentes para que luego puedan ser resueltos por el contenedor. En nuestra aplicación, podemos tener tantos contenedores diferentes como necesitemos. Windsor nos permite definir la configuración mediante ficheros XML o mediante código con su API fluída (Fluent API). En los ejemplos sólo usaré la configuración por código. Veamos un ejemplo mediante un test implementado con xUnit:
[Fact]
public void Registrar_componentes_uno_a_uno()
{
_container.Register(
Component.For<IRepository>().ImplementedBy<ClientRepository>(),
Component.For<IDataStore>().ImplementedBy<Database>());
}
Hemos registrado dos componentes. Uno para el servicio IRepository mediante la clase ClientRepository y otro para el servicio IDataStore implementado por la clase Database.
Mediante la configuración por código también podemos registrar los componentes por convención, en vez de uno a uno:
[Fact]
public void Registrar_componentes_por_convencion_y_configurarlos_como_transient()
{
_container.Register(
AllTypes.FromThisAssembly()
.Where(Component.IsInNamespace("Rbp.Spike.CastleWindsor.LogicLayer"))
.WithService.AllInterfaces()
.Configure(component => component.LifeStyle.Transient));
_container.Resolve<IRepository>();
_container.Resolve<IDataStore>();
}
Este test registra todos los componentes del ensamblado actual que se encuentre en el namespace “Rbp.Spike.CastleWindsor.LogicLayer” y finalmente define el LifeStyle al tipo Transient. En el siguiente punto explicaré qué son los LifeStyle.
Si un componente en concreto tuviera una configuración extra especial, podemos definírsela a partir del método ConfigureFor<>:
private void RegistrarContenedorPorConvencion()
{
_container.Register(
AllTypes.FromThisAssembly()
.Where(Component.IsInNamespace("Rbp.Spike.CastleWindsor.LogicLayer"))
.WithService.AllInterfaces()
.ConfigureFor<ExtendedDao>(x => x.Named("extendido"))
.ConfigureFor<IRepository>(
x => x.ServiceOverrides(
ServiceOverride.ForKey<IDao>().Eq("extendido"))));
}
En este ejemplo, al componente ExtendedDao se le ha dado un nombre específico (más adelante veremos para qué sirve) y a los componentes del servicio IRepository se les redefine, mediante ServiceOverrides, la dependencia (o colaborador) a usar (también lo veremos más adelante).
Windsor trae de fábrica un amplio abanico de posibilidades para definir qué componentes registrar por convención. En la documentación oficial encontrarás explicadas todas las posibilidades.
Por defecto, Singleton
Cada vez que resolvemos el mismo servicio del contenedor, ¿nos creará una instancia nueva del componente? ¿o nos devolverá siempre la misma instancia?. Depende, de la configuración definida al componente a la hora de registrarlo.
Este concepto es llamado por el framework LifeStyle. A la hora de registrar un objeto, por defecto, lo hará definiéndole el LifeStyle Singleton. Es decir, la primera vez que resolvamos un servicio, generará un objeto del componente que implementa el servicio. Las próximas veces que resolvamos el mismo servicio, nos devolverá la misma referencia al objeto del componente que se creó inicialmente.
Castle Windsor soporta los siguientes LifeStyles: Singleton (por defecto), Transient, PerThread, PerWebRequest, Pooled y Custom. Custom nos permite implementar un ciclo de vida propio.
Comprobemos esto con uno par de test unitarios:
[Fact]
public void Lifestyle_singleton_por_defecto()
{
_container.Register(
AllTypes.FromThisAssembly()
.Where(Component.IsInNamespace("Rbp.Spike.CastleWindsor.LogicLayer"))
.WithService.AllInterfaces());
IRepository repositorio1 = _container.Resolve<IRepository>();
IRepository repositorio2 = _container.Resolve<IRepository>();
Assert.Equal(repositorio1, repositorio2);
}
Podemos observar que las dos variables (repositorio1 y repositorio2) tienen la referencia al mismo objeto.
Si hubiéramos configurado los componentes con LifeStyle = Transient, en cada resolución, el contenedor hubiera creado instancias nuevas del componente y las variables tendrían referencias a objetos distintos.
Si alguna vez usas el LifeStyle Transient, recuerda liberar los componentes del contenedor cuando ya no los vayas a usar. Esto es necesario porque el contenedor guardar un registro de los objetos creados. Para liberarlos, sólo tienes que usar el método Release del contenedor. De otra forma, tu desarrollo usará más y más memoria del sistema a lo largo del tiempo, hasta que el sistema deje de funcionar correctamente.
Continúa con la 2ª parte de la introducción a Castle Windsor