La mayoría de proyectos en los que nos vemos envueltos implican modificar código existente en vez de escribir código nuevo. En la mayoría de éstas ocasiones modificar dicho código resulta un trabajo tedioso. El principio Open Closed nos ayuda a acometer esta tarea de una forma mucho mas simple (siempre y cuando se siga éste principio a la hora de desarrollar nuestros proyectos).
De forma muy breve, se puede resumir éste principio de la siguiente forma:  las entidades software deben estar abiertas a extensiones, pero cerradas a modificaciones.  Éste principio está fuertemente relacionado con los patrones Bajo acoplamiento y Alta cohesión (ambos explicados en sus correspondientes links).
Tal y como comentaba, éste principio nos indica que, (en un mundo ideal) no deberíamos cambiar código existente o clases. Cualquier nueva funcionalidad se puede añadir mediante subclases y sobreescribiendo métodos o bien reutilizando código existente mediante delegación.

Esto evita que introduzcamos nuevos bugs o errores en un código existente. Si nunca lo cambiamos,¡ nunca lo rompemos!

¿Y cómo llevamos a cabo nuestro objetivo?

Pues no hay un método o procedimiento único. Debemos aplicar, en cada contexto, aquellos patrones de diseño que nos permitan preveer posibles actualizaciones o ampliaciones de nuestro código.
Un ejemplo práctico:

A continuación os presento un pequeño ejemplo en el que muestro los beneficios de aplicar correctamente los patrones de diseño con tal de cumplir con el principio Open Closed:

Este ejemplo muestra una clase llamada Filtro que se utiliza para filtrar los elementos que se le pasan dentro de una colección en función de uno de los atributos o propiedades de dichos elementos. En este ejemplo, la clase filtro filtrará elementos del tipo Persona. La clase persona tiene el siguiente código:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace OpenClosedPrinciple
{
    /// <summary>
    /// Clase Persona. Almacena los atributos básicos de una
    /// persona. Sirve de ejemplo para mostrar los beneficios
    /// de aplicar el principio Open Closed.
    /// </summary>
    public class Persona
    {
       #region Class attributes

       /// <summary>
       /// Nombre de la persona
       /// </summary>
       private string _name;

       /// <summary>
       /// Apellidos de la persona
       /// </summary>
       private string _secondName;

       /// <summary>
       /// Edad de la persona.
       /// </summary>
       private int _age;

       #endregion

       #region Class constructors

       /// <summary>
       /// Constructor por defecto de la clase Persona
       /// </summary>
       public Persona()
       {
       }

       /// <summary>
       /// Constructor que recibe como parámetro el nombre de la persona
       /// </summary>
       /// <param name="name">Nombre de la persona</param>
       public Persona(string name)
       : this()
       {
          this._name = name;
       }

       /// <summary>
       /// Constructor que recibe como parámetro el nombre
       /// y el apellido de la persona
       /// </summary>
       /// <param name="name">Nombre de la persona</param>
       /// <param name="secondName">Apellido de la persona</param>
       public Persona(string name, string secondName)
       : this(name)
       {
          this._secondName = secondName;
       }

       /// <summary>
       /// Constructor que recibe como parámetro el nombre, apellidos y
       /// la edad de la persona
       /// </summary>
       /// <param name="name">Nombre de la persona</param>
       /// <param name="secondName">Apellido de la persona</param>
       /// <param name="age">Edad de la persona</param>
       public Persona(string name, string secondName,int age)
       : this(name,secondName)
       {
          this._age = age;
       }

       #endregion

       #region Class public properties

       /// <summary>
       /// Devuelve o establece la edad de la persona.
       /// </summary>
       public int Age
       {
          get
          {
             return _age;
          }

          set
          {
            _age = value;
          }
       }

      /// <summary>
      /// Devuelve o establece el nombre de la persona.
      /// </summary>
      public string Name
      {
         get
         {
            return _name;
         }

         set
         {
            _name = value;
         }
      }

      /// <summary>
      /// Devuelve o establece el apellido de la persona.
      /// </summary>
      public string SecondName
      {
         get
         {
            return _secondName;
         }

         set
         {
           _secondName = value;
         }
      }

      #endregion
   }
}

Una vez definida la clase que se deberá filtrar (es decir, recibiremos una lista de personas y deberemos filtrar por alguno de sus atributos o propiedades, por ejemplo, filtrar por nombre o por apellidos) ahora podemos pasar a definir la clase que se encargará de filtrar los elementos que recibirá como parámetro. A esta clase la hemos llamado Filtro.

En esta primera muestra de código no utilizaré ningún patrón, por lo que no cumpliremos con el principio, como veremos más adelante:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace OpenClosedPrinciple
{
    /// <summary>
    /// Clase filtro. Filtra objetos en base a un atributo de los elementos
    /// y a un parámetro de entrada.
    /// </summary>
    public class Filtro
    {
       /// <summary>
       /// Método que filtra un enumerado de personas en base a su nombre.
       /// </summary>
       public IEnumerable<Persona> Filtrar(IEnumerable<Persona> personas, string nombre)
       {
          foreach (var Persona in personas)
          {
              if (Persona.Name.Equals(nombre))
                 yield return Persona;
          }
       }
    }
}

Esta clase funciona perfectamente. De hecho, a simple vista, nadie diría que tenga que tocarse en un futuro. Pero la verdad es otra y, muy seguramente, en un futuro nos pedirían que se pueda filtrar las personas por su apellido. Esto implica que tenemos que “abrir” la clase, cambiar el código y luego volver a compilar y distribuir. Entonces tendríamos algo tal que así:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace OpenClosedPrinciple
{
    /// <summary>
    /// Clase filtro. Filtra objetos en base a un atributo de los elementos
    /// y a un parámetro de entrada.
    /// </summary>
    public class Filtro
    {
       /// <summary>
       /// Método que filtra un enumerado de personas en base a su nombre.
       /// </summary>
       public IEnumerable<Persona> Filtrar(IEnumerable<Persona> personas, string nombre)
       {
          foreach (var Persona in personas)
          {
              if (Persona.Name.Equals(nombre))
                 yield return Persona;
          }
       }

       public IEnumerable<Persona> FiltrarApellidos(IEnumerable<Persona> personas, string apellido)
       {
          foreach (var Persona in personas)
          {
              if (Personas.Apellido.Equals(apellido))
                 yield return Persona;
          }
       }
    }
}

Bueno, no tiene mala pinta, pero, como ya he comentado, hemos tenido que abrir el código, cambiarlo, recompilar y redistribuir de nuevo. Si es sólo por esta vez podríamos aceptarlo, pero ahora viene cuando alguien decide que también debemos filtrar por la edad de la persona. ¿Y que implica eso? Pues volver a abrir, cambiar el código, volver a recompilar y volver a distribuir.
Como podeis ver, no es la mejor de las opciones. Lo que nos indica el principio Open Closed es que la clase Filtro debería quedar cerrada, lo que indica que no deberíamos volver a tocarla. Cualquier nueva funcionalidad la tendríamos que implementar o bien en clases derivadas y sobreescribiendo métodos o bien mediante la delegación de métodos.

La solución, aplicar patrones de diseño o una buena guia de diseño como la que vamos a aplicar…”Encapsular todo aquello que cambia”.

Asi pues, modificaremos el código para que quede de la siguiente forma. Crearemos una interfaz llamada IElemento. Todo elemento (objeto) que pueda ser filtrado, deberá heredar de IElemento.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace OpenClosedPrinciple
{
    interface IElemento
    {
    }
}

Crearemos una interfaz llamada IComparacion que será una interfaz genérica y que tendrá un método que nos permita comparar un atributo interno con un elemento del tipo de la interfaz. Esta interfaz deberá ser implementada por aquellas clases que sirvan como comparador a la hora de aplicar un filtro. Por decirlo de alguna forma, la clase que implemente esta interfaz  será la encargada de determinar si un elemento de la lista de elementos totales estará en el resultado o no.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace OpenClosedPrinciple
{
    interface IComparacion<T>
    {
          bool CompararCon(T item);
    }
}

Seguidamente, definiremos la interfaz IFiltro. Esta interfaz permitirá definir distintos filtros en función de la comparación y los elementos a comparar. La clase que implemente esta interfaz será la que se encargará de controlar el proceso de filtrado.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace OpenClosedPrinciple
{
    interface IFiltro<typodefiltro>
    {
         IEnumerable<typodefiltro> Filtrar(IEnumerable<typodefiltro> elementos, IComparacion<typodefiltro> comparacion);
    }
}

Como puede observarse esta interfaz es genérica y permite definir el tipo de filtro a aplicar. El tipo de filtro nos indicará el tipo de elementos que contendrá la lista de elementos asi como el tipo de objeto que la comparación utiliza para filtrar.
Una vez definidas las interfaces, pasaremos a definir las clases que las implementan. La primera será la clase Persona (tal y como la definimos antes). El único cambio en esta clase es que ahora implementa la interfaz IElemento:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace OpenClosedPrinciple
{
    /// <summary>
    /// Clase Persona. Almacena los atributos básicos de una
    /// persona. Sirve de ejemplo para mostrar los beneficios
    /// de aplicar el principio Open Closed.
    /// </summary>
    public class Persona:IElemento
    {
       #region Class attributes
    ...
}

El siguiente paso será crear una clase que implemente la interfaz IComparacion. Crearemos una clase que, tal y como se hacía en el ejemplo original, filtre las personas por su nombre. La clase se llamará ComparacionPersonaNombre.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace OpenClosedPrinciple
{
    class CompararPersonaNombre:IComparacion<IElemento>
    {
          private string nombre;

         #region Miembros de IComparacion<string>

         public bool CompararCon(IElemento item)
        {
              return nombre.Equals(((Persona)item).Name);
        }

       #endregion

       public CompararPersonaNombre(string nombre)
       {
             this.nombre = nombre;
       }
   }
}

Esta clase recibe como parámetro un nombre con el que se quiere comparar el elemento que recibe como parámetro el método CompararCon (método definido en la interfaz). Como queremos comparar un nombre con el nombre de una Persona, lo que haces es un cast al objeto item para obtener el nombre de la persona.

Lo último que nos queda por hacer es definir la clase Filtro, que implementará la interfaz IFiltro y que será la encargada de controlar todo el filtrado.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace OpenClosedPrinciple
{
      class Filtro:IFiltro<IElemento>
      {
         #region Miembros de IFiltro<IElemento>

         public IEnumerable<IElemento> Filtrar(IEnumerable<IElemento> elementos, IComparacion<IElemento> comparacion)
         {
             foreach (var elemento in elementos)
             {
                if(comparacion.CompararCon(elemento))
                   yield return elemento;
             }
         }

         #endregion
     }
}

Como puede verse, en la implementación del método Filtrar, lo que se hace es recorrer la lista de elementos y aplicar a cada elemento la comparación. Si es válida se añade el elemento a la lista de resultados.

Y con esto ya está todo. Si, es evidente que no es tan simple como antes. No es complicado, digamos que no es trivial. Pero el diseño es mucho más limpio, más cohesionado, menos acoplado y mucho más mantenible.

Por último muestro una pequeña aplicación de tipo Consola que hace uso de todo esto:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace OpenClosedPrinciple
{
    class Program
    {
      static void Main(string[] args)
      {
           //Creamos una lista de personas.
           IEnumerable<IElemento> personas = listaPersonas();

           //Creamos una comparación por nombre para filtrar las personas
           //por aquellas que se llamen "j"
           CompararPersonaNombre compararNombre = new CompararPersonaNombre("j");

           //Creamos el objeto filtro que va a filtrar la lista de personas
           Filtro filtro = new Filtro();

           IEnumerable<IElemento> filtradas = filtro.Filtrar(personas, compararNombre);

           //Mostramos el resultado
           foreach (Persona persona in filtradas)
           {
             Console.WriteLine(persona.Name);
           }
           Console.ReadKey();
      }

      private static IEnumerable<IElemento> listaPersonas()
      {
          yield return new Persona("j", "j", 25);
          yield return new Persona("p", "p", 35);
          yield return new Persona("k", "k", 45);
          yield return new Persona("h", "j", 15);
      }
   }
}

Y eso es todo acerca de este principio. Espero que os resulte interesante y, sobretodo, útil.

Gracias a todos!