c# - patron - ¿Qué patrón usar para iniciar sesión? Inyección de Dependencia o Localizador de Servicio?
inyección de dependencias en spring (9)
Cambiamos todos nuestros atributos de Registro / Seguimiento a PostSharp (marco AOP). Todo lo que necesita hacer para crear un registro para un método es agregarle el atributo.
Beneficios:
- Uso fácil de AOP
- Separación clara de preocupaciones
- Ocurre en tiempo de compilación -> Impacto mínimo en el rendimiento
Mira this .
Considera este escenario. Tengo una lógica comercial que de vez en cuando se requerirá para escribir en un registro.
interface ILogger
{
void Log(string stuff);
}
interface IDependency
{
string GetInfo();
}
class MyBusinessObject
{
private IDependency _dependency;
public MyBusinessObject(IDependency dependency)
{
_dependency = dependency;
}
public string DoSomething(string input)
{
// Process input
var info = _dependency.GetInfo();
var intermediateResult = PerformInterestingStuff(input, info);
if (intermediateResult== "SomethingWeNeedToLog")
{
// How do I get to the ILogger-interface?
}
var result = PerformSomethingElse(intermediateResult);
return result;
}
}
¿Cómo obtendrías la interfaz de ILogger? Veo dos posibilidades principales;
- Pasarlo usando Dependency Injection en el constructor.
- Obténgalo a través de un Localizador de servicios singleton.
¿Qué método preferirías y por qué? ¿O hay un patrón aún mejor?
Actualización: Tenga en cuenta que no necesito registrar TODAS las llamadas a métodos. Solo quiero registrar algunos (raros) eventos que pueden ocurrir o no en mi método.
DI funcionaría bien aquí. Otra cosa a mirar sería AOP .
El registrador es claramente un servicio del que depende la lógica de su negocio, y por lo tanto debe tratarse como una dependencia de la misma forma que lo hace con IDependency
. Inyecta el registrador en tu constructor.
Nota: aunque se menciona el AOP como la forma de inyectar el registro, no estoy de acuerdo en que sea la solución en este caso. AOP funciona muy bien para el seguimiento de la ejecución, pero nunca será una solución para el registro como parte de la lógica empresarial.
Mi pequeña regla general:
Si está en una biblioteca de clases, use la inyección de constructor o la inyección de propiedades con un patrón de objeto nulo.
Si está en una aplicación principal, use el localizador de servicios (o singleton).
Me parece que esto se aplica bastante bien cuando se usa log4net. No desea que las bibliotecas de clase lleguen a cosas que podrían no estar allí, pero en un programa de aplicación, usted sabe que el registrador estará allí, y las bibliotecas como log4net se basan en gran medida en el patrón de ubicación del servicio.
Tiendo a pensar en iniciar sesión como algo suficientemente estático que realmente no necesita DI. Es extremadamente improbable que alguna vez cambie la implementación del registro en una aplicación, especialmente porque cada estructura de registro es increíblemente flexible y fácil de extender. Es más importante en las bibliotecas de clase cuando es posible que su biblioteca deba ser utilizada por varias aplicaciones que ya utilizan diferentes registradores.
YMMV, por supuesto. DI es genial, pero eso no significa que todo deba ser DIED.
Preferiría el servicio de Singleton.
La inyección de dependencia atormentará al constructor.
Si puedes usar AOP, eso sería lo mejor.
Puede derivar otro tipo, por ejemplo, LoggableBusinessObject
que toma un registrador en su constructor. Esto significa que solo pasa en el registrador los objetos que lo usarán:
public class MyBusinessObject
{
private IDependency _dependency;
public MyBusinessObject(IDependency dependency)
{
_dependency = dependency;
}
public virtual string DoSomething(string input)
{
// Process input
var info = _dependency.GetInfo();
var result = PerformInterestingStuff(input, info);
return result;
}
}
public class LoggableBusinessObject : MyBusinessObject
{
private ILogger _logger;
public LoggableBusinessObject(ILogger logger, IDependency dependency)
: base(dependency)
{
_logger = logger;
}
public override string DoSomething(string input)
{
string result = base.DoSomething(input);
if (result == "SomethingWeNeedToLog")
{
_logger.Log(result);
}
}
}
Tal vez esto sea poco oftálico, pero ¿por qué necesitamos inyectar logger en absoluto, cuando podemos simplemente escribir al principio de la clase?
Logger logger = LogManager.GetLogger("MyClassName");
El registrador no cambia durante el desarrollo y más tarde durante el mantenimiento. Los registradores modernos son altamente personalizables, por lo que el argumento
¿y si quiero reemplazar el registrador de texto con la base de datos?
se extraña.
No niego el uso de la inyección de dependencia, solo tengo curiosidad sobre tu mente.
Yo personalmente hago una mezcla de ambos.
Aquí están mis convenciones:
- Desde un contexto estático - Ubicación del servicio
- Desde un contexto de instancia - Inyección de dependencia
Siento que esto me da el equilibrio correcto de capacidad de prueba. Me resulta un poco más difícil configurar las pruebas contra las clases que utilizan la ubicación del servicio que el uso de DI, por lo que Service Location termina siendo la excepción en lugar de la regla. Sin embargo, soy consistente en su uso, así que no es difícil recordar qué tipo de prueba necesito escribir.
Algunos han planteado la preocupación de que DI tiende a saturar a los constructores. No creo que esto sea un problema, pero si se siente de esta manera, existen varias alternativas que usan DI, pero evite los parámetros de los constructores. Aquí hay una lista de los métodos DI de Ninject: http://ninject.codeplex.com/wikipage?title=Injection%20Patterns
Descubrirá que la mayoría de los contenedores de Inversión de Control tienen las mismas características que Ninject. Elegí mostrar Ninject porque tienen las muestras más concisas.
Espero que esto sea útil.
Editar: Para ser claros, uso Unity y Common Service Locator . Tengo una instancia única de mi contenedor de Unity para DI y mi implementación de IServiceLocator es simplemente un contenedor alrededor de ese contenedor Singleton de Unity. De esta forma, no tengo que hacer ningún tipo de mapeo dos veces ni nada de eso.
Tampoco considero que AOP sea particularmente útil más allá del rastreo. Me gusta más el registro manual simplemente por su claridad. Sé que la mayoría de los marcos de registro de AOP son capaces de ambos, pero no necesito el anterior (pan y mantequilla de AOP) la mayor parte del tiempo. Esto es solo preferencia personal, por supuesto.
Yo recomendaría ninguno de estos enfoques. Es mejor usar programación orientada a aspectos. La explotación forestal es el "mundo de saludo" de AOP.