c# - office - registrador memorandum
¿Es una buena práctica tener un registrador como singleton? (8)
Esto también se está poniendo molesto, no es DRY
Es verdad. Pero hay mucho que puedes hacer por una preocupación transversal que se extiende a todos los tipos que tienes. Debe usar el registrador en todas partes, por lo que debe tener la propiedad en esos tipos.
Así que veamos qué podemos hacer al respecto.
Semifallo
Los singletons son terribles <flame-suit-on>
.
Recomiendo seguir con la inyección de propiedades como lo hiciste con tu segundo ejemplo. Este es el mejor factoring que puede hacer sin recurrir a la magia. Es mejor tener una dependencia explícita que ocultarla a través de un singleton.
Pero si los singletons le ahorran un tiempo considerable, incluyendo todas las refactorizaciones que tendrá que hacer (¡bola de cristal!), Supongo que podría vivir con ellas. Si alguna vez hubo un uso para un Singleton, este podría ser. Tenga en cuenta el costo, si alguna vez quiere cambiar de opinión, será tan alto como sea posible.
Si hace esto, revise las respuestas de otras personas usando el patrón de Registry
(vea la descripción) y las que registran una fábrica de singleton (reajustable) en lugar de una instancia de registrador de singleton.
Hay otras alternativas que podrían funcionar igual de bien sin comprometerse demasiado, por lo que debe verificarlas primero.
Fragmentos de código de Visual Studio
Podría usar fragmentos de código de Visual Studio para acelerar la entrada de ese código repetitivo. Podrás escribir algo así como la pestaña logger
, y el código aparecerá mágicamente para ti.
Usar AOP para DRY off
Podría eliminar un poco de ese código de inyección de propiedad usando un marco de Programación Orientada a Aspectos (AOP) como PostSharp para autogenerar parte de él.
Podría parecerse a esto cuando hayas terminado:
[InjectedLogger]
public ILogger Logger { get; set; }
También podría usar su método para rastrear el código de muestra para rastrear automáticamente el código de entrada y salida del método, lo que podría eliminar la necesidad de agregar algunas de las propiedades del registrador. Puede aplicar el atributo a nivel de clase, o ancho de espacio de nombres:
[Trace]
public class MyClass
{
// ...
}
// or
#if DEBUG
[assembly: Trace( AttributeTargetTypes = "MyNamespace.*",
AttributeTargetTypeAttributes = MulticastAttributes.Public,
AttributeTargetMemberAttributes = MulticastAttributes.Public )]
#endif
Tenía la costumbre de pasar logger a constructor, como:
public class OrderService : IOrderService {
public OrderService(ILogger logger) {
}
}
Pero eso es bastante molesto, así que lo he usado como una propiedad por un tiempo:
private ILogger logger = NullLogger.Instance;
public ILogger Logger
{
get { return logger; }
set { logger = value; }
}
Esto también se está poniendo molesto, no está seco, necesito repetir esto en todas las clases. Podría usar la clase base, pero de nuevo, estoy usando la clase Form, así que necesitaría FormBase, etc. Así que, creo, ¿cuál sería el inconveniente de tener singleton con ILogger expuesto, por lo que uno sabría dónde obtenerlo?
Infrastructure.Logger.Info("blabla");
ACTUALIZACIÓN: Como Merlyn notó correctamente, debo mencionar, que en los ejemplos primero y segundo estoy usando DI.
Buena pregunta. Creo que en la mayoría de los proyectos el registrador es un singleton.
Algunas ideas solo vienen a mi mente:
- Use ServiceLocator (u otro contenedor de Inyección de Dependencia si ya usa alguno) que le permite compartir el registrador entre los servicios / clases, de esta forma puede crear instancias de registrador o incluso múltiples registradores diferentes y compartirlos a través de ServiceLocator que obviamente sería un singleton , algún tipo de Inversión de control . Este enfoque le brinda mucha flexibilidad sobre el proceso de inicialización y creación de instancias de un registrador.
- Si necesita registrador en casi todas partes, implemente métodos de extensión para el tipo de
Object
para que cada clase pueda llamar a los métodos del registrador comoLogInfo()
,LogDebug()
,LogError()
Hay un libro Dependency Injection en .NET. En función de lo que necesita, debe usar la interceptación.
En este libro hay un diagrama que ayuda a decidir si se usa inyección de Constructor, inyección de propiedad, inyección de método, contexto ambiental o intercepción.
Así es como uno razona usando este diagrama:
- ¿Tienes dependencia o lo necesitas? - Necesito
- ¿Es una preocupación transversal? - Sí
- ¿Necesitas una respuesta? - No
Usar interceptación
Otra solución que personalmente encuentro más fácil es usar una clase Logger estática. Puede llamarlo desde cualquier método de clase sin tener que cambiar la clase, por ejemplo, agregar inyección de propiedad, etc. Es bastante simple y fácil de usar.
Logger::initialize ("filename.log", Logger::LEVEL_ERROR); // only need to be called once in your application
Logger::log ("my error message", Logger::LEVEL_ERROR); // to be used in every method where needed
Puse una instancia de registrador en mi contenedor de inyección de dependencia, que luego inyecta el registrador en las clases que lo necesitan.
Si desea ver una buena solución para iniciar sesión, le sugiero que consulte el motor de la aplicación google con python, donde el registro es tan simple como el import logging
y luego puede simplemente logging.debug("my message")
o logging.info("my message")
que realmente lo mantiene tan simple como debería.
Java no tenía una buena solución para el registro, es decir, log4j debería evitarse ya que prácticamente te obliga a utilizar singletons que, como aquí, es "terrible" y he tenido una experiencia horrible al tratar de hacer que la salida de registro sea la misma declaración de registro solo una vez cuando sospecho que la razón para el doble registro fue que tengo un Singleton del objeto de registro en dos cargadores de clases en la misma máquina virtual (!)
Le ruego me disculpe por no ser tan específico con C #, pero por lo que he visto, las soluciones con C # tienen un aspecto similar a Java, donde teníamos log4j y también deberíamos convertirlo en singleton.
Es por eso que realmente me gustó la solución con GAE / python , es tan simple como puede ser y usted no tiene que preocuparse por los cargadores de clases, obtener una declaración de doble registro o cualquier diseño patterna para nada.
Espero que parte de esta información pueda ser relevante para usted y espero que quiera echarle un vistazo a la solución de registro de I que recomiendo, en lugar de limitar el problema de Singleton debido a la imposibilidad de tener un singleton real cuando debe estar instanciando en varios clasificadores.
Un simple singleton no es una buena idea. Hace que sea difícil reemplazar el registrador. Tiendo a usar filtros para mis registradores (algunas clases "ruidosas" solo pueden registrar avisos / errores).
Uso el patrón de singleton combinado con el patrón de proxy para la fábrica de registradores:
public class LogFactory
{
private static LogFactory _instance;
public static void Assign(LogFactory instance)
{
_instance = instance;
}
public static LogFactory Instance
{
get { _instance ?? (_instance = new LogFactory()); }
}
public virtual ILogger GetLogger<T>()
{
return new SystemDebugLogger();
}
}
Esto me permite crear una FilteringLogFactory
o simplemente una SimpleFileLogFactory
sin cambiar ningún código (y por lo tanto cumplir con el principio de Open / Closed).
Extensión de muestra
public class FilteredLogFactory : LogFactory
{
public override ILogger GetLogger<T>()
{
if (typeof(ITextParser).IsAssignableFrom(typeof(T)))
return new FilteredLogger(typeof(T));
return new FileLogger(@"C:/Logs/MyApp.log");
}
}
Y para usar la nueva fábrica
// and to use the new log factory (somewhere early in the application):
LogFactory.Assign(new FilteredLogFactory());
En tu clase que debería iniciar sesión:
public class MyUserService : IUserService
{
ILogger _logger = LogFactory.Instance.GetLogger<MyUserService>();
public void SomeMethod()
{
_logger.Debug("Welcome world!");
}
}
Un singleton es una buena idea. Una idea aún mejor es usar el patrón de registro , que da un poco más de control sobre la creación de instancias. En mi opinión, el patrón singleton está muy cerca de las variables globales. Con una creación o reutilización de objetos de manejo de registro, hay espacio para cambios futuros en las reglas de creación de instancias.
El Registro en sí puede ser una clase estática para dar una sintaxis simple para acceder al registro:
Registry.Logger.Info("blabla");