El objetivo de mi tarea es diseñar un sistema pequeño que pueda ejecutar tareas recurrentes programadas. Una tarea recurrente es algo así como "enviar un correo electrónico al administrador cada hora de 8:00 a. M. A 5:00 p. M., De lunes a viernes".
Tengo una clase base llamada RecurringTask .
public abstract class RecurringTask{
// I've already figured out this part
public bool isOccuring(DateTime dateTime){
// implementation
}
// run the task
public abstract void Run(){
}
}
Y tengo varias clases que se heredan de RecurringTask . Uno de ellos se llama SendEmailTask .
public class SendEmailTask : RecurringTask{
private Email email;
public SendEmailTask(Email email){
this.email = email;
}
public override void Run(){
// need to send out email
}
}
Y tengo un servicio de correo electrónico que me puede ayudar a enviar un correo electrónico.
La última clase es RecurringTaskScheduler , es responsable de cargar tareas desde la caché o la base de datos y ejecutar la tarea.
public class RecurringTaskScheduler{
public void RunTasks(){
// Every minute, load all tasks from cache or database
foreach(RecuringTask task : tasks){
if(task.isOccuring(Datetime.UtcNow)){
task.run();
}
}
}
}
Aquí está mi problema: ¿dónde debo poner EmailService ?
Opción 1 : inyectar EmailService en SendEmailTask
public class SendEmailTask : RecurringTask{
private Email email;
public EmailService EmailService{ get; set;}
public SendEmailTask (Email email, EmailService emailService){
this.email = email;
this.EmailService = emailService;
}
public override void Run(){
this.EmailService.send(this.email);
}
}
Ya hay algunas discusiones sobre si debemos inyectar un servicio en una entidad, y la mayoría de la gente está de acuerdo en que no es una buena práctica. Ver este artículo .
Opción 2: si ... lo demás en RecurringTaskScheduler
public class RecurringTaskScheduler{
public EmailService EmailService{get;set;}
public class RecurringTaskScheduler(EmailService emailService){
this.EmailService = emailService;
}
public void RunTasks(){
// load all tasks from cache or database
foreach(RecuringTask task : tasks){
if(task.isOccuring(Datetime.UtcNow)){
if(task is SendEmailTask){
EmailService.send(task.email); // also need to make email public in SendEmailTask
}
}
}
}
}
Me han dicho si ... De lo contrario, el elenco como el anterior no es OO, y traerá más problemas.
Opción 3: cambie la firma de Ejecutar y cree ServiceBundle .
public class ServiceBundle{
public EmailService EmailService{get;set}
public CleanDiskService CleanDiskService{get;set;}
// and other services for other recurring tasks
}
Inyecte esta clase en RecurringTaskScheduler
public class RecurringTaskScheduler{
public ServiceBundle ServiceBundle{get;set;}
public class RecurringTaskScheduler(ServiceBundle serviceBundle){
this.ServiceBundle = ServiceBundle;
}
public void RunTasks(){
// load all tasks from cache or database
foreach(RecuringTask task : tasks){
if(task.isOccuring(Datetime.UtcNow)){
task.run(serviceBundle);
}
}
}
}
El método Run de SendEmailTask sería
public void Run(ServiceBundle serviceBundle){
serviceBundle.EmailService.send(this.email);
}
No veo grandes problemas con este enfoque.
Opción 4 : Patrón de visitante.
La idea básica es crear un visitante que encapsule servicios al igual que ServiceBundle .
public class RunTaskVisitor : RecurringTaskVisitor{
public EmailService EmailService{get;set;}
public CleanDiskService CleanDiskService{get;set;}
public void Visit(SendEmailTask task){
EmailService.send(task.email);
}
public void Visit(ClearDiskTask task){
//
}
}
Y también necesitamos cambiar la firma del método Run . El método Run de SendEmailTask es
public void Run(RecurringTaskVisitor visitor){
visitor.visit(this);
}
Es una implementación típica del Patrón de visitante, y el visitante será inyectado en RecurringTaskScheduler .
En resumen: entre estos cuatro enfoques, ¿cuál es el mejor para mi escenario? ¿Y hay alguna gran diferencia entre la Opción 3 y la Opción 4 para este problema?
¿O tienes una mejor idea sobre este problema? ¡Gracias!
Actualización 22/05/2015 : Creo que la respuesta de Andy resume muy bien mi intención; Si todavía está confundido sobre el problema en sí, sugiero leer su publicación primero.
Me acabo de enterar que mi problema es muy similar al problema de Despacho de mensajes , que lleva a la Opción 5.
Opción 5 : Convertir mi problema a Despacho de mensajes .
Hay un mapeo uno a uno entre mi problema y el problema de envío de mensajes :
Despachador de mensajes : reciba IMessage y envíe subclases de IMessage a sus controladores correspondientes. → RecurringTaskScheduler
IMessage : una interfaz o una clase abstracta. → Tarea recurrente
Mensaje A : se extiende desde IMessage y tiene información adicional. → SendEmailTask
MensajeB : Otra subclase de IMessage . → CleanDiskTask
MessageAHandler : cuando reciba MessageA , trátelo → SendEmailTaskHandler, que contiene EmailService, y enviará un correo electrónico cuando reciba SendEmailTask
MessageBHandler : igual que MessageAHandler , pero maneja MessageB en su lugar. → CleanDiskTaskHandler
La parte más difícil es cómo enviar diferentes tipos de IMessage a diferentes manejadores. Aquí hay un enlace útil .
Realmente me gusta este enfoque, no contamina mi entidad con el servicio, y no tiene ninguna clase de Dios .
SendEmailTask
me parece más un servicio que una entidad. Iría por la opción 1 sin dudarlo.
accept
s visitantes. La motivación para Visitor es que tiene muchos tipos de clases en algunos agregados que necesitan visitas, y no es conveniente modificar su código para cada nueva funcionalidad (operación). Todavía no veo cuáles son esos objetos agregados, y creo que Visitor no es apropiado. Si es el caso, debe editar su pregunta (que se refiere al visitante).