Patrón Criteria (patrón de diseño)
En diseño de software, el patrón de especificación (y en su caso particular, criteria o filter) es un patrón de diseño, mediante el cual, se permite filtrar una colección de objetos bajo diversos criterios, encadenándolos de una manera desacoplada por medio de operaciones lógicas.
Este patrón se utiliza en escenarios específicos, donde la obtención de uno o más entidades depende de reglas de negocio. Fue creado por Eric Evans y Martin Fowler

Propósito 
Filtrar colecciones de objetos según diversos criterios de forma desacoplada.
Permitir la reutilización y anidación de criterios por medio de operaciones lógicas (and, or, not). Generar una manera legible y extensible de agregar o quitar lógica para filtrar colecciones de objetos.

Motivación 
Frecuentemente, se necesita filtrar colecciones de objetos de la misma familia (clase base) utilizando criterios similares, pero en distinto orden y/o condición.
También a menudo, esta lógica para filtrar colecciones es un proceso clave o complejo como pueden ser los criterios de planificación en un Sistema Operativo o el manejo de prioridades para una cola de peticiones.
Otra caso sería el tener altas posibilidades de cambio en el futuro sobre la lógica que realiza el filtrado en esta colección de objetos. Sea quitando/modificando criterios existentes, como así también agregando nuevos.
Este patrón pone gran énfasis en la extensibilidad y reutilización de criterios, como así también en la legibilidad del código.

Ventajas 
 Extensibilidad
 Legibilidad
 Reutilización de criterios
 Facilidad para hacer test unitarios (cada criterio se puede probar independiente, el patrón asegura el uso colectivo)
 Activación de criterios en tiempo de ejecución (dependiendo del lenguaje de programación)

Estructura 
Estructura básica del patrón independiente del lenguaje de programación.

Estructura .NET 
Aquí se presenta para .NET, utilizando métodos de extensión

Implementación (C#) 
public interface ICriteria<E>
{
   List<E> MeetCriteria(List<E> entities); 
}

internal class AndCriteria<E> : ICriteria<E>
{
   private ICriteria<E> _criteria;
   private ICriteria<E> _otherCriteria;
  
   internal AndCriteria(ICriteria<E> criteria, ICriteria<E> otherCriteria)
   {
      _criteria = criteria;
      _otherCriteria = otherCriteria;
   }

   public List<E> MeetCriteria(List<E> entities)
   {
      var result = _criteria.MeetCriteria(entities);
      // Si ya devuelve 1, es que uno solo cumplio el criterio y no
      // se ejecutan los ands anidados
      // Si ya devuelve 0, es que ninguno cumplio con el criterio y
      // no se ejecutan los ands anidados
      if (result.Count == 0 || result.Count == 1)
         return result;
      
      return _otherCriteria.MeetCriteria(result);
   }
}

internal class OrCriteria<E> : ICriteria<E>
{
   private ICriteria<E> _criteria;
   private ICriteria<E> _otherCriteria;

   internal OrCriteria(ICriteria<E> criteria, ICriteria<E> otherCriteria)
   {
      _criteria = criteria;
      _otherCriteria = otherCriteria;
   }

   public List<E> MeetCriteria(List<E> entities)
   {
      List<E> firstCriteriaItems = _criteria.MeetCriteria(entities);
      List<E> otherCriteriaItems = _otherCriteria.MeetCriteria(entities);
    
      foreach (E otherCriteriaItem in otherCriteriaItems)
      {
         if(!firstCriteriaItems.Contains(otherCriteriaItem))
            firstCriteriaItems.Add(otherCriteriaItem);
      }
     
      return firstCriteriaItems;
   }
}

internal class NotCriteria<E> : ICriteria<E>
{
   private ICriteria<E> _criteria;

   internal NotCriteria(ICriteria<E> x)
   {
      _criteria = x;
   }

   public List<E> MeetCriteria(List<E> entities)
   {
      List<E> notCriteriaItems = _criteria.MeetCriteria(entities);
  
      foreach (E notCriteriaItem in notCriteriaItems)
         entities.Remove(notCriteriaItem);
 
      return entities;
   }
}

public static class CriteriaExtensionMethods
{
   public static ICriteria<E> And<E>(this ICriteria<E> criteria, ICriteria<E> otherCriteria)
   {
      return new AndCriteria<E>(criteria, otherCriteria);
   }

   public static ICriteria<E> Or<E>(this ICriteria<E> criteria, ICriteria<E> otherCriteria)
   {
      return new OrCriteria<E>(criteria, otherCriteria);
   }

   public static ICriteria<E> Not<E>(this ICriteria<E> criteria)
   {
      return new NotCriteria<E>(criteria);
   }
}

Ejemplo de uso 
Suponiendo la existencia de la clase Persona
public class Persona
{
   public int Edad { get; set; }
   public string Nombre { get; set; }
   public string Descripcion { get; set; }
   public string Apellido { get; set; }
   public Sexo Sexo { get; set; }
   public Origen Origen { get; set; }
   public EstadoMarital EstadoMarital { get; set; }
   public Profesion Profesion { get; set; }
}

Y creando estos criterios:
public class CriterioMasculinos : ICriteria<Persona>
    {
        public List<Persona> MeetCriteria(List<Persona> entities)
        {
            var hombres =   from h in entities
                            where h.Sexo == Sexo.Masculino
                            select h;

            return hombres.ToList();
        }
    }

    public class CriterioFemeninos : ICriteria<Persona>
    {
        public List<Persona> MeetCriteria(List<Persona> entities)
        {
            var mujeres =   from m in entities
                            where m.Sexo == Sexo.Femenino
                            select m;

            return mujeres.ToList();
        }
    }
    

    public class CriterioExtranjeros : ICriteria<Persona>
    {
        public List<Persona> MeetCriteria(List<Persona> entities)
        {
            var personas = from h in entities
                          where h.Origen == Origen.Extranjero
                          select h;

            return personas.ToList();
        }
    }

    public class CriterioSolteros : ICriteria<Persona>
    {
        public List<Persona> MeetCriteria(List<Persona> entities)
        {
            var personas = from h in entities
                           where h.EstadoMarital == EstadoMarital.Soltero
                           select h;

            return personas.ToList();
        }
    }

Podríamos filtrar a los hombres extranjeros y solteros
ICriteria<Persona> masculino = new CriterioMasculinos();
ICriteria<Persona> femenino = new CriterioFemeninos();
ICriteria<Persona> soltero = new CriterioSolteros();
ICriteria<Persona> extranjero = new CriterioExtranjeros();

/* ---------- HOMBRES EXTRANJEROS Y SOLTEROS ---------- */
criterios =  masculino.And(extranjero).And(soltero);

foreach (var persona in criterios.MeetCriteria(personas))
   Console.WriteLine(persona.Descripcion);

Console.ReadLine();

O a las mujeres extranjeras
ICriteria<Persona> masculino = new CriterioMasculinos();
ICriteria<Persona> femenino = new CriterioFemeninos();
ICriteria<Persona> soltero = new CriterioSolteros();
ICriteria<Persona> extranjero = new CriterioExtranjeros();

/* ---------- MUJERES EXTRANJERAS ---------- */
criterios = femenino.And(extranjero); // esto seria lo mismo que masculino.Not().And(extranjero)

foreach (var persona in criterios.MeetCriteria(personas))
   Console.WriteLine(persona.Descripcion);

Console.ReadLine();

O también podríamos filtrar a los hombres o mujeres (a todos)
ICriteria<Persona> masculino = new CriterioMasculinos();
ICriteria<Persona> femenino = new CriterioFemeninos();
ICriteria<Persona> soltero = new CriterioSolteros();
ICriteria<Persona> extranjero = new CriterioExtranjeros();

/* ---------- HOMBRES O MUJERES ---------- */
criterios = masculino.Or(femenino);

foreach (var persona in criterios.MeetCriteria(personas))
   Console.WriteLine(persona.Descripcion);

Console.ReadLine();

Referencias

Enlaces externos 

 Specifications by Eric Evans and Martin Fowler
 Prácticas de software.
 Definición del patrón

Software 
Patrones de diseño
En diseño de software , el patrón de especificación ( y en su caso particular , criteria o filter ) es un patrón de diseño , mediante el cual , se permite filtrar una colección de objetos bajo diversos criterios , encadenando los de una manera desacoplada por medio de operaciones lógicas . Este patrón se utiliza en escenarios específicos , donde la obtención de uno o más entidades depende de reglas de negocio . Fue creado por Eric Evans y Martin Fowler Propósito Filtrar colecciones de objetos según diversos criterios de forma desacoplada . Permitir la reutilización y anidación de criterios por medio de operaciones lógicas ( and , or , not ) . Generar una manera legible y extensible de agregar o quitar lógica para filtrar colecciones de objetos . Motivación Frecuentemente , se necesita filtrar colecciones de objetos de la misma familia ( clase base ) utilizando criterios similares , pero en distinto orden y/o condición . También a menudo , esta lógica para filtrar colecciones es un proceso clave o complejo como pueden ser los criterios de planificación en un Sistema Operativo o el manejo de prioridades para una cola de peticiones . Otra caso sería el tener altas posibilidades de cambio en el futuro sobre la lógica que realiza el filtrado en esta colección de objetos . Sea quitando/modificando criterios existentes , como así también agregando nuevos . Este patrón pone gran énfasis en la extensibilidad y reutilización de criterios , como así también en la legibilidad del código . Ventajas Extensibilidad Legibilidad Reutilización de criterios Facilidad para hacer test unitarios ( cada criterio se puede probar independiente , el patrón asegura el uso colectivo ) Activación de criterios en tiempo de ejecución ( dependiendo del lenguaje de programación ) Estructura Estructura básica del patrón independiente del lenguaje de programación . Estructura\.NET Aquí se presenta para\.NET , utilizando métodos de extensión Implementación ( C # ) public interface ICriteria<E> { List<E> MeetCriteria( List<E> entities ) ; } internal class AndCriteria<E> \ : ICriteria<E> { private ICriteria<E> _criteria ; private ICriteria<E> _otherCriteria ; internal AndCriteria( ICriteria<E> criteria , ICriteria<E> otherCriteria ) { _criteria = criteria ; _otherCriteria = otherCriteria ; } public List<E> MeetCriteria( List<E> entities ) { var result = _criteria.MeetCriteria( entities ) ; // Si ya devuelve es que uno solo cumplio el criterio y no // se ejecutan los ands anidados // Si ya devuelve es que ninguno cumplio con el criterio y // no se ejecutan los ands anidados if ( result.Count ==|| result.Count == return result ; return _otherCriteria.MeetCriteria( result ) ; } } internal class OrCriteria<E> \ : ICriteria<E> { private ICriteria<E> _criteria ; private ICriteria<E> _otherCriteria ; internal OrCriteria( ICriteria<E> criteria , ICriteria<E> otherCriteria ) { _criteria = criteria ; _otherCriteria = otherCriteria ; } public List<E> MeetCriteria( List<E> entities ) { List<E> firstCriteriaItems = _criteria.MeetCriteria( entities ) ; List<E> otherCriteriaItems = _otherCriteria.MeetCriteria( entities ) ; foreach ( E otherCriteriaItem in otherCriteriaItems ) { if( ! firstCriteriaItems.Contains ( otherCriteriaItem ) ) firstCriteriaItems.Add ( otherCriteriaItem ) ; } return firstCriteriaItems ; } } internal class NotCriteria<E> \ : ICriteria<E> { private ICriteria<E> _criteria ; internal NotCriteria( ICriteria<E> x ) { _criteria = x ; } public List<E> MeetCriteria( List<E> entities ) { List<E> notCriteriaItems = _criteria.MeetCriteria( entities ) ; foreach ( E notCriteriaItem in notCriteriaItems ) entities.Remove ( notCriteriaItem ) ; return entities ; } } public static class CriteriaExtensionMethods { public static ICriteria<E> And<E>( this ICriteria<E> criteria , ICriteria<E> otherCriteria ) { return new AndCriteria<E>( criteria , otherCriteria ) ; } public static ICriteria<E> Or<E>( this ICriteria<E> criteria , ICriteria<E> otherCriteria ) { return new OrCriteria<E>( criteria , otherCriteria ) ; } public static ICriteria<E> Not<E>( this ICriteria<E> criteria ) { return new NotCriteria<E>( criteria ) ; } } Ejemplo de uso Suponiendo la existencia de la clase Persona public class Persona { public int Edad { get ; set ; } public string Nombre { get ; set ; } public string Descripcion { get ; set ; } public string Apellido { get ; set ; } public Sexo Sexo { get ; set ; } public Origen Origen { get ; set ; } public EstadoMarital EstadoMarital { get ; set ; } public Profesion Profesion { get ; set ; } } Y creando estos criterios : public class CriterioMasculinos \ : ICriteria<Persona> { public List<Persona> MeetCriteria( List<Persona> entities ) { var hombres = from h in entities where h.Sexo == Sexo.Masculino select h ; return hombres.ToList ( ) ; } } public class CriterioFemeninos \ : ICriteria<Persona> { public List<Persona> MeetCriteria( List<Persona> entities ) { var mujeres = from m in entities where m.Sexo == Sexo.Femenino select m ; return mujeres.ToList ( ) ; } } public class CriterioExtranjeros \ : ICriteria<Persona> { public List<Persona> MeetCriteria( List<Persona> entities ) { var personas = from h in entities where h.Origen == Origen.Extranjero select h ; return personas.ToList ( ) ; } } public class CriterioSolteros \ : ICriteria<Persona> { public List<Persona> MeetCriteria( List<Persona> entities ) { var personas = from h in entities where h.EstadoMarital == EstadoMarital.Soltero select h ; return personas.ToList ( ) ; } } Podríamos filtrar a los hombres extranjeros y solteros ICriteria<Persona> masculino = new CriterioMasculinos ( ) ; ICriteria<Persona> femenino = new CriterioFemeninos ( ) ; ICriteria<Persona> soltero = new CriterioSolteros ( ) ; ICriteria<Persona> extranjero = new CriterioExtranjeros ( ) ; /* ---------- HOMBRES EXTRANJEROS Y SOLTEROS ---------- */ criterios = masculino.And ( extranjero ) .And( soltero ) ; foreach ( var persona in criterios.MeetCriteria ( personas ) ) Console.WriteLine ( persona.Descripcion ) ; Console.ReadLine ( ) ; O a las mujeres extranjeras ICriteria<Persona> masculino = new CriterioMasculinos ( ) ; ICriteria<Persona> femenino = new CriterioFemeninos ( ) ; ICriteria<Persona> soltero = new CriterioSolteros ( ) ; ICriteria<Persona> extranjero = new CriterioExtranjeros ( ) ; /* ---------- MUJERES EXTRANJERAS ---------- */ criterios = femenino.And ( extranjero ) ; // esto seria lo mismo que masculino.Not ( ) .And( extranjero ) foreach ( var persona in criterios.MeetCriteria ( personas ) ) Console.WriteLine ( persona.Descripcion ) ; Console.ReadLine ( ) ; O también podríamos filtrar a los hombres o mujeres ( a todos ) ICriteria<Persona> masculino = new CriterioMasculinos ( ) ; ICriteria<Persona> femenino = new CriterioFemeninos ( ) ; ICriteria<Persona> soltero = new CriterioSolteros ( ) ; ICriteria<Persona> extranjero = new CriterioExtranjeros ( ) ; /* ---------- HOMBRES O MUJERES ---------- */ criterios = masculino.Or ( femenino ) ; foreach ( var persona in criterios.MeetCriteria ( personas ) ) Console.WriteLine ( persona.Descripcion ) ; Console.ReadLine ( ) ; Referencias Enlaces externos Specifications by Eric Evans and Martin Fowler Prácticas de software . Definición del patrón Software Patrones de diseño