Tujuan dari tugas saya adalah merancang sistem kecil yang dapat menjalankan tugas yang dijadwalkan berulang. Tugas berulang adalah sesuatu seperti "mengirim email ke administrator setiap jam dari jam 8:00 hingga 5:00 sore, Senin hingga Jumat".
Saya memiliki kelas dasar yang disebut 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(){
}
}
Dan saya memiliki beberapa kelas yang diwarisi dari RecurringTask . Salah satunya disebut SendEmailTask .
public class SendEmailTask : RecurringTask{
private Email email;
public SendEmailTask(Email email){
this.email = email;
}
public override void Run(){
// need to send out email
}
}
Dan saya memiliki Layanan Email yang dapat membantu saya mengirim email.
Kelas terakhir adalah RecurringTaskScheduler , yang bertanggung jawab untuk memuat tugas dari cache atau database dan menjalankan tugas.
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();
}
}
}
}
Inilah masalah saya: di mana saya harus meletakkan EmailService ?
Option1 : Suntikkan Layanan Email ke 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);
}
}
Sudah ada beberapa diskusi tentang apakah kita harus menyuntikkan layanan ke entitas, dan kebanyakan orang setuju itu bukan praktik yang baik. Lihat artikel ini .
Option2: If ... Else 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
}
}
}
}
}
Saya telah diberitahu Jika ... Lain dan pemain seperti di atas bukan OO, dan akan membawa lebih banyak masalah.
Opsi3: Ubah tanda tangan Jalankan dan buat ServiceBundle .
public class ServiceBundle{
public EmailService EmailService{get;set}
public CleanDiskService CleanDiskService{get;set;}
// and other services for other recurring tasks
}
Suntikkan kelas ini ke dalam 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);
}
}
}
}
The Run metode SendEmailTask akan
public void Run(ServiceBundle serviceBundle){
serviceBundle.EmailService.send(this.email);
}
Saya tidak melihat masalah besar dengan pendekatan ini.
Opsi4 : Pola pengunjung.
Ide dasarnya adalah membuat pengunjung yang akan merangkum layanan seperti 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){
//
}
}
Dan kita juga perlu mengubah tanda tangan dari metode Run . The Run metode SendEmailTask adalah
public void Run(RecurringTaskVisitor visitor){
visitor.visit(this);
}
Ini adalah implementasi khas Pola Pengunjung, dan pengunjung akan disuntikkan ke RecurringTaskScheduler .
Singkatnya: Di antara empat pendekatan ini, mana yang terbaik untuk skenario saya? Dan apakah ada perbedaan besar antara Option3 dan Option4 untuk masalah ini?
Atau apakah Anda memiliki ide yang lebih baik tentang masalah ini? Terima kasih!
Pembaruan 5/22/2015 : Saya pikir jawaban Andy merangkum niat saya dengan sangat baik; jika Anda masih bingung tentang masalah itu sendiri, saya sarankan membaca postingnya terlebih dahulu.
Saya baru tahu bahwa masalah saya sangat mirip dengan masalah Pengiriman Pesan , yang mengarah ke Option5.
Option5 : Konversikan masalah saya ke Pengiriman Pesan .
Ada pemetaan satu-ke-satu antara masalah saya dan masalah Pengiriman Pesan :
Message Dispatcher : Terima IMessage dan kirim sub-kelas IMessage ke penangannya. → RecurringTaskScheduler
IMessage : Antarmuka atau kelas abstrak. → RecurringTask
MessageA : Meluas dari IMessage , memiliki beberapa informasi tambahan. → SendEmailTask
MessageB : Subclass lain dari IMessage . → CleanDiskTask
MessageAHandler : Saat menerima MessageA , tangani → SendEmailTaskHandler, yang berisi EmailService, dan akan mengirimkan email ketika menerima MessageEandTask
MessageBHandler : Sama seperti MessageAHandler , tetapi menangani MessageB sebagai gantinya. → CleanDiskTaskHandler
Bagian tersulit adalah cara mengirim berbagai jenis IMessage ke penangan yang berbeda. Berikut ini tautan yang bermanfaat .
Saya sangat menyukai pendekatan ini, tidak mencemari entitas saya dengan layanan, dan tidak memiliki kelas Tuhan .
SendEmailTask
sepertinya lebih seperti layanan daripada entitas bagi saya. Saya akan pergi untuk opsi 1 tanpa dan ragu-ragu.
accept
pengunjung. Motivasi untuk Pengunjung adalah bahwa Anda memiliki banyak jenis kelas dalam beberapa agregat yang perlu dikunjungi, dan tidak nyaman untuk memodifikasi kode mereka untuk setiap fungsionalitas baru (operasi). Saya masih tidak melihat objek agregat itu, dan berpikir bahwa Pengunjung tidak tepat. Jika demikian, Anda harus mengedit pertanyaan Anda (yang mengacu pada pengunjung).