WWW.PRACTICASDESOFTWARE.COM.AR

Criteria
Patrón de diseño
Andrés Grosso – andres.grosso@engee.com.ar
20/09/2010

El patrón Criteria es una adaptación del patrón Specification y es utilizado para filtrar colecciones de objetos de una
manera desacoplada.
Criteria
Andrés Grosso – andres.grosso@engee.com.ar

Índice
Definición ..................................................................................................................................................................................... 3
Propósito ..................................................................................................................................................................................... 3
Motivación ................................................................................................................................................................................... 3
Estructura .................................................................................................................................................................................... 4
Implementación ........................................................................................................................................................................... 6
Ejemplo ........................................................................................................................................................................................ 8
Referencias .................................................................................................................................................................................. 9

2
Criteria
Andrés Grosso – andres.grosso@engee.com.ar

Definición
Criteria 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.

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.

3
Criteria
Andrés Grosso – andres.grosso@engee.com.ar

Estructura
Estructura básica del patrón independiente del lenguaje de programación.
A continuación se presentan dos maneras de representar la estructura del patrón.

a)

b)

4
Criteria
Andrés Grosso – andres.grosso@engee.com.ar

Aquí se presenta para .NET

c)

5
Criteria
Andrés Grosso – andres.grosso@engee.com.ar

Implementación
Implementación específica para .NET, la cual utiliza métodos de extensión en lugar de la clase abstracta
de “CompositeCriteriaOOP”.
Según la necesidad específica para cada caso, se puede determinar que si ya se encuentra un objeto
que cumple con el criterio se salga del resto de los criterios anidados (como en este ejemplo), teniendo
como objetivo final devolver siempre un único objeto.
En el caso de que se necesite que devuelva todos los ítems que cumplen con el criterio, sea 1 o más, se
debe modificar la implementación del método “MeetCriteria” de la clase “AndCriteria”.
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);
//
//
//
//
if

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
(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;
}

6
Criteria
Andrés Grosso – andres.grosso@engee.com.ar

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);
}
}

7
Criteria
Andrés Grosso – andres.grosso@engee.com.ar

Ejemplo
Suponiendo el escenario de un centro médico, donde por cada paciente hay una colección de ítems de
atención, los cuales pueden ser indicaciones (laboratorio, rayos, radiografías, ect.) o turnos, el proceso
que determina que turno e indicación deben ser atendidas primero, puede ser implementado con el
patrón Criteria.
Para este escenario de ejemplo, básicamente existen una colección de turnos e indicaciones para un
paciente, y según ciertos criterios se debe elegir a un solo turno y una sola indicación. El turno e
indicación que cumplan con los criterios serán los que primero realizará el paciente.
static void Main(string[] args)
{
// Se generan 3 turnos para el paciente Juan Perez
List<Turno> turnos = FillTestTurnos();
// Se generan 5 indicaciones para el paciente Juan Perez
List<Indicacion> indicaciones = FillTestIndicaciones();
/* Se recomienda utilizar un factory para la creación de los criterios */
// Le da prioridad a los items que tengan el estado ‘ProximaAtencion’
ICriteria<Item> proxAtenc = new CriterioProximaAtencion();
// Le da prioridad a los items que tengan el flag de urgente en true
ICriteria<Item> urgente = new CriterioUrgente();
// Le da prioridad al ítem con el menor orden cronológico
ICriteria<Item> ordCron = new CriterioOrdenCronologico();
// Retorna unicamente los items que tengan por lo menos un consultorio habilitado
// (que puedan ser atendidos)
ICriteria<Item> consulHabil = new CriterioConsultorioHabiitado();
// Le da prioridad a las indicaciones que tengan el menor tiempo de espera
ICriteria<Indicacion> tiemEsp = new CriterioTiempoEspera();
// Se crean los criterios de los turnos según orden y necesidad
ICriteria<Item> turnosCriteria = consulHabil.
And(proxAtenc).
And(urgente).
And(ordCron);
// Se crean los criterios de las indicaciones según orden y necesidad
ICriteria<Item> indicacionesCriteria = consulHabil.
And(proxAtenc.And(urgente)).
Or(tiemEsp.And(ordCron));
var turnos = turnosCriteria.MeetCriteria(turnos);
var indicaciones = indicacionesCriteria.MeetCriteria(indicaciones);
// En este ejemplo de uso del patrón, se necesita obtener un sólo turno y una sola
// indicación. Con lo cual las colecciones de turnos e indicaciones tendrán siempre
// un único ítem.
var turno = turnos.FirstOrDefault();
var indicacion = indicaciones.FirstOrDefault();
Console.WriteLine(string.Format("El Turno que será atendido es el {0}",
turno.idTurno));
Console.WriteLine(string.Format("La indicacion que será atendida es {0}",
indicacion.idIndicacion));
Console.ReadLine();
}

8
Criteria
Andrés Grosso – andres.grosso@engee.com.ar

Referencias
-

http://martinfowler.com/apsupp/spec.pdf

9

Patrón de diseño Criteria

  • 1.
    WWW.PRACTICASDESOFTWARE.COM.AR Criteria Patrón de diseño AndrésGrosso – andres.grosso@engee.com.ar 20/09/2010 El patrón Criteria es una adaptación del patrón Specification y es utilizado para filtrar colecciones de objetos de una manera desacoplada.
  • 2.
    Criteria Andrés Grosso –andres.grosso@engee.com.ar Índice Definición ..................................................................................................................................................................................... 3 Propósito ..................................................................................................................................................................................... 3 Motivación ................................................................................................................................................................................... 3 Estructura .................................................................................................................................................................................... 4 Implementación ........................................................................................................................................................................... 6 Ejemplo ........................................................................................................................................................................................ 8 Referencias .................................................................................................................................................................................. 9 2
  • 3.
    Criteria Andrés Grosso –andres.grosso@engee.com.ar Definición Criteria 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. 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. 3
  • 4.
    Criteria Andrés Grosso –andres.grosso@engee.com.ar Estructura Estructura básica del patrón independiente del lenguaje de programación. A continuación se presentan dos maneras de representar la estructura del patrón. a) b) 4
  • 5.
    Criteria Andrés Grosso –andres.grosso@engee.com.ar Aquí se presenta para .NET c) 5
  • 6.
    Criteria Andrés Grosso –andres.grosso@engee.com.ar Implementación Implementación específica para .NET, la cual utiliza métodos de extensión en lugar de la clase abstracta de “CompositeCriteriaOOP”. Según la necesidad específica para cada caso, se puede determinar que si ya se encuentra un objeto que cumple con el criterio se salga del resto de los criterios anidados (como en este ejemplo), teniendo como objetivo final devolver siempre un único objeto. En el caso de que se necesite que devuelva todos los ítems que cumplen con el criterio, sea 1 o más, se debe modificar la implementación del método “MeetCriteria” de la clase “AndCriteria”. 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); // // // // if 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 (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; } 6
  • 7.
    Criteria Andrés Grosso –andres.grosso@engee.com.ar 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); } } 7
  • 8.
    Criteria Andrés Grosso –andres.grosso@engee.com.ar Ejemplo Suponiendo el escenario de un centro médico, donde por cada paciente hay una colección de ítems de atención, los cuales pueden ser indicaciones (laboratorio, rayos, radiografías, ect.) o turnos, el proceso que determina que turno e indicación deben ser atendidas primero, puede ser implementado con el patrón Criteria. Para este escenario de ejemplo, básicamente existen una colección de turnos e indicaciones para un paciente, y según ciertos criterios se debe elegir a un solo turno y una sola indicación. El turno e indicación que cumplan con los criterios serán los que primero realizará el paciente. static void Main(string[] args) { // Se generan 3 turnos para el paciente Juan Perez List<Turno> turnos = FillTestTurnos(); // Se generan 5 indicaciones para el paciente Juan Perez List<Indicacion> indicaciones = FillTestIndicaciones(); /* Se recomienda utilizar un factory para la creación de los criterios */ // Le da prioridad a los items que tengan el estado ‘ProximaAtencion’ ICriteria<Item> proxAtenc = new CriterioProximaAtencion(); // Le da prioridad a los items que tengan el flag de urgente en true ICriteria<Item> urgente = new CriterioUrgente(); // Le da prioridad al ítem con el menor orden cronológico ICriteria<Item> ordCron = new CriterioOrdenCronologico(); // Retorna unicamente los items que tengan por lo menos un consultorio habilitado // (que puedan ser atendidos) ICriteria<Item> consulHabil = new CriterioConsultorioHabiitado(); // Le da prioridad a las indicaciones que tengan el menor tiempo de espera ICriteria<Indicacion> tiemEsp = new CriterioTiempoEspera(); // Se crean los criterios de los turnos según orden y necesidad ICriteria<Item> turnosCriteria = consulHabil. And(proxAtenc). And(urgente). And(ordCron); // Se crean los criterios de las indicaciones según orden y necesidad ICriteria<Item> indicacionesCriteria = consulHabil. And(proxAtenc.And(urgente)). Or(tiemEsp.And(ordCron)); var turnos = turnosCriteria.MeetCriteria(turnos); var indicaciones = indicacionesCriteria.MeetCriteria(indicaciones); // En este ejemplo de uso del patrón, se necesita obtener un sólo turno y una sola // indicación. Con lo cual las colecciones de turnos e indicaciones tendrán siempre // un único ítem. var turno = turnos.FirstOrDefault(); var indicacion = indicaciones.FirstOrDefault(); Console.WriteLine(string.Format("El Turno que será atendido es el {0}", turno.idTurno)); Console.WriteLine(string.Format("La indicacion que será atendida es {0}", indicacion.idIndicacion)); Console.ReadLine(); } 8
  • 9.
    Criteria Andrés Grosso –andres.grosso@engee.com.ar Referencias - http://martinfowler.com/apsupp/spec.pdf 9