Pipeline

Procesa una solicitud mediante manejadores que le aplican propiedades de manera progresiva.

Contexto

Necesitas aplicar filtros a una imagen. Imagen --> (blanco y negro) --> (fondo borroso) --> (recorte) -> (redimensión) -> Imagen resultante

Problema

Si los pasos están acoplados, no puedes reordenarlos ni reutilizarlos, además de que el método crecería bastante. Si agregamos más cantidad de filtros aún mas importante será poder tener cada parte del proceso separada. En ocasiones en otros tipos de negocios vas a requerir que un pipeline también sea un step porque requerís de sub procesos.

Solución

Los manejadores no se conocen entre ellos, solo saben que trabajo deben hacer, con qué y cuando.

#Ejemplo en C#

public record ImageContext(ExpandoObject Image, HashSet<Type> RequestedFilters);

public interface IStep<TContext>
{
	bool CanExecute(TContext context);
	void Execute(TContext context);
}

public class BackAndWhiteFilter : IStep<ImageContext>
{
	public bool CanExecute(ImageContext context)
	{
		return context.RequestedFilters.Contains(typeof(BackAndWhiteFilter));
	}

	public void Execute(ImageContext context)
	{
		
		dynamic image = context.Image;
		image.IsBlackAndWhite = true;
	}
}
public class ResizeFilter : IStep<ImageContext>
{
	public bool CanExecute(ImageContext context)
	{
		return context.RequestedFilters.Contains(typeof(ResizeFilter));
	}

	public void Execute(ImageContext context)
	{
		dynamic image = context.Image;
		image.IsResized = true;
	}
}
public class BlurFilter : IStep<ImageContext>
{
	public bool CanExecute(ImageContext context)
	{
		return context.RequestedFilters.Contains(typeof(BlurFilter));
	}

	public void Execute(ImageContext context)
	{
		dynamic image = context.Image;
		image.IsBlurred = true;
	}
}

public interface IPipeline<TContext> : IStep<TContext>
{
	IPipeline<TContext> AddStep(IStep<TContext> context);
}
public class Pipeline<TContext> : IPipeline<TContext>
{
	private readonly List<IStep<TContext>> _steps = new();

	public IPipeline<TContext> AddStep(IStep<TContext> context)
	{
		_steps.Add(context);
		return this;
	}

	public bool CanExecute(TContext context) => true;

	public void Execute(TContext context)
	{
		foreach (var step in _steps)
		{
			if(step.CanExecute(context))
				step.Execute(context);
		}
	}
}
Cuándo NO aplicarlo

Cuando no es necesario procesar la información de forma secuencial, o cuando no pueden identificarse pasos claros e independientes que complementen, transformen o enriquezcan progresivamente el contexto.

Tradeoffs
Pro Contra
Reordenar y reutilizar pasos Difícil saber quién procesó la request

#behavioral #chain