Mục tiêu của nhiệm vụ của tôi là thiết kế một hệ thống nhỏ có thể chạy các nhiệm vụ định kỳ theo lịch trình. Một nhiệm vụ định kỳ là một cái gì đó như "gửi email cho quản trị viên mỗi giờ từ 8:00 sáng đến 5:00 chiều, từ thứ Hai đến thứ Sáu".
Tôi có một lớp cơ sở gọi là 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(){
}
}
Và tôi có một vài lớp được kế thừa từ RecurringTask . Một trong số đó được gọi là SendEmailTask .
public class SendEmailTask : RecurringTask{
private Email email;
public SendEmailTask(Email email){
this.email = email;
}
public override void Run(){
// need to send out email
}
}
Và tôi có một EmailService có thể giúp tôi gửi email.
Lớp cuối cùng là RecurringTaskScheduler , nó chịu trách nhiệm tải các tác vụ từ bộ đệm hoặc cơ sở dữ liệu và chạy tác vụ.
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();
}
}
}
}
Đây là vấn đề của tôi: tôi nên đặt EmailService ở đâu?
Option1 : Tiêm EmailService vào 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);
}
}
Đã có một số cuộc thảo luận về việc chúng ta có nên tiêm dịch vụ vào một thực thể hay không và hầu hết mọi người đều đồng ý rằng đó không phải là một thực tiễn tốt. Xem bài viết này .
Tùy chọn 2: Nếu ... Khác trong 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
}
}
}
}
}
Tôi đã được thông báo Nếu ... Khác và diễn viên như trên không phải là OO, và sẽ mang lại nhiều vấn đề hơn.
Tùy chọn 3: Thay đổi chữ ký của Run và tạo ServiceBundle .
public class ServiceBundle{
public EmailService EmailService{get;set}
public CleanDiskService CleanDiskService{get;set;}
// and other services for other recurring tasks
}
Tiêm lớp này vào 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);
}
}
}
}
Các Run phương pháp SendEmailTask sẽ
public void Run(ServiceBundle serviceBundle){
serviceBundle.EmailService.send(this.email);
}
Tôi không thấy bất kỳ vấn đề lớn với phương pháp này.
Tùy chọn4 : Mẫu khách truy cập.
Ý tưởng cơ bản là tạo một khách truy cập sẽ đóng gói các dịch vụ giống như 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){
//
}
}
Và chúng ta cũng cần thay đổi chữ ký của phương thức Run . Các Run phương pháp SendEmailTask là
public void Run(RecurringTaskVisitor visitor){
visitor.visit(this);
}
Đây là một triển khai điển hình của Mẫu khách truy cập và khách truy cập sẽ được đưa vào RecurringTaskScheduler .
Tóm lại: Trong số bốn cách tiếp cận này, cách nào là tốt nhất cho kịch bản của tôi? Và có sự khác biệt lớn nào giữa Option3 và Option4 cho vấn đề này không?
Hay bạn có ý tưởng tốt hơn về vấn đề này? Cảm ơn!
Cập nhật 22/05/2015 : Tôi nghĩ câu trả lời của Andy tóm tắt ý định của tôi thực sự tốt; Nếu bạn vẫn còn bối rối về vấn đề này, tôi khuyên bạn nên đọc bài viết của mình trước.
Tôi mới phát hiện ra vấn đề của mình rất giống với sự cố Message Message , dẫn đến Option5.
Tùy chọn5 : Chuyển đổi vấn đề của tôi sang Công văn tin nhắn .
Có một ánh xạ một-một giữa vấn đề của tôi và vấn đề Gửi thư :
Bộ điều phối thư : Nhận IMessage và gửi các lớp phụ của IMessage đến các bộ xử lý tương ứng của chúng. → Định kỳTaskScheduler
IMessage : Một giao diện hoặc một lớp trừu tượng. → Định kỳ
MessageA : Mở rộng từ IMessage , có thêm một số thông tin. → SendEmailTask
MessageB : Một lớp con khác của IMessage . → CleanDiskTask
MessageAHandler : Khi nhận MessageA , hãy xử lý nó → SendEmailTaskHandler, có chứa EmailService và sẽ gửi email khi nhận được SendEmailTask
MessageBHandler : Tương tự MessageAHandler , nhưng xử lý MessageB thay thế. → CleanDiskTaskHandler
Phần khó nhất là làm thế nào để gửi loại IMessage khác nhau đến những người xử lý khác nhau. Đây là một liên kết hữu ích .
Tôi thực sự thích cách tiếp cận này, nó không gây ô nhiễm thực thể của tôi với dịch vụ và nó không có bất kỳ lớp Chúa nào .
SendEmailTask
có vẻ giống như một dịch vụ hơn là một thực thể đối với tôi. Tôi sẽ đi cho tùy chọn 1 mà không và do dự.
accept
khách truy cập. Động lực cho Khách truy cập là bạn có nhiều loại lớp trong một số tổng hợp cần truy cập và không thuận tiện để sửa đổi mã của họ cho mỗi chức năng (hoạt động) mới. Tôi vẫn không thấy những đối tượng tổng hợp đó là gì và nghĩ rằng Khách truy cập không phù hợp. Nếu đó là trường hợp, bạn nên chỉnh sửa câu hỏi của bạn (trong đó đề cập đến khách truy cập).