El patrón decorador es uno de mis favoritos porque, en mi opinión, permite añadir funcionalidad a un sistema de una forma muy sencilla. Si además contamos con algún tipo de contenedor de inversión de control, podemos añadir esa funcionalidad de una manera muy limpia y aséptica, sin tocar el código que ya existe y acercándonos al ideal del OCP.
Suelo utilizar Castle Windsor como contenedor de inversión de control en mis aplicaciones, y aunque me costó encontrarla, existe una forma idiomática de configurar cadenas de decoradores en Windsor.
Supongamos que tenemos una serie de servicios definidos así:
public interface IService { void DoSomething(); } public class Service : IMyService { public void DoSomething() { // ... } } public class SecurityDecorator : IMyService { private readonly IService inner; public SecurityDecorator(IService inner) { this.inner = inner; } public void DoSomething() { // Validar que se puede ejecutar la acción // ... // Delegar en el servicio que estamos decorando inner.DoSomething(); } } public class AuditDecorator : IMyService { private readonly IService inner; public AuditDecorator(IService inner) { this.inner = inner; } public void DoSomething() { // Delegar en el servicio que estamos decorando inner.DoSomething(); // Auditar la acción // ... } }
Si queremos registrarlos para que SecurityService
sea un decorador de AuditService
y que éste, a su vez, decore al Service
real, bastaría con hacer lo siguiente:
var container = new WindsorContainer(); container.Register( Component.For<IService>().ImplementedBy<SecurityDecorator>(), Component.For<IService>().ImplementedBy<AuditDecorator>(), Component.For<IService>().ImplementedBy<Service>());
Lo que hace Windsor a la hora de resolver los componentes, es buscar las implementaciones de las dependencias de cada componente, pero en ese proceso tiene en cuenta cual es el componente que estamos resolviendo y lo saca de la lista de candidatos posibles para satisfacer la dependencia para evitar entrar en un bucle infinito.
De esta forma, con el registro que aparece arriba, cuando le pedimos a Windsor resolver un IService
, devuelve el primer componente registrado (que es el componente por defecto, al menos hasta Winsor 3.0). Para construir un SecurityDecorator
, Windsor necesita una instancia de IService
, puesto que SecurityDecorator
recibe un parámetro de ese tipo en el contructor (inner
). Para crear ese IService
, Windsor busca la siguiente clase que esté registrada como IService
. Por tanto, Windsor selecciona AuditDecorator
para satisfacer la dependencia sobre IService
de SecurityDecorator
. Cuando Windsor va a instanciar AuditDecorator
… bueno, ya se ve claro lo que pasa.
Lo mejor de esta forma de registrar decoradores es que queda muy claro el orden en que se ejecutan y quién decora a quién. Además, el que sea tan fácil crear la cadena de decoradores ayuda a emplear más este patrón que, en muchas ocasiones, ayuda enormemente a simplificar el diseño y evitar saturar una clase con demasiadas responsabilidades.
Pingback: Modificar una clase en Javascript: Eliminar los markers de Google Maps « Koalite's blog
Pingback: AOP con Castle Windsor: IInterceptor « Koalite's blog