Мета мого завдання - розробити невелику систему, яка може виконувати заплановані повторювані завдання. Повторне завдання - це на кшталт "надсилати електронному листу адміністратору щогодини з 8:00 до 17:00, з понеділка по п’ятницю".
У мене базовий клас під назвою 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(){
}
}
І у мене є кілька класів, які успадковані від RecurringTask . Один з них називається SendEmailTask .
public class SendEmailTask : RecurringTask{
private Email email;
public SendEmailTask(Email email){
this.email = email;
}
public override void Run(){
// need to send out email
}
}
І у мене є EmailService, який може допомогти мені надіслати електронний лист.
Останній клас - RecurringTaskScheduler , він відповідає за завантаження завдань з кешу чи бази даних та виконання завдання.
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();
}
}
}
}
Ось моя проблема: куди мені поставити EmailService ?
Option1 : Inject EmailService в 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);
}
}
Вже є деякі дискусії щодо того, чи варто вводити послугу в організацію, і більшість людей погоджуються, що це не є хорошою практикою. Дивіться цю статтю .
Варіант2: Якщо ... інше в 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
}
}
}
}
}
Мені сказали, якщо ... Інакше і так, як це було вище, це не ОО, і це принесе більше проблем.
Варіант3: Змініть підпис Run і створіть ServiceBundle .
public class ServiceBundle{
public EmailService EmailService{get;set}
public CleanDiskService CleanDiskService{get;set;}
// and other services for other recurring tasks
}
Введіть цей клас у 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);
}
}
}
}
Run метод SendEmailTask буде
public void Run(ServiceBundle serviceBundle){
serviceBundle.EmailService.send(this.email);
}
Я не бачу великих проблем з таким підходом.
Варіант4 : шаблон відвідувачів.
Основна ідея - створити відвідувача, який буде інкапсулювати послуги так само, як 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){
//
}
}
І нам також потрібно змінити підпис методу Run . Run метод SendEmailTask є
public void Run(RecurringTaskVisitor visitor){
visitor.visit(this);
}
Це типова реалізація Шаблона відвідувачів, і відвідувач буде введений у RecurringTaskScheduler .
Підсумовуючи: Серед цих чотирьох підходів, який із моїх сценаріїв найкращий? І чи є велика різниця між Варіантом 3 та Варіантом 4 для цієї проблеми?
Або ви краще розумієте цю проблему? Дякую!
Оновлення 22.05.2015 : Я думаю, що відповідь Енді дуже добре підсумовує мій намір; якщо ви все ще плутаєтесь із самою проблемою, пропоную спочатку прочитати його допис.
Щойно я з’ясував, що моя проблема дуже схожа на проблему відправки повідомлень , що призводить до варіанту5.
Варіант 5 : Перетворіть мою проблему на розсилку повідомлень .
Між моєю проблемою та проблемою диспетчеризації повідомлень відображається індивідуальне відображення :
Диспетчер повідомлень : Одержуйте повідомлення та диспетчери підкласів частування відповідних обробників. → RecurringTaskScheduler
Посилання : інтерфейс або абстрактний клас. → Повторне завдання
ПовідомленняA : поширюється на функцію IMessage , має додаткову інформацію. → SendEmailTask
MessageB : Ще один підклас IMessage . → CleanDiskTask
MessageAHandler : Коли ви отримаєте MessageA , обробіть його → SendEmailTaskHandler, який містить EmailService, і надішле електронний лист, коли отримає SendEmailTask
MessageBHandler : Те саме, що і MessageAHandler , але обробляйте MessageB . → CleanDiskTaskHandler
Найскладніша частина полягає в тому, як розсилати різного роду повідомлення для різних обробників. Ось корисне посилання .
Мені дуже подобається такий підхід, він не забруднює мою сутність службою і не має класу Бога .
SendEmailTask
мені здається більше схожим на послугу, ніж на сутність. Я б пішов на варіант 1 без вагань.
accept
відвідувачів. Мотивація для відвідувача полягає в тому, що у вас є багато типів класу в якомусь сукупності, які потребують відвідування, і не зручно змінювати їх код для кожного нового функціоналу (операції). Я все ще не бачу, що це об'єднані об'єкти, і вважаю, що відвідувач не підходить. Якщо це так, слід відредагувати своє запитання (яке стосується відвідувача).