Das Ziel meiner Aufgabe ist es, ein kleines System zu entwerfen, das geplante wiederkehrende Aufgaben ausführen kann. Eine wiederkehrende Aufgabe ist etwa "Senden Sie von Montag bis Freitag stündlich von 8.00 bis 17.00 Uhr eine E-Mail an den Administrator".
Ich habe eine Basisklasse namens 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(){
}
}
Und ich habe mehrere Klassen, die von RecurringTask geerbt werden . Eine davon heißt SendEmailTask .
public class SendEmailTask : RecurringTask{
private Email email;
public SendEmailTask(Email email){
this.email = email;
}
public override void Run(){
// need to send out email
}
}
Und ich habe einen EmailService, der mir beim Versenden einer E-Mail helfen kann.
Die letzte Klasse ist RecurringTaskScheduler . Sie ist dafür verantwortlich, Aufgaben aus dem Cache oder der Datenbank zu laden und die Aufgabe auszuführen.
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();
}
}
}
}
Hier ist mein Problem: Wo soll ich EmailService platzieren ?
Option 1 : Injizieren Sie EmailService in 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);
}
}
Es gibt bereits einige Diskussionen darüber, ob wir einem Unternehmen einen Service hinzufügen sollten, und die meisten Menschen sind sich einig, dass dies keine gute Praxis ist. Siehe diesen Artikel .
Option 2: Wenn ... sonst in 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
}
}
}
}
}
Mir wurde gesagt, wenn ... Sonst und Besetzung wie oben ist nicht OO, und wird mehr Probleme bringen.
Option 3: Ändern Sie die Signatur von Ausführen und erstellen Sie ServiceBundle .
public class ServiceBundle{
public EmailService EmailService{get;set}
public CleanDiskService CleanDiskService{get;set;}
// and other services for other recurring tasks
}
Fügen Sie diese Klasse in RecurringTaskScheduler ein
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);
}
}
}
}
Die Run- Methode von SendEmailTask wäre
public void Run(ServiceBundle serviceBundle){
serviceBundle.EmailService.send(this.email);
}
Ich sehe keine großen Probleme mit diesem Ansatz.
Option 4 : Besuchermuster.
Die Grundidee besteht darin, einen Besucher zu erstellen, der Dienste wie ServiceBundle kapselt .
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){
//
}
}
Außerdem müssen wir die Signatur der Run- Methode ändern . Die Run- Methode von SendEmailTask lautet
public void Run(RecurringTaskVisitor visitor){
visitor.visit(this);
}
Dies ist eine typische Implementierung des Besuchermusters, und der Besucher wird in RecurringTaskScheduler eingefügt .
Zusammenfassend: Welcher dieser vier Ansätze ist der beste für mein Szenario? Und gibt es für dieses Problem einen großen Unterschied zwischen Option3 und Option4?
Oder haben Sie eine bessere Vorstellung von diesem Problem? Vielen Dank!
Update 22.05.2015 : Ich denke, Andys Antwort fasst meine Absicht wirklich gut zusammen. Wenn Sie immer noch verwirrt sind über das Problem selbst, empfehle ich, zuerst seinen Beitrag zu lesen.
Ich habe gerade herausgefunden, dass mein Problem dem Problem des Nachrichtenversands sehr ähnlich ist , was zu Option 5 führt.
Option 5 : Konvertieren Sie mein Problem in Message Dispatch .
Es gibt eine Eins-zu-Eins-Zuordnung zwischen meinem Problem und dem Problem des Nachrichtenversands :
Message Dispatcher : Empfangen Sie IMessage und senden Sie Unterklassen von IMessage an die entsprechenden Handler. → RecurringTaskScheduler
IMessage : Eine Schnittstelle oder eine abstrakte Klasse. → RecurringTask
MessageA : Erweitert sich aus IMessage und enthält einige zusätzliche Informationen. → SendEmailTask
NachrichtB : Eine weitere Unterklasse von IMessage . → CleanDiskTask
MessageAHandler : Wenn Sie MessageA empfangen , behandeln Sie es → SendEmailTaskHandler, das EmailService enthält, und senden Sie eine E-Mail, wenn SendEmailTask empfangen wird
MessageBHandler : Wie MessageAHandler , jedoch stattdessen MessageB verarbeiten . → CleanDiskTaskHandler
Der schwierigste Teil ist, wie verschiedene Arten von IMessage an verschiedene Handler gesendet werden. Hier ist ein nützlicher Link .
Ich mag diesen Ansatz wirklich, er verschmutzt mein Wesen nicht mit Dienst und es gibt keine Gottklasse .
SendEmailTask
scheint mir eher eine Dienstleistung als eine Einheit zu sein. Ich würde ohne zu zögern für Option 1 gehen.
accept
Besucher sind. Die Motivation für Besucher ist, dass Sie viele Klassentypen in einem Aggregat haben, die besucht werden müssen, und es nicht bequem ist, ihren Code für jede neue Funktionalität (Operation) zu ändern. Ich sehe immer noch nicht, was diese aggregierten Objekte sind, und denke, dass Besucher nicht angemessen ist. Wenn dies der Fall ist, sollten Sie Ihre Frage bearbeiten (die sich auf den Besucher bezieht).