Görevimin amacı, zamanlanan yinelenen görevleri çalıştırabilecek küçük bir sistem tasarlamaktır. Yinelenen bir görev, "Pazartesi-Cuma günleri 08: 00-17: 00 saatleri arasında yöneticiye her saat e-posta gönder" gibi bir şeydir.
RecurringTask adında bir temel sınıf var .
public abstract class RecurringTask{
// I've already figured out this part
public bool isOccuring(DateTime dateTime){
// implementation
}
// run the task
public abstract void Run(){
}
}
Ve RecurringTask'tan devralınan birkaç sınıfım var . Bunlardan birine SendEmailTask denir .
public class SendEmailTask : RecurringTask{
private Email email;
public SendEmailTask(Email email){
this.email = email;
}
public override void Run(){
// need to send out email
}
}
Ve bir e-posta göndermeme yardımcı olabilecek bir EmailService'im var .
Son sınıf RecurringTaskScheduler , görevleri önbellekten veya veritabanından yüklemek ve görevi çalıştırmaktan sorumludur.
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();
}
}
}
}
İşte benim sorunum: EmailService'i nereye koymalıyım ?
Seçenek1 : SendEmailTask içine EmailService enjekte
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);
}
}
Bir işletmeye bir hizmet enjekte etmemiz gerekip gerekmediği konusunda bazı tartışmalar var ve çoğu insan bunun iyi bir uygulama olmadığına katılıyor. Bkz bu yazıyı .
Seçenek2: Eğer ... YinelenenTaskScheduler içinde başka
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
}
}
}
}
}
Bana söylendi Eğer ... Başka ve yukarıdaki gibi döküm OO değildir ve daha fazla sorun getirecektir.
Seçenek3: Çalıştır imzasını değiştirin ve ServiceBundle oluşturun .
public class ServiceBundle{
public EmailService EmailService{get;set}
public CleanDiskService CleanDiskService{get;set;}
// and other services for other recurring tasks
}
Bu sınıfı RecurringTaskScheduler içine enjekte edin
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);
}
}
}
}
Çalışma yöntemi SendEmailTask olurdu
public void Run(ServiceBundle serviceBundle){
serviceBundle.EmailService.send(this.email);
}
Bu yaklaşımla ilgili büyük bir sorun görmüyorum.
Seçenek4 : Ziyaretçi kalıbı.
Temel fikir, ServiceBundle gibi hizmetleri kapsayacak bir ziyaretçi oluşturmaktır .
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){
//
}
}
Ayrıca Run yönteminin imzasını da değiştirmemiz gerekiyor . Çalışma yöntemi SendEmailTask olduğunu
public void Run(RecurringTaskVisitor visitor){
visitor.visit(this);
}
Ziyaretçi Paterninin tipik bir uygulamasıdır ve ziyaretçi RecurringTaskScheduler'a enjekte edilecektir .
Özetle: Bu dört yaklaşım arasında, senaryom için hangisi en iyisidir? Ve bu sorun için Option3 ve Option4 arasında büyük bir fark var mı?
Yoksa bu sorun hakkında daha iyi bir fikriniz mi var? Teşekkürler!
Güncelleme 5/22/2015 : Bence Andy'nin cevabı niyetimi gerçekten iyi özetliyor; hala sorunun kendisiyle ilgili kafanız karıştıysa, önce onun yazısını okumanızı öneririm.
Sorunumun Option5'e yol açan Message Dispatch sorununa çok benzediğini öğrendim.
Seçenek5 : Sorunumu Message Dispatch'a dönüştür . Sorunum
ile Message Dispatch sorunu arasında bire bir eşleme var :
İleti Dispatcher : Alma IMessage ve sevk alt sınıfları IMessage bunlara karşılık gelen işleyicilerine. → Yinelenen GörevScheduler
IMessage : Bir arayüz veya soyut bir sınıf. → Yinelenen Görev
MessageA : Bazı ek bilgilere sahip olan IMessage'dan genişler . → SendEmailTask
MessageB : IMessage'ın başka bir alt sınıfı . → CleanDiskTask
MessageAHandler : aldığınızda MessageA , EmailService içeren SendEmailTaskHandler, → idare ve SendEmailTask aldığında bir e-posta gönderir
MessageBHandler : MessageAHandler ile aynı , ancak bunun yerine MessageB'yi işleyin . → CleanDiskTaskHandler
En zor yanı, farklı işleyicilere farklı türde IMessage nasıl gönderileceğidir . İşte faydalı bir bağlantı .
Bu yaklaşımı gerçekten seviyorum, varlığımı hizmetle kirletmiyor ve hiçbir Tanrı sınıfı yok.
SendEmailTask
Bana göre bir varlıktan çok bir hizmet gibi görünüyor. Seçenek 1 olmadan ve tereddüt gitmek istiyorum.
accept
ziyaretçilerin sınıf yapısıdır . Ziyaretçinin motivasyonu, bazı toplamalarda ziyaret edilmesi gereken birçok sınıf türüne sahip olmanız ve her yeni işlevsellik (işlem) için kodlarını değiştirmenin uygun olmamasıdır. Hala bu toplu nesnelerin ne olduğunu görmüyorum ve Ziyaretçi'nin uygun olmadığını düşünüyorum. Durum buysa, sorunuzu düzenlemelisiniz (ziyaretçiyi ifade eder).