내 작업의 목표는 예약 된 되풀이 작업을 실행할 수있는 작은 시스템을 설계하는 것입니다. 되풀이되는 작업은 "월요일부터 금요일까지 오전 8 시부 터 오후 5 시까 지 관리자에게 이메일을 보내는 것"과 같습니다.
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 : 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 : 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
}
}
}
}
}
위와 같이 말하면 OO가 아니며 더 많은 문제가 발생할 것입니다.
옵션 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);
}
}
}
}
실행 방법 SendEmailTask는 것
public void Run(ServiceBundle serviceBundle){
serviceBundle.EmailService.send(this.email);
}
이 방법에는 큰 문제가 없습니다.
Option4 : 방문자 패턴.
기본 아이디어는 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 메소드 의 서명도 변경해야합니다 . SendEmailTask 의 Run 메소드 는
public void Run(RecurringTaskVisitor visitor){
visitor.visit(this);
}
방문자 패턴의 일반적인 구현이며 방문자는 RecurringTaskScheduler에 주입됩니다 .
요약하자면,이 네 가지 접근법 중 어느 것이 내 시나리오에 가장 적합합니까? 그리고이 문제에 대해 Option3과 Option4 사이에 큰 차이가 있습니까?
아니면이 문제에 대해 더 잘 알고 있습니까? 감사!
2015 년 5 월 22 일 업데이트 : Andy의 답변에 내 의도가 잘 요약되어 있다고 생각합니다. 여전히 문제 자체에 대해 혼란 스러우면 먼저 그의 게시물을 읽는 것이 좋습니다.
방금 내 문제가 Message Dispatch 문제 와 매우 유사하다는 것을 알았습니다. 이는 Option5로 이어집니다.
Option5 : 내 문제를 Message Dispatch로 변환하십시오 .
내 문제와 Message Dispatch 문제 사이에 일대일 매핑이 있습니다.
메시지 Dispatcher는 : 수신 iMessage를 의 파견 하위 클래스 iMessage를을 자신의 해당 핸들러. → RecurringTaskScheduler
IMessage : 인터페이스 또는 추상 클래스. → 되풀이 작업
MessageA : 추가 정보가있는 IMessage 에서 확장됩니다 . → SendEmailTask
MessageB : IMessage의 또 다른 서브 클래스 . → CleanDiskTask
MessageAHandler는 :받을 때 MessageA을 , EmailService을 포함 SendEmailTaskHandler, →를 취급하고 SendEmailTask를받을 때 이메일을 보내드립니다
MessageBHandler : MessageAHandler 와 동일 하지만 대신 MessageB 를 처리하십시오 . → CleanDiskTaskHandler
가장 어려운 부분은 다른 종류의 IMessage 를 다른 핸들러 로 발송하는 방법 입니다. 유용한 링크 는 다음과 같습니다 .
나는이 접근 방식이 정말 마음에 들며, 실체를 서비스로 오염시키지 않으며, 신 클래스도 없습니다 .
SendEmailTask
나에게 엔티티보다 서비스처럼 보입니다. 망설임없이 옵션 1을 선택합니다.
accept
입니다. 방문자의 동기는 일부 클래스에 방문이 필요한 많은 클래스 유형이 있으며 각 새 기능 (작업)에 대해 코드를 수정하는 것이 편리하지 않다는 것입니다. 나는 여전히 그 집계 객체가 무엇인지 알지 못하고 방문자가 적절하지 않다고 생각합니다. 이 경우 질문을 수정해야합니다 (방문자 참조).