lunes, 7 de noviembre de 2011

Builders genéricos: Agregando funcionalidad (3)

Esta es la tercera parte de la serie de posts que escribí sobre Builders genéricos.
Escribo los links de los post anteriores:

Builders genéricos: Introducción (1)
Builders genéricos: Implementación (2)


En este post voy a dar ejemplos más avanzados en los cuales me es muy útil utilizar el concepto de Builder. Quizás existan mejores formas de hacer lo que les voy a explicar (escucho sugerencias), pero hasta ahora, este patrón me dio buenos resultados, sobre todo para realizar los Tests de mis aplicaciones.

Primero partamos de la base que nos dejó mi post anterior:

Tenemos una interfaz IBuilder que recibe un TObject como parámetro genérico cuyo método principal es Build. Este método nos permite construir objetos de tipo TObject:

public interface IBuilder<TBuilder, TObject>
    where TBuilder : IBuilder<TBuilder, TObject>
{
    TBuilder With(Action<TObject> setProperties);
    TObject Build();
}


Aparte, también tenemos nuestra implementación abstracta que nos permite construir un objeto de tipo TObject. Para esto tuvimos que agregarle la restricción a TObject de que debía tener como mínimo un constructor público sin parámetros ( new() ):

public abstract class AbstractBuilder<TBuilder, TObject>
    : IBuilder<TBuilder, TObject>
    where TBuilder : AbstractBuilder<TBuilder, TObject>
    where TObject : class, new()
{
    protected TObject obj;
    protected AbstractBuilder()
    {
        obj = new TObject();
    }

    public virtual TBuilder With(Action<TObject> setProperties)
    {
        if (setProperties == null)
            throw new ArgumentNullException("setProperties");
        setProperties(obj);
        return this as TBuilder;
    }

    public virtual TObject Build()
    {
        TObject objAux = obj;
        obj = null;
        return objAux;
    }
}


Ahora comenzaremos a utilizarlo y extenderemos su funcionalidad o la simplificaremos :) cuando veamos que es posible.

Revisemos primero la interfaz IBuilder<TObject>.

Leyendo uno de los posts del Blog de Jorge Rowies que hablaba sobre el Principio de Separación de Interfaces - SOLID se me cruzó por la mente que mi interfaz IBuilder, aunque en apariencia resulte simple estaba haciendo algo más que lo que debería hacer.

¿Qué es lo que dice el principio de separación de interfaces (Interface Segregation Principle o ISP)?
El principio dice: Una clase cliente no debe ser forzada a depender de interfaces que no usa.

En nuestro caso, tenemos una interfaz de un Builder que contiene un método Build para generar un objeto :) pero aparte tiene el método With que recibe un Action para setear sus propiedades :(.

Planteemos un ejemplo para ver que es lo que no está del todo bien:

Tenemos la siguiente clase que representa un número aleatorio cuyo único constructor recibe un número entero:

public class RandomNumber
{
    public RandomNumber(int num)
    {
        IntegerValue = num;
    }
    public int IntegerValue { get; private set; }
}

El Builder genérico no nos sirve porque la clase RandomNumber no cumple la restricción de tener al menos un constructor público sin parámetros. Por lo tanto nos creamos una implementación customizada que nos devuelva esta clase pero que implemente IBuilder:

public class RandomNumberBuilder : IBuilder<RandomNumberBuilder,RandomNumber>
{
    public RandomNumber Build()
    {
        // Implementación sólo de ejemplo
        return new RandomNumber(new Random(Guid.NewGuid().GetHashCode()).Next());
    }
    public RandomNumberBuilder With(Action<RandomNumber> setProperties)
    {
        throw new NotSupportedException("Este builder no permite setear propiedades!");
    }
}


A simple vista nos damos cuenta que hay 2 cosas que nos hacen ruido:

  1. Debemos implementar el método With aún cuando ya sabemos de antemano que la clase RandomNumber no permite el seteo de propiedades. Como primer solución, en el cuerpo de este método lanzamos una excepción de tipo "método no soportado".
  2. Por otro lado, como el método With debe devolver la instancia del builder, debemos pasarle por parámetro al propio Builder que nunca se utilizará dentro de la clase.

¿Cómo debería estar implementada nuestra clase RandomNumberBuilder?

public class RandomNumberBuilder : IBuilder<RandomNumber>
{
    public RandomNumber Build()
    {
        return new RandomNumber(new Random(Guid.NewGuid().GetHashCode()).Next());
    }
}

Utilizando el principio de ISP simplemente separamos en 2 la interfaz:

public interface IBuilder<TObject>
{
    TObject Build();
}

public interface IBuilderWithSetters<TBuilder,TObject>
    : IBuilder<TObject>
    where TBuilder : IBuilderWithSetters<TBuilder, TObject>
{
    TBuilder With(Action<TObject> setProperties);
}

De esta forma queda más reusable la interfaz IBuilder y es mucho más clara la función que debe cumplir.

Con este cambio, nuestro Generic Builder quedaría igual que antes sólo que implementaría la interfaz IBuilderWithSetters<TBuilder,TObject>:

public abstract class AbstractBuilder<TBuilder, TObject> 
        : IBuilderWithSetters<TBuilder,TObject>
    where TBuilder : AbstractBuilder<TBuilder, TObject>
    where TObject : class, new()
{
    // Misma implementación que antes
}


Otro ejemplo. Utilizaremos la clase Person definida en el post anterior junto con el siguiente builder:

public class Person
{
    public string Name { get; set; }
    public int? Age { get; set; }
}

public class PersonBuilder : AbstractBuilder<PersonBuilder,Person>
{
}

Supongamos que en lugar de querer crear un objeto, queremos crear una colección de objetos Person con valores iguales(que no es lo mismo que una colección de instancias iguales). Para crear 10 elementos la solución sería la siguiente:

[Test]
public void TestBuildCollection()
{
    var people = new List<Person>();
    for (int i = 0; i < 10; i++)
    {
        people.Add(
            new PersonBuilder()
                .With(person => person.Name = "Juan")
                .With(person => person.Age = 30)
                .Build());
    }
    Assert.That(people.Count(), Is.EqualTo(10));
    foreach (Person p in people)
    {
        Assert.That(p, Is.Not.Null);
        Assert.That(p.Name, Is.EqualTo("Juan"));
        Assert.That(p.Age, Is.EqualTo(30));
    }
}

Esta solución funciona correctamente, sin embargo estamos creando 10 builders exactamente iguales, 1 por cada objeto Person que queremos crear.

Una segunda posible solución podría haber sido la siguiente:

[Test]
public void TestBuildCollection()
{
    var people = new List<Person>();
    var personBuilder = new PersonBuilder()
        .With(person => person.Name = "Juan")
        .With(person => person.Age = 30);
    for (int i = 0; i < 10; i++)
    {
        people.Add(personBuilder.Build());
    }
    Assert.That(people.Count(), Is.EqualTo(10));
    foreach (Person p in people)
    {
        Assert.That(p, Is.Not.Null);
        Assert.That(p.Name, Is.EqualTo("Juan"));
        Assert.That(p.Age, Is.EqualTo(30));
    }
}

Sin embargo, cada vez que hacemos build, el campo "obj" del builder se setea a null ya que sino, una nueva llamada al método Build devolvería la misma instancia.

public virtual TObject Build()
{
   TObject objAux = obj;
   obj = null;   // Para prevenir utilizar una instancia creada previamente
   return objAux;
}

Por otro lado, si en vez de setear esa propiedad en null, le seteamos new TObject(), al correr el test nos encontraremos con un resultado raro. El primer objeto creado tendría todas sus propiedades con valores pero los demás estarían vacíos. Esto se debe a que cada llamada al método With se ejecuta inmediatamente y por lo tanto, en el nuevo objeto a crear este seteo no estaría registrado:

public virtual TObject Build()
{
   TObject objAux = obj;
   obj = new TObject();   // NO!!!.. Se están perdiendo todos los seteos
   return objAux;
}


¿Cómo podemos modificar nuestro Builder genérico para que el segundo test corra correctamente? 

En lugar de ejecutar inmediatamente las acciones las guardamos en una lista para luego ejecutarlas en cada llamada al método Build. Para no complicar la clase AbstractBuilder crearía una nueva llamada AbstractTemplateBuilder (le agregué la palabra Template porque lo que estoy haciendo es crear una plantilla de objetos).

Las clases AbstractTemplateBuilder y PersonBuilder quedarían de la siguiente manera:

public abstract class AbstractTemplateBuilder<TBuilder, TObject>
        : IBuilderWithSetters<TBuilder, TObject>
    where TBuilder : AbstractTemplateBuilder<TBuilder, TObject>
    where TObject : class, new()
{
    protected readonly IList<Action<TObject>> actions;
    protected AbstractTemplateBuilder()
    {
        actions = new List<Action<TObject>>();
    }

    public virtual TBuilder With(Action<TObject> setProperties)
    {
        if (setProperties == null)
            throw new ArgumentNullException("setProperties");
        actions.Add(setProperties);
        return (TBuilder)this;
    }

    public virtual TObject Build()
    {
        TObject toBuild = new TObject();
        foreach (Action<TObject> action in actions)
        {
            if (action != null)
                action(toBuild);
        }
        return toBuild;
    }
}

public class PersonBuilder : AbstractTemplateBuilder<TPersonbuilder, Person>
{
}

Ahora que podemos generar las clases mediante un template. ¿Podemos generalizar la obtención de la colección de objetos? La respuesta es Sí :)
¿Cómo quedaría el test?

[Test]
public void TestBuildCollection()
{
    var personBuilder = new PersonBuilder()
        .With(person => person.Name = "Juan")
        .With(person => person.Age = 30);

    var people = personBuilder.BuildCollection(10);

    Assert.That(people.Count(), Is.EqualTo(10));
    foreach (Person p in people)
    {
        Assert.That(p, Is.Not.Null);
        Assert.That(p.Name, Is.EqualTo("Juan"));
        Assert.That(p.Age, Is.EqualTo(30));
    }
}


Para que esto funcione, creamos la interfaz ICollectionBuilder:

public interface ICollectionBuilder<TObject>
{
    IEnumerable<TObject> BuildCollection(int length);
}

La implementación del método sería la siguiente:

public virtual IEnumerable<TObject> BuildCollection(int length)
{
    TObject toBuild = Build();
    for (int i = 0; i < length; i++)
    {
        yield return toBuild;
    }
}

Y para finalizar, ya que creamos una colección, ¿Por qué no permitir que se seteen valores específicos a cada elemento teniendo en cuenta la posición del elemento a agregar? Otra vez podemos utilizar los actions para permitir esto.

Por ejemplo, si quiero devolver una colección de personas con el mismo nombre pero con edades de 0 a 10 el test sería el siguiente:

[Test]
public void TestBuildCollection()
{
    var personBuilder = new PersonBuilder()
        .With(person => person.Name = "Juan")
        .With(person => person.Age = 30);

    var people = personBuilder
        .BuildCollection(10, (person, index) => person.Age = index);

    Assert.That(people.Count(), Is.EqualTo(10));
    int aux = 0;
    foreach (Person p in people)
    {
        Assert.That(p, Is.Not.Null);
        Assert.That(p.Name, Is.EqualTo("Juan"));
        Assert.That(p.Age, Is.EqualTo(aux++));
    }
}

Para no extenderme más, les dejo como quedarían la interfaz ICollectionBuilder y la implementación completa de AbstractTemplateBuilder:

public interface ICollectionBuilder<TObject>
{
    IEnumerable<TObject> BuildCollection(int length);
    IEnumerable<TObject> BuildCollection(int length, Action<TObject,int> setProperties);
}

public abstract class AbstractTemplateBuilder<TBuilder, TObject>
        : IBuilderWithSetters<TBuilder, TObject>,
            ICollectionBuilder<TObject>
    where TBuilder : AbstractTemplateBuilder<TBuilder, TObject>
    where TObject : class, new()
{
    protected readonly IList<Action<TObject>> actions;
    protected AbstractTemplateBuilder()
    {
        actions = new List<Action<TObject>>();
    }

    public virtual TBuilder With(Action<TObject> setProperties)
    {
        if (setProperties == null)
            throw new ArgumentNullException("setProperties");
        actions.Add(setProperties);
        return (TBuilder)this;
    }

    public virtual TObject Build()
    {
        TObject toBuild = new TObject();
        foreach (Action<TObject> action in actions)
        {
            if (action != null)
                action(toBuild);
        }
        return toBuild;
    }

    public virtual IEnumerable<TObject> BuildCollection(int length)
    {
        for (int i = 0; i < length; i++)
        {
            TObject toBuild = Build();
            yield return toBuild;
        }
    }

    public virtual IEnumerable<TObject> BuildCollection(int length, Action<TObject, int> setProperties)
    {
        for (int i = 0; i < length; i++)
        {
            TObject toBuild = Build();
            if (setProperties != null)
                setProperties(toBuild, i);
            yield return toBuild;
        }
    }
}

Todavía me quedaron unos ejemplos más avanzados pero ya me extendí demasiado. Quizás haya un 4° post si lo amerita.

Cualquier duda, consulta o sugerencia como siempre será bienvenida.
Hasta luego.

27/12/2015
Fix: BuildCollection tenía un error. Se estaba creando el objeto por fuera del foreach cuando debería haber sido dentro del mismo.

Builders genéricos: Implementación (2)

Continuación del post: Builders genéricos: Introducción (1).

En principio, ¿Qué es un builder?

Según Wikipedia, es un patrón de diseño que es usado para permitir la creación de una variedad de objetos complejos desde un objeto fuente (Producto).

Intención: Abstrae el proceso de creación de un objeto complejo, centralizando dicho proceso en un único punto, de tal forma que el mismo proceso de construcción pueda crear representaciones diferentes.

Vamos a definir las clases básicas que manipularemos.

   
public class Person
{
    public string Name { get; set; }
    public int? Age { get; set; }
}

En este ejemplo, la persona sería el producto que queremos construir.

Empezemos con definir nuestra interfaz IPersonaBuilder que nos permitirá construir nuestra persona:

public interface IPersonBuilder
{
    IPersonBuilder WithName(string name);
    IPersonBuilder WithAge(int age);
    Person Build();
}

La interfaz tiene 2 métodos que devuelven un objeto del mismo tipo que la interfaz para poder utilizarse de forma Fluent (como se vió en mi post anterior) y un método que devuelve el objeto creado.

Una posible implementación sería la siguiente:

   
public class PersonBuilder : IPersonBuilder
{
    protected Person person;
    public PersonBuilder()
    {
        person = new Person();
    }
    public virtual IPersonBuilder WithName(string name)
    {
        person.Name = name;
        return this;
    }
    public virtual IPersonBuilder WithAge(int age)
    {
        person.Age = age;
        return this;
    }
    public Person Build()
    {
        Person personAux = person;
        person = null;
        return personAux;
    }
}


La implementación no tiene nada fuera de lo común, cada método "With" setea una propiedad de la persona y devuelve el builder.

Se utilizaría de la siguiente manera:
Person juan = new PersonBuilder()
                .WithName("Juan")
                .WithAge(30)
                .Build();


Quizás alguien me pregunten porque en lugar de lo anterior no utilicé Object Initializers y, aprovechando las ventajas de C# 3.0, el código se reduzca a esto:
Person juan = new Person
                {
                    Name = "Juan",
                    Age = 30
                };


En este caso, se puede utilizar Object Initializers porque es un caso trivial pero si el objeto a construir necesite otro tipo de propiedades más complejas, o si la lógica de construcción pueda ser cambiada, esto no sería tan simple.

Ahora supongamos que queremos crear otro builder de persona para, por ejemplo, un Doctor. En este caso, queremos que le agregue el prefijo "Dr. " al nombre. La clase extendería a PersonBuilder y quedaría así:
public class DoctorBuilder : PersonBuilder
{
    public override IPersonBuilder WithName(string name)
    {
        person.Name = "Dr. " + name;
        return this;
    }
}


Hagamos unos Tests simples con NUnit para comprobar que funcionan correctamente los Builders:

[Test]
public void TestPersonBuilder()
{
    Person juan = new PersonBuilder()
                        .WithName("Juan")
                        .WithAge(30)
                        .Build();
    Assert.That(juan, Is.Not.Null, "El Builder no devolvió el objeto!");
    Assert.That(juan.Name, Is.Not.Null.And.Not.Empty, "No se pudo setear el nombre!");
    Assert.That(juan.Name, Is.EqualTo("Juan"), "Nombre incorrecto!");
    Assert.That(juan.Age.HasValue, Is.True, "No se pudo setear la edad!");
    Assert.That(juan.Age.Value, Is.EqualTo(30), "Edad mal seteada");
}

[Test]
public void TestDoctorBuilder()
{
    Person juan = new DoctorBuilder()
                        .WithName("Juan")
                        .WithAge(30)
                        .Build();
    Assert.That(juan, Is.Not.Null, "El Builder no devolvió el objeto!");
    Assert.That(juan.Name, Is.Not.Null.And.Not.Empty, "No se pudo setear el nombre!");
    Assert.That(juan.Name, Is.EqualTo("Dr. Juan"), "Nombre incorrecto!");
    Assert.That(juan.Age.HasValue, Is.True, "No se pudo setear la edad!");
    Assert.That(juan.Age.Value, Is.EqualTo(30), "Edad mal seteada");
}


Si corren los test el resultado será satisfactorio.

Ahora supongamos que queremos generalizar la interfaz base de un builder con las siguientes premisas:

  1. La construcción final del objeto se hace llamando al método Build.
  2. Se pueden setear propiedades mediante actions llamando al método With. 

La interfaz podría quedar de la siguiente manera:

public interface IBuilder<TObject>
{
    IBuilder<TObject> With(Action<TObject> setProperties);
    TObject Build();
}



Sin embargo, como vimos en el post anterior, tenemos los problemas de casteo al extender las clases y utilizar el método With ya que deberíamos estar casteando constantemente IBuilder<TObject> a la clase correspondiente.

Por lo tanto, haciendo mejor uso de Generics la interfaz quedaría de la siguiente manera:


public interface IBuilder<TBuilder,TObject>
    where TBuilder : IBuilder<TBuilder, TObject>
{
    TBuilder With(Action<TObject> setProperties);
    TObject Build();
}


La primera implementación de nuestro Builder genérico, siguiendo la interfaz anterior, quedaría así:

public abstract class AbstractBuilder<TBuilder,TObject> : IBuilder<TBuilder,TObject>
    where TBuilder : AbstractBuilder<TBuilder, TObject>
    where TObject : class, new()
{
    protected TObject obj;
    protected AbstractBuilder()
    {
        obj = new TObject();
    }

    public virtual TBuilder With(Action<TObject> setProperties)
    {
        if (setProperties == null)
            throw new ArgumentNullException("setProperties");
        setProperties(obj);
        return this as TBuilder;
    }

    public virtual TObject Build()
    {
        TObject objAux = obj;
        obj = null;
        return objAux;
    }
}


La contra que tiene esta solución es que el objeto a construir debe tener un constructor sin parámetros por defecto. Esto igualmente puede solucionarse fácilmente creando un método abstracto TObject New() que los builders concretos sobreescriban para generar el objeto por defecto.
Por ahora con esta solución nos alcanza.

¿Cómo quedaría nuestra clase PersonBuilder utilizando esto y cómo se utilizaría de la manera más genérica posible?

public class GenericPersonBuilder : AbstractBuilder<GenericPersonBuilder,Person>
{
}

[Test]
public void TestGenericPersonBuilder()
{
    Person juan = new GenericPersonBuilder()
                        .With(person => person.Name = "Juan")
                        .With(person => person.Age = 30)
                        .Build();
    Assert.That(juan, Is.Not.Null, "El Builder no devolvió el objeto!");
    Assert.That(juan.Name, Is.Not.Null.And.Not.Empty, "No se pudo setear el nombre!");
    Assert.That(juan.Name, Is.EqualTo("Juan"), "Nombre incorrecto!");
    Assert.That(juan.Age.HasValue, Is.True, "No se pudo setear la edad!");
    Assert.That(juan.Age.Value, Is.EqualTo(30), "Edad mal seteada");
}


¿Cómo quedarían nuestras clases PersonBuilder y DoctorBuilder agregándole métodos concretos?

public abstract class GenericPersonBuilder<TBuilder> : AbstractBuilder<TBuilder, Person>
    where TBuilder : GenericPersonBuilder<TBuilder>
{
    public virtual TBuilder WithName(string name)
    {
        return With(person => person.Name = name);
    }
    public virtual TBuilder WithAge(int age)
    {
        return With(person => person.Age = age);
    }
}

public class PersonBuilder2 : GenericPersonBuilder<Personbuilder2>
{
}

public class DoctorBuilder2 : GenericPersonBuilder<Doctorbuilder2>
{
    public override DoctorBuilder2 WithName(string name)
    {
        return With(person => person.Name = "Dr. " + name);
    }
}


Los tests serían exáctamente iguales a los primeros 2 solamente habría que cambiar la clase PersonBuilder por PersonBuilder2 y DoctorBuilder por DoctorBuilder2 así que no las voy a escribir nuevamente. Si quieren, prueben correr los tests y verán que funcionan correctamente.

En la tercera parte, hablaré de casos más complejos en la que la utilización de Builders es muy útil y una implementación distinta del AbstractBuilder.

Builders genéricos: Introducción (1)

Este post está basado en una publicación que encontré en Codeproject sobre el patrón Builder.

Es interesante ver el uso que el autor se le da a los Generics en C# para crear una clase que recibirá como parámetro genérico a la misma clase. Suena un poco confuso pero es bastante simple. Esto es muy común en programas que utilizan configuración Fluent en la cual un objeto, al llamarse uno de sus métodos, debe devolverse a sí mismo.

Veamos un ejemplo:

Si tuviesemos una clase llamada Configuration como la siguiente:

public class Configuration
{
    protected Dictionary<string, string> properties;
    public Configuration SetName(string name)
    {
        properties["Name"] = name;
        return this;
    }
    public Configuration Configure()
    {
        foreach (var item in properties)
        {
            System.Console.WriteLine("{0}: {1}", item.Key, item.Value);
        }
        return this;
    }
}

Por Fluent se configuraría de la siguiente manera:

Configuration config = new Configuration()
                .SetName("MyConfiguration")
                .Configure();

Ahora supongamos que queremos extender esta misma clase para poder configurar una Base de datos que necesita de un Connection String. La configuración sería la siguiente:

DBConfiguration config = new DBConfiguration()
                .SetName("MyConfiguration")
                .SetConnStringName("db1")
                .Configure();

Entonces la clase DBConfiguration debería quedar así:

public class DBConfiguration : Configuration
{
    public virtual DBConfiguration SetConnStringName(string connStringName)
    {
        properties["ConnStringName"] = connStringName;
        return this;
    }
}

Sin embargo tenemos los siguientes 2 errores al compilar:

'Pruebas.Configuration' does not contain a definition for 'SetConnStringName' and no extension method 'SetConnStringName' accepting a first argument of type 'Pruebas.Configuration' could be found (are you missing a using directive or an assembly reference?)

Cannot implicitly convert type 'Pruebas.Configuration' to 'Pruebas.DBConfiguration'. An explicit conversion exists (are you missing a cast?)

Estos errores aparecen porque estamos intentando llamar a un método de "DBConfiguration" cuando en realidad SetName devuelve un objeto de tipo "Configuration". Lo mismo sucede al intentar setear resultado a la variable config.

Un posible Fix a esto sería hacer "new" de los métodos SetName y Configure:

public new DBConfiguration SetName(string name)
{
    return (DBConfiguration)base.SetName(name);
}
public new DBConfiguration Configure()
{
    return (DBConfiguration)base.Configure();
}

En este punto, nuestro código ya compila y hace lo que debe hacer, sin embargo tuvimos que ocultar la implementación de la clase que heredamos y la volvimos a escribir. Es decir, la herencia de clases no nos sirvió para mucho.

¿Y si usamos Generics cómo podríamos resolverlo?

Fácilmente podríamos generar la siguiente clase abstracta:

public abstract class Configuration<TConfiguracion>
    where TConfiguracion : Configuration<TConfiguracion>
{
    protected Dictionary<string, string> properties;
    public TConfiguracion SetName(string name)
    {
        properties["Name"] = name;
        return (TConfiguracion)this;
    }
    public TConfiguracion Configure()
    {
        foreach (var item in properties)
        {
            System.Console.WriteLine("{0}: {1}", item.Key, item.Value);
        }
        return (TConfiguracion)this;
    }
}


Con la definición anterior lo que estamos diciendo es que la clase "Configuration<TConfiguration>" recibirá como tipo genérico una clase que hereda de "Configuration<TConfiguration>".

De esta manera, las clases Configuration (sin parámetros) y DBConfiguration simplemente quedarían así:

public class Configuration : Configuration<Configuration>
{
}

public class DBConfiguration : Configuration<DBConfiguration>
{
    public DBConfiguration SetConnStringName(string connStringName)
    {
        properties["ConnStringName"] = connStringName;
        return this;
    }
}


Ahora el código, aparte de compilar, es mucho más sencillo de extender. En un principio les puede costar entender esta sentencia: "class Configuration : Configuration<Configuration>", pero lo que debemos tener en cuenta es que "Configuration" y "Configuration<TConfiguration>" son en realidad dos clases diferentes.

Bueno, como este post me quedó un poco largo, lo dejaré como introducción al tema y dejaré mi explicación de los Builders en la segunda parte.

Cualquier duda o sugerencia es bienvenida.

domingo, 6 de noviembre de 2011

Bienvenidos a mi blog...

Hola, bienvenidos a mi primer blog. El mismo tratará de expresar mi experiencia con C# y las soluciones básicas a los problemas que me haya encontrado.
Espero que la información que escriba les resulte útil.

Antes que nada voy a escribir uno de los programas más comunes (sólo para probar que me funcione el SyntaxHighlighter... Los pasos para configurarlo para mi blog lo pueden encontrar en el siguiente link)
   
using System;
namespace MyPrimerBlog
{
    public class HelloWorld
    {
        private static void Main()
        {
            Console.WriteLine("Hola Mundo!!!");
            Console.ReadLine();
        }
    }
}

Saludos!!!