Está en la página 1de 23

Object Pool

Object Pool (OP) es un patrón creacional, integrante los “gang of four”. Este
consiste en una clase, que contiene dentro de ella una colección de objetos
previamente instanciados, listos para ser utilizados. El OP, es quien tiene la
mayoría de responsabilidades en cuanto al manejo de los objetos se refiere.
Cuando una clase solicita un objeto, la clase OP se encarga de retornar este
objeto, y moverlo a una estructura de datos, generalmente una lista, de objetos
que están en uso en este momento. Dependiendo de la implementación y el
contexto del OP, este puede llegar a quedarse sin objetos para retornar, en este
caso, OP se encarga de instanciar un nuevo objeto, agregarlo a la lista de
objetos disponibles, y retornarlo. Además, para evitar que la clase sea
innecesariamente grande, OP puede tener un atributo que indique la cantidad
máxima de objetos que puede contener; si la cantidad llega a este límite y un
objeto es solicitado, se debe esperar a que algún objeto vuelva a estar disponible.
El OP, suele instanciarse como singleton. De esta manera, hay un mejor manejo
de los objetos, ya que estos volverían siempre a la misma clase OP de donde
salieron.

Qué problema resuelve

El propósito del OP es utilizarlo en situaciones que no se quiere instanciar nuevos


objetos constantemente debido a que el costo de instanciarlos puede volverse
bastante grande cuando se necesita lidiar con una gran cantidad de delegación de
objetos, por lo tanto soluciona problemas de eficiencia. Normalmente se utiliza
cuando la creación de un objeto nuevo involucra realizar un query a un servidor o
hacer parsing a un archivo, lo cual son operaciones que involucran utilizar un
medio más lento que la memoria RAM. Usando un objet pool los objetos se
instancian únicamente una vez, realizando la operación lenta solo en este
momento, y luego el objeto puede reutilizarse con esta información. Comúnmente
utilizado en aplicaciones multi-thread, y en conexiones con bases de datos.

Propuesta UML

Casos en los que se puede utilizar


El object pool puede ser utilizado en varios casos, algunos de estos son:

 En Unity (motor de video juegos) se utiliza porque si uno va a crear un


objeto visual, usarlo, y destruirlo en un lapso muy corto, sale muy caro,
sería mejor utilizar el objeto, y cuando este se vaya a destruir, usarlo de
nuevo en vez de crear otro.

 Conexiones a bases de datos: Las conexiones a bases de datos consisten


de un objeto conexión el cual trae todas las propiedades necesarias para
conectarse a la base de datos, y en un sistema donde se requiere
conexiones a la base de datos constantemente un object pool llega a ser de
gran efectividad.

Ejemplo

 Contexto
 Aplicación patrón en lenguaje y SOLID

namespace DesignPattern.Objectpool
{
// The PooledObject class is the type that is expensive or slow to instantiate,
// or that has limited availability, so is to be held in the object pool.
public class PooledObject
{
   DateTime _createdAt = DateTime.Now;

   public DateTime CreatedAt


   {
       get { return _createdAt; }
   }

   public string TempData { get; set; }


}

// The Pool class is the most important class in the object pool design pattern. It
controls access to the
// pooled objects, maintaining a list of available objects and a collection of objects
that have already been
// requested from the pool and are still in use. The pool also ensures that objects
that have been released
// are returned to a suitable state, ready for the next time they are requested.
public static class Pool
{
   private static List<PooledObject> _available = new List<PooledObject>();
   private static List<PooledObject> _inUse = new List<PooledObject>();

   public static PooledObject GetObject()


   {
       lock(_available)
       {
           if (_available.Count != 0)
           {
               PooledObject po = _available[0];
               _inUse.Add(po);
               _available.RemoveAt(0);
               return po;
           }
           else
           {
               PooledObject po = new PooledObject();
               _inUse.Add(po);
               return po;
           }
       }
   }

   public static void ReleaseObject(PooledObject po)


   {
       CleanUp(po);

       lock (_available)
       {
           _available.Add(po);
           _inUse.Remove(po);
       }
   }

   private static void CleanUp(PooledObject po)


   {
       po.TempData = null;
   }
}
}
Patrones de diseño: Singleton

Cuando hablamos de un patrón de diseño nos referimos a una solución a un


problema concreto en el desarrollo de software. Pero no cualquier solución,
sólo aquellas que se ha desmostrado que son eficientes en diferentes escenarios
y reutilizables en gran cantidad de contextos de aplicaciones. Por lo tanto, aunque
los ejemplos que podamos dar estén en un lenguaje de programación concreto, la
idea será extrapolable a diferentes lenguajes de programación orientada a objetos.

Singleton

El patrón singleton consiste en crear una instancia de un objeto y solo una, para
toda nuestra aplicación. Sería como una especie de variable global que almacena
nuestro objeto.

En un primer momento esta definición puede sonar muy extraña. Por lo general,
siempre se recomienda no usar variables globales en una aplicación, y mucho
menos en programación orientada a objetos. Pero cuando hablamos de singleton,
estamos jugando a crear una especie de variable global de forma encubierta.
¿Cómo puede ser esto un patrón?

Para responder a esta pregunta vamos a proponeros dos escenarios diferentes:

 Piensa en una aplicación Web, que almacena en un objeto una serie de


valores tipo parámetros de configuración. Estos parámetros son comunes
para toda la aplicación. Se guardan en una base de datos y si son
modificados por un administrador, quedan modificados para todos los
usuarios que acceden a la página.

 Ahora vamos a imaginar que tenemos un recurso compartido como puede


ser un fichero en el que escribimos un log de la aplicación. Este log puede
ser accedido desde cualquier parte de la aplicación. Pero sabemos que un
fichero no se puede abrir si otro proceso lo abrió anteriormente y aún no lo
ha cerrado.

Para ambos problemas podemos encontrar una solución usando el patrón


singleton. Crearemos una especie de variable global, pero con unas
características concretas:

 solo se puede instanciar una vez (single-instance)


 no se debe instanciar si nunca fue utilizada 
 es thread-safe, que quiere decir que sus métodos son accesibles desde
diferentes hilos de ejecución, sin crear bloqueos ni excepciones debido a la
concurrencia
 no tiene un constructor público, luego el objeto que la usa no puede
instanciarla directamente
 posee un mecanismo para acceder a la instancia que se ha creado
(mediante una propiedad estática, por ejemplo)

Teniendo en cuenta estas características vamos a desarrollar una clase singleton:

public sealed class Singleton


{
private static Singleton instance;

private Singleton()
{
}

public static Singleton Instance


{
get
{
if (instance == null) instance = new Singleton();
return instance;
}
}
}

En esta pequeña porción de código hemos conseguido realizar una única instancia
en el momento en el que se llama por primera vez. Además hemos creado un
constructor con acceso privado para que nadie pueda instanciar la clase. Y para
terminar hemos creado una propiedad de solo lectura con la que se puede
acceder a la instancia creada. Pero ésta no será Thread-safe. Para conseguirlo
podríamos modificar la clase de la siguiente forma:

public sealed class Singleton


{
private static readonly Singleton instance = new Singleton();

private Singleton() { }

public static Singleton Instance { get { return instance; } }


}

Al crear el atributo que almacena la instancia como readonly, y al ser estática, se


instanciará al arrancar la aplicación. Así conseguiremos que sea una clase thread-
safe. Es decir, que no habrá problemas si varios procesos acceden a esta clase al
mismo tiempo. No obstante, si quisieramos respetar que solo se instanciara el
objeto bajo demanda, deberíamos usar bloqueos:
public sealed class Singleton
{
private static readonly object locker = new object();
private static volatile Singleton instance;

private Singleton() { }

public static Singleton Instance


{
get
{
if (instance == null)
{
lock (locker)
{
if (instance == null) instance = new Singleton();
}
}

return instance;
}
}
}

Gracias al bloqueo ya podremos ejecutar nuestra clase singleton en un contexto


multihilo, instanciándola sólo cuando se ha solicitado la primera vez. A este efecto
de carga en diferido se le denomina en inglés "Lazy Loading". Y desde la versión
4.0 de la framework .net se nos provee un objeto que nos ayuda a realizarla: Lazy.
Por lo que podríamos simplificar nuestro ejemplo usándolo:

public sealed class Singleton


{
private static readonly Lazy<Singleton> instance = new Lazy<Singleton>(() =>
new Singleton());

private Singleton() { }

public static Singleton Instance


{
get
{
return instance.Value;
}
}
}
El objeto Lazy ya es de por si thread-safe y en su declaración simplemente
debemos indicarle de qué forma se debe instanciar el objeto que contiene. Por
esta razón es posiblemente la mejor implementación del patrón singleton.

Si por ejemplo estuvieramos desarrollando la herramientas de log de nuestra


aplicación, bastaría con que añadieramos las funciones necesarias para escribir
en el log a nuestra clase singleton:

public sealed class Logger


{
private static readonly Lazy<Logger> instance = new Lazy<Logger>(() => new
Logger());

private Logger() { }

public static Logger Current


{
get
{
return instance.Value;
}
}

public void WriteInformation(string message)


{
// ...
}

public void WriteWarning(string message)


{
// ...
}

public void WriteError(string message)


{
// ...
}
}

Viendo este código en nuestra aplicación, está claro que para poder escribir en el
log desde cualquier punto de la misma sólo tendremos que hacer esta llamada:

Logger.Current.WriteInformation("Una información");
Logger.Current.WriteWarning("Un aviso");
Logger.Current.WriteError("Un error");
Al pararnos a pensar las consecuencias de escribir este código, caeremos en la
cuenta de que singleton nos está creando una dependencia en todo el programa
donde queramos tener información del proceso en forma de logs (eso es a lo largo
de toda la aplicación). Algo que comunmente conocemos como acoplamiento
entre clases.

El acoplamiento puede dar varios problemas a lo largo del ciclo de vida de un


software. Como por ejemplo a la hora de realizar pruebas unitarias. Pero no es
objeto de este artículo centrarse en este problema. Aunque si lo es proponer
soluciones de implementación del patrón singleton que se adapten a un desarrollo
sólido.

Si quisieramos evitar este acoplamiento, es recomendable usar un IoC Container


(Inversion Of Control Container) para respetar la "D" de los pincipios SOLID:
Dependency Inversion Principle. Esta, por así llamarla, norma nos dice que
debemos depender de las abstraciones (las interfaces, los contratos) no de
las concreciones (clases que implementan esas interfaces). 

En las frameworks de inversión de control más conocidas se han implementado


mecanismos que nos permiten crear objetos singleton desde el propio contenedor.
Esto quiere decir que simplemente tendríamos que crear una interfaz y una
implementación de la misma, sin preocuparnos de como se intancia. Visto en
forma de código sería esto:

public interface ILogger


{
void WriteInformation(string message);
void WriteWarning(string message);
void WriteError(string message);
}

public class Logger : ILogger


{
public Logger()
{
// ...
}
public void WriteInformation(string message)
{
// ...
}
public void WriteWarning(string message)
{
// ...
}
public void WriteError(string message)
{
// ...
}
}

De esta forma, delegaríamos la gestión del ciclo de vida de las instancias al IoC
Container que hayamos decidido. A continuación mostraremos cómo podemos
configurar una instancia singleton usando las frameworks de inyección de
dependencias (DI) más conocidas:

 Usando Structure maps:

// configurar
ObjectFactory.Initialize(x =>
{
x.For<ILogger>().Singleton().Use<Logger>();
}
// recoger valor
var x = ObjectFactory.GetInstance<ILogger>();

 Con Ninject:

// configurar
IKernel ninject = new StandardKernel(new InlineModule(
x => x.Bind<ILogger>().To<Logger>(),
x => x.Bind<Logger>().ToSelf().InSingletonScope()));
// recoger valor
var x = ninject.Get<ILogger>();

 Con Unity:

// configurar
IUnityContainer container = new UnityContainer();
container.RegisterType<ILogger, Logger>(new
ContainerControlledLifetimeManager());
// recoger valor
var x = container.Resolve<ILogger>();

 O con autofact:

var builder = new ContainerBuilder();


builder
.Register(c => new Logger())
.As<ilogger>()
.SingleInstance();
var container = builder.Build();
var x = container.Resolve<ilogger>();
Pero esto no quiere decir que no nos sirva la implementación de singleton que
hicimos anteriormente, ya que es posible que no nos fiemos o que nuestro
contenedor no tenga ningún artefacto que nos facilite la implementación singleton.
Para estos casos, podríamos hacer que un contenedor como Unity nos devolviera
la instancia singleton que gestiona nuestra clase usando la propiedad estática.
Simplemente tendríamos que seguir usando una interface, implementarla en
nuestra clase singleton y registrar una instancia en lugar de una clase en el
contenedor:

public interface ILogger


{
void WriteInformation(string message);
void WriteWarning(string message);
void WriteError(string message);
}

public sealed class Logger : ILogger


{
private static readonly Lazy<Logger> instance = new Lazy<Logger>(() => new
Logger());

private Logger() { }

public static Logger Current


{
get
{
return instance.Value;
}
}

public Logger()
{
// ...
}
public void WriteInformation(string message)
{
// ...
}
public void WriteWarning(string message)
{
// ...
}
public void WriteError(string message)
{
// ...
}
}

De esta forma, por ejemplo, si usamos el contenedor de Unity, tendríamos que


registrar su valor así:

var container = new UnityContainer();


container.RegisterInstance<ILogger>(Logger.Current);

Con este código sería nuestro singleton Logger quien gestione el ciclo de vida y
conseguiríamos desacoplarnos de la implementación gracias al IoC.

Podríamos hacer lo mismo con Structure maps:

ObjectFactory.Initialize(x =>
{
x.For<ILogger>().Use(Logger.Current);
}

var x = ObjectFactory.GetInstance<ILogger>();

Y para finalizar, con Ninject:

IKernel ninject = new StandardKernel(new InlineModule(


x => x.Bind<ILogger>().ToConstant(Logger.Current)));

var x = ninject.Get<ILogger>();
El patrón Composite en la práctica

Los patrones de diseño del Gang of Four son una de las piedras angulares en el
desarrollo de software. Algunos de ellos tan importantes que han sido absorbidos
por implementaciones nativas en los lenguajes de programación y los damos
como algo dado sin darnos cuenta del patrón que reside detrás (caso de Iterator).

Para aprender el funcionamiento de estos patrones se suele remitir al libro original,


aunque en mi caso los aprendí con el magnífico Head First Design Patterns.
Internet está lleno de referencias y explicaciones, por supuesto, y yo aportaré mi
granito de arena con ejemplos prácticos de uso de algunos de ellos.

Composite

Este patrón es, a mi parecer, uno de los más útiles para conseguir un código
dividido en pequeñas piezas reusables y testeables, que respetan al máximo el
Single Responsibility Principle. Esto significa que una vez tenemos desarrollada
cierta funcionalidad dentro de una clase, en pocas ocasiones tendremos un motivo
para cambiar dicha clase en el futuro. Lo veremos más claro con el ejemplo.

La definición “oficial” del patrón la podemos encontrar en Wikipedia, traducido


sería:

En Ingeniería del Software, el patrón Composite es un patrón de diseño utilizado


para particionar. El patrón Composite describe un grupo de objetos que serán
tratados de la misma forma como una instancia única de un objeto. La intención
del patrón es “componer” objetos en estructuras de árboles para representar
jerarquías. Implementar el patrón Composite permite a los clientes tratar objetos
individuales y composiciones uniformemente.

Y el diagrama UML (en el mismo sitio) es:


Por tanto tenemos que:

 Existe una interfaz Component que define una operación


 Dicha interfaz es implementada por dos clases, Leaf y Composite
 La clase Leaf contiene una implementación específica de la operación
 La clase Composite contiene un número variable de referencias (children) a
instancias de la interfaz Component. Dichas instancias pueden ser Leaf o,
de nuevo Composite, permitiendo generar las estructuras en forma de árbol
que se mencionan en la definición
 La implementación de la operación en Composite delegará en la operación
de cada una de las instancias “child”
 Las instancias de Composite están “vivas”, es decir, mediante sus
operaciones add y remove permiten configurar la jerarquía que contiene

Es este último punto precisamente el que supone un pequeño hándicap para este
patrón de diseño. En el libro de la serie “Head First” se discute bastante sobre este
asunto, el problema es que estas operaciones al ser específicas de la clase
Composite no pertenecen a la interfaz, por lo que, en cierta medida, los clientes
han de ser conscientes de las implementaciones que existen de la interfaz (y
hacer downcasting), rompiendo el principio Program to an interface, not an
implementation. Otra posible solución sería elevar las operaciones a la interfaz, y
lanzando una excepción si las invocamos desde Leaf, pero esa solución me
parece igualmente mala.

La versión inmutable

En general, el típico ejemplo para explicar el patrón Composite es una aplicación


de dibujo en la que a partir de formas geométricas básicas (círculo, cuadrado…)
creamos formas más complejas que se van agrupando. En este caso sí que tiene
sentido el carácter vivo de la clase Composite.

Sin embargo, el mayor uso que yo he ido encontrando para este patrón es
bastante diferente, y guarda relación con lo que mencionaba al principio de este
artículo. Cuando nos enfrentamos al desarrollo de aplicaciones de gran dimensión,
es muy frecuente que determinadas clases que tienen como objetivo realizar
tareas repetitivas en pasos sucesivos vayan creciendo y creciendo debido a
nuevos requerimientos.

Lo que ocurre a largo plazo es que un determinado módulo o clase se vuelve


inmanejable, y cada vez más difícil de testear. La consecuencia más directa suele
ser que se dejan de mantener los tests, lo cual es terrible, y la segunda
consecuencia es que cada vez es más difícil de mantener.

En gran medida, estos problemas pueden evitarse con lo que yo llamo la versión
inmutable del patrón Composite. Es inmutable porque se inicializa una sola vez y
no permite modificar su estrucutra, es decir, nos cargamos las operaciones add y
remove de la versión vista más arriba. Veámoslo con un ejemplo concreto.

Una implementación de ejemplo

Imaginemos un escenario en el que tenemos que validar una estructura de datos


utilizada para dar de alta un nuevo usuario en un sistema.

public class NewUserInfo {

private String name;


private String surname;
private Address address;
private int age;
private String password;
//...
}

public class Address {

private String line1;


private String line2;
private String county;
private String city;
private String postcode;
//...
}

Los datos pueden provenir de un servicio web, un formulario HTML…no es


demasiado importante para el ejemplo.
Lo que queremos es crear un clase Validator que reciba la instancia de
NewUserInfo y devuelva una lista de Strings con todos los errores encontrados
(campos sin rellenar, password insegura, menor de edad…). Es decir, queremos
esto:

public interface Validator<T> {

List<String> validate(T info);


}

La interfaz es genérica, ya que así podríamos reutilizarla para otras estrucuturas


de datos.

Sin el patrón Composite, lo normal sería crear una implementación,


NewUserInfoValidator, que fuera validando de forma sucesiva todos y cada uno de
los campos, con la complejidad añadida de que cada campo requiere de
validaciones diferentes. Por mucho que intentemos seguir las prácticas de Clean
Code, lo más normal es que esa clase crezca a varios cientos de líneas, y que
además requiera de mantenimiento posterior cada vez que necesitemos añadir un
nuevo campo a la estructura de datos o una nueva validación a uno de los campos
existentes. Por no hablar de lo monstruosos que pueden llegar a ser los tests.

Vamos, por tanto, a utilizar el patrón Composite para separar la validación en


piezas, a partir del siguiente diagrama UML:

La clase CompositeValidator

La implementación de la clase Composite sería:

public class CompositeValidator<T> implements Validator<T> {

private final List<Validator<T>> validators;


public CompositeValidator(List<Validator<T>> validators) {
this.validators = validators;
}

@Override
public List<String> validate(T info) {
List<String> errors = Lists.newArrayList();

for (Validator validator : validators) {


errors.addAll(validator.validate(info));
}

return errors;
}
}

Como vemos, no hace más que delegar la validación a cada uno de sus “hijos” y
recopilar los resultados. Lo bueno de esta estructura, es que ¡los hijos pueden ser
tanto implementaciones concretas como nuevos Composites!

Las implementaciones “concretas”

La idea es dividir las validaciones de los diferentes campos (o grupos de campos


asociados) en sus propias clases Validator. Veamos un par de ejemplos:

public class NameValidator implements Validator<NewUserInfo> {

@Override
public List<String> validate(NewUserInfo info) {
List<String> errors = Lists.newArrayList();

String name = info.getName();


String surname = info.getSurname();

if (StringUtils.isEmpty(name)) {
errors.add("Name must be populated");
}

if (StringUtils.isEmpty(surname)) {
errors.add("Surname must be populated");
}

return errors;
}
}
public class AgeValidator implements Validator<NewUserInfo> {
@Override
public List<String> validate(NewUserInfo info) {
List<String> errors = Lists.newArrayList();

int age = info.getAge();

if (age < 18) {


errors.add("Age must be >= 18");
}

return errors;
}
}

(El resto de clases Validator están en el código subido a GitHub)

Creo que queda bastante claro de qué forma se simplifica el código en pequeñas
piezas reutilizables. Un posible temor ante esta situación es encontrarnos ante un
montón de clases por mantener, pero yo diría que es mucho más fácil mantener
pequeñas clases fáciles de entender y localizar que una clase gigantesca en la
que hay que bucear para encontrar el código a modificar. Y por último reducimos
los potenciales motivos para modificar una clase determinada.

¿Cuántas veces en el futuro creéis que será necesario modificar la clase


AgeValidator o CompositeValidator? Diría que el cambio es altamente improbable.
Por otro lado, si un nuevo campo es añadido a la estructura de datos, lo único que
deberíamos hacer sería crear una nueva clase para validar ese nuevo campo y
agregarla a la jerarquía.

Juntando las piezas

La agregación de las diferentes piezas la haremos en el fichero XML para


configurar el contexto de Spring. De esta forma podemos establecer la
configuración de nuestro validador sin tocar para nada las clases.

<bean id="newUserInfoValidator"
class="com.raulavila.patterns.composite.CompositeValidator">
<constructor-arg>
<list>
<ref bean="nameAndAddressValidator" />
<ref bean="ageValidator" />
<ref bean="passwordValidator" />
</list>
</constructor-arg>
</bean>
<bean id="nameAndAddressValidator"
class="com.raulavila.patterns.composite.CompositeValidator">
<constructor-arg>
<list>
<ref bean="nameValidator" />
<ref bean="addressValidator" />
</list>
</constructor-arg>
</bean>

<bean id="nameValidator"
class="com.raulavila.patterns.composite.NameValidator"/>

<bean id="addressValidator"
class="com.raulavila.patterns.composite.AddressValidator"/>

<bean id="ageValidator"
class="com.raulavila.patterns.composite.AgeValidator"/>

<bean id="passwordValidator"
class="com.raulavila.patterns.composite.PasswordValidator"/>

Es un poco forzado el segundo nivel creado con nameAndAddressValidator, que


he añadido a efectos de demostración. En proyectos más reales se utilizarían los
diferentes niveles en la jerarquía para agrupar validaciones de campos
relacionados, o agrupar diferentes tipos de validaciones en el mismo campo (por
ejemplo, separar una validación básica con otra más compleja que necesitara
comunicarse con un sistema externo, por ejemplo).

Veamos un ejemplo del funcionamiento de nuestro validador:

ApplicationContext context = new ClassPathXmlApplicationContext(


"spring-beans-composite.xml");

@SuppressWarnings("unchecked")
Validator<NewUserInfo> newInfoUserValidator =
context.getBean("newUserInfoValidator", Validator.class);

NewUserInfo newUserInfo = new NewUserInfo();

List<String> errors = newInfoUserValidator.validate(newUserInfo);

System.out.println(errors);
La salida de esta pequeña aplicación, dado que estamos validando una instancia
sin rellenar (por lo que contiene los valores por defecto) sería una lista con los
siguientes mensajes:

"Name must be populated",


"Surname must be populated",
"Address can't be null",
"Age must be >= 18", //age contiene el valor por defecto para int => 0
"Password field must be populated"
Los tests

Veamos por último lo sencillos que quedarían algunos de los tests. Empecemos
con el test de la clase CompositeValidator:

public class CompositeValidatorTest {

private CompositeValidator<Object> compositeValidator;

@Mock
private Validator<Object> validator1;
@Mock
private Validator<Object> validator2;

@Mock
private Object info;

@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);

List<Validator<Object>> validators =
Lists.newArrayList(validator1, validator2);

compositeValidator = new CompositeValidator<Object>(validators);

when(validator1.validate(info)).thenReturn(Lists.newArrayList("error1"));
when(validator2.validate(info)).thenReturn(Lists.newArrayList("error2"));

@Test
public void testComposite() throws Exception {
List<String> errors = compositeValidator.validate(info);

verify(validator1).validate(info);
verify(validator2).validate(info);
assertThat(errors).containsExactly("error1", "error2");
}
}

Se comprueba que la clase Composite delega en sus hijos, como es de esperar.

Cada una de las clases Validator en particular tendría su propia clase específica
de test, en el caso de AgeValidator sería algo como:

public class AgeValidatorTest {

private AgeValidator ageValidator = new AgeValidator();

@Mock
private NewUserInfo newUserInfo;

@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}

@Test
public void testAdult() throws Exception {
when(newUserInfo.getAge()).thenReturn(20);

List<String> errors = ageValidator.validate(newUserInfo);


assertThat(errors).isEmpty();
}

@Test
public void testChild() throws Exception {
when(newUserInfo.getAge()).thenReturn(15);

List<String> errors = ageValidator.validate(newUserInfo);


assertThat(errors).hasSize(1);
assertThat(errors).contains("Age must be >= 18");
}
}

También podría gustarte