También podría usar algún tipo de marco de IoC, como Spring.NET, para inyectar el diccionario. De esta forma, si obtiene un nuevo tipo de mensaje, no tiene que volver a compilar este concentrador central, solo cambie un archivo de configuración.
El ejemplo tan esperado:
Crear una nueva aplicación de consola, llamada ejemplo, y añadir lo siguiente:
using System;
using System.Collections.Generic;
using Spring.Context.Support;
namespace Example
{
internal class Program
{
private static void Main(string[] args)
{
MessageBroker broker = (MessageBroker) ContextRegistry.GetContext()["messageBroker"];
broker.Dispatch(null, new Type1EventArgs());
broker.Dispatch(null, new Type2EventArgs());
broker.Dispatch(null, new EventArgs());
}
}
public class MessageBroker
{
private Dictionary<Type, object> handlers;
public Dictionary<Type, object> Handlers
{
get { return handlers; }
set { handlers = value; }
}
public void Dispatch<T>(object sender, T e) where T : EventArgs
{
object entry;
if (Handlers.TryGetValue(e.GetType(), out entry))
{
MessageHandler<T> handler = entry as MessageHandler<T>;
if (handler != null)
{
handler.HandleMessage(sender, e);
}
else
{
//I'd log an error here
Console.WriteLine("The handler defined for event type '" + e.GetType().Name + "' doesn't implement the correct interface!");
}
}
else
{
//I'd log a warning here
Console.WriteLine("No handler defined for event type: " + e.GetType().Name);
}
}
}
public interface MessageHandler<T> where T : EventArgs
{
void HandleMessage(object sender, T message);
}
public class Type1MessageHandler : MessageHandler<Type1EventArgs>
{
public void HandleMessage(object sender, Type1EventArgs args)
{
Console.WriteLine("Type 1, " + args.ToString());
}
}
public class Type2MessageHandler : MessageHandler<Type2EventArgs>
{
public void HandleMessage(object sender, Type2EventArgs args)
{
Console.WriteLine("Type 2, " + args.ToString());
}
}
public class Type1EventArgs : EventArgs {}
public class Type2EventArgs : EventArgs {}
}
y un archivo app.config:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<sectionGroup name="spring">
<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core"/>
<section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core"/>
</sectionGroup>
</configSections>
<spring>
<context>
<resource uri="config://spring/objects"/>
</context>
<objects xmlns="http://www.springframework.net">
<object id="messageBroker" type="Example.MessageBroker, Example">
<property name="handlers">
<dictionary key-type="System.Type" value-type="object">
<entry key="Example.Type1EventArgs, Example" value-ref="type1Handler"/>
<entry key="Example.Type2EventArgs, Example" value-ref="type2Handler"/>
</dictionary>
</property>
</object>
<object id="type1Handler" type="Example.Type1MessageHandler, Example"/>
<object id="type2Handler" type="Example.Type2MessageHandler, Example"/>
</objects>
</spring>
</configuration>
Salida:
Type 1, Example.Type1EventArgs
Type 2, Example.Type2EventArgs
No handler defined for event type: EventArgs
Como puede ver, MessageBroker
no tiene conocimiento de ninguno de los controladores, y los controladores no saben acerca de MessageBroker
. Todo el mapeo se hace en la aplicación.config, de modo que si necesita manejar un nuevo tipo de evento, puede agregarlo en el archivo de configuración. Esto es especialmente bueno si otros equipos están definiendo tipos de eventos y manejadores: pueden simplemente compilar sus cosas en un dll, lo dejan caer en su implementación y simplemente agregan un mapeo.
El diccionario tiene valores de tipo objeto en lugar de MessageHandler<>
porque los controladores reales no se pueden convertir a MessageHandler<EventArgs>
, así que tuve que hackear un poco ese aspecto. Creo que la solución aún está limpia y maneja bien los errores de mapeo. Tenga en cuenta que también deberá hacer referencia a Spring.Core.dll en este proyecto. Puede encontrar las bibliotecas here y la documentación here. El es relevante para esto. También tenga en cuenta que no hay ninguna razón por la que necesite usar Spring.NET para esto; la idea importante aquí es la inyección de dependencia. De alguna manera, algo va a necesitar decirle al agente que envíe mensajes de tipo a a x, y usar un contenedor IoC para la inyección de dependencia es una buena manera de que el agente no sepa sobre x, y viceversa.
Algunos otros SO pregunta relacionada con la COI y DI:
Me gustan las respuestas aquí, pero tal vez en algún lugar como RefactorMyCode.com podría ser un mejor lugar para ello. Es un poco más fácil para todos publicar código en sus respuestas en ese sitio. –
Ok, publiqué el ejemplo, compruébalo –