क्या विज़िटर पैटर्न इस परिदृश्य में मान्य है?


9

मेरे कार्य का लक्ष्य एक छोटी प्रणाली को डिजाइन करना है जो अनुसूचित आवर्ती कार्यों को चला सकती है। एक आवर्ती कार्य कुछ ऐसा है "प्रशासक को हर घंटे सुबह 8:00 बजे से शाम 5:00 बजे, सोमवार से शुक्रवार तक ईमेल भेजें"।

मेरा एक बेस क्लास है, जिसे रिकरिंगटैस्क कहा जाता है ।

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
    }
}

और मेरे पास एक ईमेल सेवा है जो मुझे ईमेल भेजने में मदद कर सकती है।

अंतिम वर्ग 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();
            }
        }
    }
}

यहाँ मेरी समस्या है: मुझे ईमेल सेवा कहां डालनी चाहिए ?

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);
    }
}

इस बात पर पहले से ही कुछ चर्चाएँ हैं कि क्या हमें एक सेवा को एक इकाई में इंजेक्ट करना चाहिए, और अधिकांश लोग सहमत हैं कि यह एक अच्छा अभ्यास नहीं है। इस लेख को देखें ।

Option2: यदि ... 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 नहीं है, और अधिक समस्याएं लाएगा।

Option3: रन के हस्ताक्षर को बदलें और 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);
}

मैं इस दृष्टिकोण के साथ कोई बड़ी समस्या नहीं देखता हूं।

विकल्प 4 : आगंतुक पैटर्न।
मूल विचार एक आगंतुक बनाता है जो सेवाबंडल की तरह सेवाओं को एन्क्रिप्ट करेगा

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 की रन विधि है

public void Run(RecurringTaskVisitor visitor){
    visitor.visit(this);
}

यह विज़िटर पैटर्न का एक विशिष्ट कार्यान्वयन है, और विज़िटर को RecurringTaskScheduler में इंजेक्ट किया जाएगा ।

संक्षेप में: इन चार दृष्टिकोणों में, मेरे परिदृश्य के लिए कौन सा सबसे अच्छा है? और क्या इस समस्या के लिए Option3 और Option4 के बीच कोई बड़ा अंतर है?

या आप इस समस्या पर बेहतर विचार रखते हैं? धन्यवाद!

अपडेट 5/22/2015 : मुझे लगता है कि एंडी का जवाब मेरे इरादे को अच्छी तरह से बताता है; यदि आप अभी भी समस्या के बारे में उलझन में हैं, तो मैं पहले उनकी पोस्ट पढ़ने का सुझाव देता हूं।

मुझे अभी पता चला है कि मेरी समस्या मैसेज डिस्पैच समस्या से काफी मिलती-जुलती है, जिसके कारण Option5 की ओर रुख किया जाता है।

Option5 : मेरी समस्या को संदेश डिस्पैच में बदलें ।
मेरी समस्या और संदेश डिस्पैच समस्या के बीच एक-से-एक मानचित्रण है:

संदेश डिस्पैचर : IMessage प्राप्त करें और अपने संबंधित हैंडलर को IMessage की उप कक्षाएं भेजें । → पुनरावर्तीटैस्क्यूलेटर

IMessage : एक इंटरफ़ेस या एक सार वर्ग। → आवर्ती

MessageA : कुछ अतिरिक्त जानकारी होने, IMessage से फैली हुई है । → SendEmailTask

MessageB : IMessage का एक और उपवर्ग । → क्लीनडिस्कटस्क

MessageAHandler : प्राप्त कर लेते हैं MessageA , यह → SendEmailTaskHandler, जो EmailService शामिल संभाल, और एक ई-मेल भेजते हैं जब यह SendEmailTask प्राप्त करता है

MessageBHandler : MessageAHandler के रूप में ही , लेकिन इसके बजाय MessageB हैंडल करें । → CleanDiskTaskHandler

सबसे कठिन हिस्सा यह है कि विभिन्न हैंडलर्स को विभिन्न प्रकार के IMessage कैसे भेजें । यहाँ एक उपयोगी लिंक है

मुझे वास्तव में यह दृष्टिकोण पसंद है, यह मेरी इकाई को सेवा के साथ प्रदूषित नहीं करता है, और इसमें कोई ईश्वर वर्ग नहीं है ।


आपने किसी भाषा या मंच को टैग नहीं किया है, लेकिन मैं क्रोन देखने की सलाह देता हूं । आपके प्लेटफ़ॉर्म में एक लाइब्रेरी हो सकती है जो समान रूप से काम करती है (उदाहरण के लिए jcron जो किसी प्रकार का विक्षेप लगता है)। निर्धारण कार्य और कार्य काफी हद तक एक समस्या है: क्या आपने अपना रोल करने से पहले अन्य विकल्पों पर ध्यान दिया है? क्या उनके उपयोग न करने के कारण थे?

@Snowman हम बाद में एक परिपक्व पुस्तकालय में जा सकते हैं। यह सब मेरे प्रबंधक पर निर्भर करता है। इस प्रश्न को पोस्ट करने का कारण यह है कि मैं इस समस्या को हल करने का एक तरीका खोजना चाहता हूं। मैंने इस तरह की समस्या को एक से अधिक बार देखा है, और एक सुंदर समाधान नहीं खोज सका। इसलिए मैं सोच रहा हूं कि क्या मैंने कुछ गलत किया है।
शेर 10

उचित रूप से, मैं हमेशा कोड पुन: उपयोग की सिफारिश करने की कोशिश करता हूं यदि संभव हो तो।

1
SendEmailTaskमेरे लिए एक इकाई से अधिक एक सेवा की तरह लगता है। मैं विकल्प 1 के लिए बिना किसी हिचकिचाहट के जाऊंगा।
बार्ट वैन इनगेन शेनौ

3
विज़िटर के लिए जो (मेरे लिए) याद आ रहा है वह है क्लास स्ट्रक्चर जो acceptआगंतुकों को। विज़िटर के लिए प्रेरणा यह है कि आपके पास कुछ एग्रीगेट में कई प्रकार के प्रकार हैं, जिन्हें विज़िट करने की आवश्यकता है, और प्रत्येक नई कार्यक्षमता (ऑपरेशन) के लिए उनके कोड को संशोधित करना सुविधाजनक नहीं है। मैं अब भी नहीं देखता कि वे समग्र वस्तुएं क्या हैं, और सोचते हैं कि आगंतुक उचित नहीं है। यदि यह मामला है, तो आपको अपने प्रश्न (जो आगंतुक को संदर्भित करता है) को संपादित करना चाहिए।
फ्यूहरमैनेटर

जवाबों:


4

मैं कहूंगा कि विकल्प 1 लेने का सबसे अच्छा मार्ग है। कारण है कि आप इसे खारिज नहीं करना चाहिए वह यह है कि SendEmailTaskहै नहीं एक इकाई। एक इकाई डेटा और राज्य को धारण करने से संबंधित एक वस्तु है। आपकी कक्षा में बहुत कम है। वास्तव में, यह एक इकाई नहीं है, लेकिन यह एक इकाई रखती है : जिस Emailवस्तु को आप स्टोर कर रहे हैं। इसका मतलब है कि Emailएक सेवा नहीं लेनी चाहिए, या एक #Sendविधि होनी चाहिए । इसके बजाय, आपके पास ऐसी सेवाएँ होनी चाहिए जो आपके लिए, जैसे कि संस्थाएँ लें EmailService। तो आप पहले से ही संस्थाओं के बाहर सेवाओं को रखने के विचार का पालन कर रहे हैं।

चूंकि SendEmailTaskएक इकाई नहीं है, इसलिए ईमेल और सेवा को इसमें इंजेक्ट करना पूरी तरह से ठीक है, और इसे कंस्ट्रक्टर के माध्यम से किया जाना चाहिए। कंस्ट्रक्टर इंजेक्शन करके, हम यह सुनिश्चित कर सकते हैं कि SendEmailTaskयह काम करने के लिए हमेशा तैयार है।

अब आइए देखें कि अन्य विकल्प (विशेष रूप से एसओएलआईडी के संबंध में ) क्यों नहीं करना चाहिए ।

विकल्प 2

आपको सही तरीके से बताया गया है कि इस तरह की ब्रांचिंग से सड़क पर अधिक सिरदर्द होगा। आइए नजर डालते हैं क्यों। सबसे पहले, ifक्लस्टर में बढ़ते हैं और बढ़ते हैं। आज, ईमेल भेजने का काम है, कल, हर अलग-अलग प्रकार के वर्ग को एक अलग सेवा या अन्य व्यवहार की आवश्यकता होती है। उस ifकथन का प्रबंधन एक बुरा सपना बन जाता है। चूंकि हम टाइप कर रहे हैं (और इस मामले में स्पष्ट प्रकार ), हम अपनी भाषा में निर्मित टाइप सिस्टम को बदल रहे हैं।

विकल्प 2 एकल जिम्मेदारी (एसआरपी) नहीं है क्योंकि पहले पुन: प्रयोज्य को RecurringTaskSchedulerअब इन सभी विभिन्न प्रकार के कार्यों के बारे में और सभी विभिन्न प्रकार की सेवाओं और व्यवहारों के बारे में जानना होगा। उस वर्ग का पुन: उपयोग करना बहुत कठिन है। यह ओपन / क्लोज्ड (OCP) भी नहीं है। क्योंकि इस तरह के कार्य या उस एक (या इस तरह की सेवा या उस एक) के बारे में जानना आवश्यक है, कार्यों या सेवाओं के लिए असमान परिवर्तन यहां परिवर्तन को मजबूर कर सकते हैं। कोई नया कार्य जोड़ें? कोई नई सेवा जोड़ें? ईमेल हैंडल करने का तरीका बदलें? बदलो RecurringTaskScheduler। क्योंकि कार्य का प्रकार मायने रखता है, यह लिस्कोव सबस्टीट्यूशन (एलएसपी) का पालन नहीं करता है। यह सिर्फ एक कार्य नहीं हो सकता है और किया जा सकता है। यह प्रकार के लिए पूछना है और प्रकार के आधार पर ऐसा करते हैं या करते हैं। कार्यों में अंतरों को घेरने के बजाय, हम उन सभी को अंदर खींच रहे हैं RecurringTaskScheduler

विकल्प 3

विकल्प 3 में कुछ बड़ी समस्याएं हैं। आपके द्वारा लिंक किए गए लेख में भी , लेखक ऐसा करने को हतोत्साहित करता है:

  • आप अभी भी स्थैतिक सेवा लोकेटर का उपयोग कर सकते हैं ...
  • मैं सेवा लोकेटर से बचता हूं जब मैं कर सकता हूं, खासकर जब सेवा लोकेटर को स्थिर होना चाहिए ...

आप अपनी कक्षा के साथ एक सेवा लोकेटर बना रहे हैं ServiceBundle। इस स्थिति में, यह स्थिर प्रतीत नहीं होता है, लेकिन इसमें अभी भी कई समस्याएं हैं जो एक सेवा लोकेटर में निहित हैं। आपकी निर्भरताएँ अब इसके नीचे छिपी हुई हैं ServiceBundle। अगर मैं आपको अपने नए कार्य के निम्नलिखित एपीआई देता हूं:

class MyCoolNewTask implements RecurringTask
{
    public bool isOccuring(DateTime dateTime) {
        return true; // It's always happenin' here!
    }

    public void Run(ServiceBundle bundle) {
        // yeah, some awesome stuff here
    }
}

मैं किन सेवाओं का उपयोग कर रहा हूं? परीक्षण में किन सेवाओं का मज़ाक उड़ाया जाना चाहिए? सिस्टम में हर सेवा का उपयोग करने से मुझे क्या रोकना है, सिर्फ इसलिए?

यदि मैं कुछ कार्यों को चलाने के लिए आपकी कार्य प्रणाली का उपयोग करना चाहता हूं, तो मैं अब आपके सिस्टम की प्रत्येक सेवा पर निर्भर हूं, भले ही मैं केवल कुछ का उपयोग करता हूं या यहां तक ​​कि किसी पर भी नहीं।

ServiceBundleक्योंकि यह के बारे में पता करने की जरूरत है वास्तव में नहीं SRP है हर आपके सिस्टम में सेवा। यह भी OCP नहीं है। नई सेवाओं को जोड़ने का अर्थ है कि परिवर्तन ServiceBundle, और परिवर्तनों का ServiceBundleमतलब हो सकता है कि कहीं और कार्यों में परिवर्तन हो। ServiceBundleअपने इंटरफ़ेस (ISP) को अलग नहीं करता है। इसमें इन सभी सेवाओं का एक विस्तृत इंटरफ़ेस है, और क्योंकि यह उन सेवाओं के लिए सिर्फ एक प्रदाता है, हम इसके इंटरफ़ेस को उन सभी सेवाओं के इंटरफेस को शामिल करने पर विचार कर सकते हैं जो इसे प्रदान करता है। कार्य अब निर्भरता के उलट (डीआईपी) का पालन नहीं करते हैं, क्योंकि उनकी निर्भरता के पीछे की ओर बाधा होती है ServiceBundle। यह भी प्रिंसिपल ऑफ लीस्ट नॉलेज (उर्फ द लॉ ऑफ डेमेटर) का पालन नहीं करता है क्योंकि चीजों को कई चीजों के बारे में पता होता है जो उन्हें करना पड़ता है।

विकल्प 4

पहले, आपके पास बहुत सी छोटी वस्तुएं थीं जो स्वतंत्र रूप से संचालित करने में सक्षम थीं। विकल्प 4 इन सभी वस्तुओं को ले जाता है और उन्हें एक ही Visitorवस्तु में एक साथ नष्ट कर देता है । यह ऑब्जेक्ट आपके सभी कार्यों पर एक ईश्वर ऑब्जेक्ट के रूप में कार्य करता है। यह आपकी RecurringTaskवस्तुओं को एनीमिक छाया में कम कर देता है जो बस एक आगंतुक को बुलाता है। व्यवहार के सभी करने के लिए चलता है Visitor। व्यवहार बदलने की आवश्यकता है? नया कार्य जोड़ने की आवश्यकता है? बदलो Visitor

अधिक चुनौतीपूर्ण हिस्सा यह है, क्योंकि सभी अलग-अलग व्यवहारों के सभी एक ही वर्ग में हैं, कुछ अन्य व्यवहार के साथ कुछ बहुरूपिए ड्रग्स को बदलते हैं। उदाहरण के लिए, हम ईमेल भेजने के दो अलग-अलग तरीके चाहते हैं (उन्हें अलग-अलग सर्वर का उपयोग करना चाहिए?)। हम इसे कैसे करेंगे? हम एक IVisitorइंटरफ़ेस बना सकते हैं और उस पर अमल कर सकते हैं , जो संभवतः #Visit(ClearDiskTask)हमारे मूल आगंतुक की तरह कोड को डुप्लिकेट कर रहा है । फिर यदि हम एक डिस्क को खाली करने के लिए एक नया तरीका लेकर आए हैं, तो हमें फिर से लागू करना और डुप्लिकेट करना होगा। फिर हम दोनों तरह के बदलाव चाहते हैं। लागू करें और फिर से डुप्लिकेट करें। ये दो अलग-अलग, असमान व्यवहार अटूट रूप से जुड़े हुए हैं।

शायद इसके बजाय हम सिर्फ उपवर्ग कर सकते हैं Visitor? नए ईमेल व्यवहार के साथ उपवर्ग, नए डिस्क व्यवहार के साथ उपवर्ग। अब तक कोई नकल नहीं! दोनों के साथ उपवर्ग? अब एक या दूसरे को डुप्लिकेट करने की आवश्यकता है (या यदि आपकी प्राथमिकता है तो दोनों)।

विकल्प 1 से तुलना करें: हमें एक नया ईमेल व्यवहार चाहिए। हम एक नया बना सकते हैं RecurringTaskजो नया व्यवहार करता है, इसकी निर्भरता में इंजेक्ट करता है, और इसे कार्यों के संग्रह में जोड़ता है RecurringTaskScheduler। हमें क्लीयरिंग डिस्क के बारे में बात करने की भी आवश्यकता नहीं है, क्योंकि यह जिम्मेदारी पूरी तरह से कहीं और है। हमारे पास अभी भी हमारे निपटान में OO टूल्स की पूरी सरणी है। हम उदाहरण के लिए, लॉगिंग के साथ उस कार्य को सजा सकते हैं।

विकल्प 1 आपको कम से कम दर्द देगा, और इस स्थिति को संभालने का सबसे सही तरीका है।


Otion2,3,4 पर आपका विश्लेषण शानदार है! यह वास्तव में मुझे बहुत मदद करता है। लेकिन Option1 के लिए, मेरा तर्क है कि * SendEmailTask ​​* एक इकाई है। इसमें आईडी है, इसका आवर्ती पैटर्न है, और अन्य उपयोगी जानकारी जो डीबी में संग्रहीत की जानी चाहिए। मुझे लगता है कि एंडी ने मेरे इरादे को अच्छी तरह बताया। शायद नाम * EMailTaskDefinitions * अधिक उपयुक्त है। मैं अपनी सेवा कोड के साथ अपनी इकाई को प्रदूषित नहीं करना चाहता। यदि मैं किसी सेवा को इकाई में इंजेक्ट करता हूं तो कुछ समस्या का उल्लेख है। मैं अपने प्रश्न को भी अपडेट करता हूं और Option5 को शामिल करता हूं, जो मुझे लगता है कि अब तक का सबसे अच्छा समाधान है।
शेर 10

@ Sher10ck यदि आप अपने SendEmailTaskडेटाबेस से बाहर के लिए कॉन्फ़िगरेशन खींच रहे हैं , तो उस कॉन्फ़िगरेशन को एक अलग कॉन्फ़िगरेशन वर्ग होना चाहिए जिसे आपके में भी इंजेक्ट किया जाना चाहिए SendEmailTask। यदि आप अपने से डेटा उत्पन्न कर रहे हैं SendEmailTask, तो आपको राज्य को संग्रहीत करने और अपने डेटाबेस में डालने के लिए एक स्मृति चिन्ह बनाना चाहिए।
कोबरा

मैं db से विन्यास को खींचने के लिए की जरूरत है, तो यू दोनों इंजेक्शन लगाने के सुझाव दे रहे हैं EMailTaskDefinitionsऔर EmailServiceमें SendEmailTask? फिर में RecurringTaskScheduler, मुझे कुछ ऐसा इंजेक्ट करने की आवश्यकता है SendEmailTaskRepositoryजिसकी जिम्मेदारी परिभाषा और सेवा लोड कर रही है और उन्हें इंजेक्ट करें SendEmailTask। लेकिन मैं अब RecurringTaskSchedulerहर काम की रिपॉजिटरी को जानने की जरूरत पर बहस करूंगा CleanDiskTaskRepository। और मुझे RecurringTaskSchedulerहर बार मुझे एक नया कार्य (रिपॉजिटरी को शेड्यूलर में जोड़ने के लिए) बदलने की आवश्यकता है ।
शेर १०

@ Sher10ck RecurringTaskSchedulerकेवल एक सामान्यीकृत कार्य भंडार और a की अवधारणा के बारे में पता होना चाहिए RecurringTask। ऐसा करने से, यह अमूर्तता पर निर्भर हो सकता है। कार्य रिपॉजिटरी को कंस्ट्रक्टर के इंजेक्ट किया जा सकता है RecurringTaskScheduler। तब अलग-अलग रिपॉजिटरी की जरूरत होती है, केवल यह जाना जाता है कि कहां RecurringTaskSchedulerपर तत्काल (या किसी फैक्ट्री में छिपा हो सकता है और वहां से कॉल किया जा सकता है)। क्योंकि यह केवल अमूर्तता पर निर्भर करता है, RecurringTaskSchedulerप्रत्येक नए कार्य के साथ बदलने की आवश्यकता नहीं है। यह निर्भरता उलटा का सार है।
कोबरा

3

क्या आपने मौजूदा पुस्तकालयों जैसे स्प्रिंग क्वार्ट्ज या स्प्रिंग बैच पर एक नज़र डाली है (मुझे यकीन नहीं है कि आपकी आवश्यकताओं को सबसे अधिक क्या फिट बैठता है)?

आपके प्रश्न के लिए:

मुझे लगता है कि समस्या यह है कि आप कुछ मेटाडेटा को एक बहुरूपी तरीके से कार्य के लिए जारी रखना चाहते हैं, इसलिए एक ई-मेल कार्य में ई-मेल पते असाइन किए गए हैं, एक लॉग-टास्क एक लॉग-लेवल, और इसी तरह। आप स्मृति या अपने डेटाबेस में उन लोगों की एक सूची संग्रहीत कर सकते हैं लेकिन उन चिंताओं को अलग करने के लिए जिन्हें आप सेवा-कोड के साथ प्रदूषित नहीं करना चाहते हैं।

मेरा प्रस्तावित समाधान:

मैं रनिंग और टास्क के डेटा-पार्ट को अलग कर दूंगा, जैसे कि TaskDefinitionए और ए TaskRunner। टास्कडिफ़िनिशन में एक टास्करनर या एक कारखाने का संदर्भ होता है जो एक बनाता है (जैसे अगर कुछ सेटअप smtp-host की तरह आवश्यक है)। कारखाना एक विशिष्ट है - यह केवल एस को संभाल सकता है EMailTaskDefinitionऔर एस के केवल उदाहरण देता है EMailTaskRunner। इस तरह यह अधिक OO है और सुरक्षित रूप से बदल जाता है - यदि आप एक नए कार्य-प्रकार का परिचय देते हैं, तो आपको एक नया विशिष्ट कारखाना (या पुन: उपयोग करना) शुरू करना होगा, यदि आप ऐसा नहीं करते कि आप संकलन नहीं कर सकते।

इस तरह आप एक निर्भरता के साथ समाप्त हो जाएंगे: इकाई परत -> सेवा परत और फिर से वापस, क्योंकि धावक को इकाई में संग्रहीत जानकारी की आवश्यकता होती है और संभवत: डीबी में अपने राज्य के लिए एक अपडेट करना चाहता है।

आप एक जेनेरिक फैक्ट्री का उपयोग करके सर्कल को तोड़ सकते हैं, जो एक टास्कफाइनेशन लेता है और एक विशिष्ट टास्करनर लौटाता है, लेकिन इसके लिए बहुत सारे आईएफएस की आवश्यकता होगी। आप एक ऐसे धावक को खोजने के लिए प्रतिबिंब का उपयोग कर सकते हैं जिसे समान रूप से आपकी परिभाषा के रूप में नामित किया गया है, लेकिन सतर्क रहें यह दृष्टिकोण कुछ प्रदर्शन को खर्च कर सकता है, और रनटाइम त्रुटियों को जन्म दे सकता है।

PS मैं यहाँ जावा मान रहा हूँ। मुझे लगता है कि यह .net जैसा है। यहां मुख्य समस्या दोहरी बाध्यकारी है।

आगंतुक पैटर्न के लिए

मुझे लगता है कि शुद्ध दोहरे बाध्यकारी उद्देश्यों की तुलना में, रनटाइम पर विभिन्न प्रकार के डेटा ऑब्जेक्ट के लिए एल्गोरिदम का आदान-प्रदान करने के लिए इसका उपयोग करने का इरादा था। उदाहरण के लिए यदि आपके पास विभिन्न प्रकार के बीमा और उन्हें गणना करने के विभिन्न प्रकार हैं, जैसे कि क्योंकि विभिन्न देशों को इसकी आवश्यकता होती है। फिर आप एक विशिष्ट गणना पद्धति चुनते हैं और इसे कई बीमाओं पर लागू करते हैं।

आपके मामले में आप एक विशिष्ट कार्य-रणनीति (जैसे ईमेल) चुनेंगे और इसे अपने सभी कार्यों पर लागू करेंगे, जो कि गलत है क्योंकि ये सभी ईमेल कार्य नहीं हैं।

PS मैंने इसका परीक्षण नहीं किया है, लेकिन मुझे लगता है कि आपका विकल्प 4 या तो काम नहीं करेगा, क्योंकि यह फिर से डबल-बाइंडिंग है।


तुम सच में अच्छी तरह से मेरे इरादे संक्षेप में, thx! मैं सर्कल को तोड़ना चाहूंगा। क्योंकि TaskDefiniton को TaskRunner का संदर्भ देता है या फ़ैक्टरी में Option1 जैसी ही समस्या है। मैं कारखाने या टास्करनर को सेवा के रूप में मानता हूं । यदि टास्कडेफिनिशन को उनके संदर्भ की आवश्यकता होती है, तो आप या तो सेवा को टास्कडिफ़िनिशन में इंजेक्ट करते हैं , या कुछ स्थिर विधि का उपयोग करते हैं, जो कि मैं बचने की कोशिश कर रहा हूं।
शेर 10

1

मैं उस लेख से पूरी तरह असहमत हूं। सेवाएँ (संक्षिप्त रूप से उनके "एपीआई") बिजनेस डोमेन की महत्वपूर्ण पार्टी हैं और जैसे डोमेन मॉडल के भीतर मौजूद होंगी। और व्यापार डोमेन में संस्थाओं के साथ कोई समस्या नहीं है, उसी व्यवसाय डोमेन में कुछ और संदर्भित करना।

जब X, Y को मेल भेजता है।

एक व्यावसायिक नियम है। और ऐसा करने के लिए, मेल भेजने वाली सेवा की आवश्यकता है। और वह इकाई जो When Xइस सेवा के बारे में जानती है।

लेकिन कार्यान्वयन के साथ कुछ समस्याएं हैं। यह इकाई के उपयोगकर्ता के लिए पारदर्शी होना चाहिए, कि इकाई किसी सेवा का उपयोग कर रही है। इसलिए कंस्ट्रक्टर में सेवा को जोड़ना अच्छी बात नहीं है। यह भी एक मुद्दा है, जब आप इकाई को डेटाबेस से अलग कर रहे हैं, क्योंकि आपको इकाई के डेटा और सेवाओं के उदाहरण दोनों को सेट करने की आवश्यकता है। सबसे अच्छा समाधान मुझे लगता है कि इकाई बनाए जाने के बाद संपत्ति इंजेक्शन का उपयोग करना है। हो सकता है कि किसी भी इकाई के प्रत्येक नए बनाए गए उदाहरण को "इनिशियलाइज़" विधि से गुजरने के लिए मजबूर करना, जो कि सभी यूनिटों की ज़रूरतों को पूरा करता है।


आप किस लेख का उल्लेख कर रहे हैं जिससे आप असहमत हैं? हालांकि, डोमेन मॉडल पर देखने का दिलचस्प बिंदु। संभवतः आप इसे इस तरह देख सकते हैं, हालांकि, लोग आमतौर पर संस्थाओं में सेवाओं को मिलाने से बचते हैं, क्योंकि यह बहुत जल्द एक तंग युग्मन पैदा करेगा।
एंडी

@Andy ने अपने सवाल में एक शेर 10ck का संदर्भ दिया। और मैं नहीं देखता कि यह कैसे एक तंग युग्मन पैदा करेगा। कोई भी बुरी तरह से लिखा कोड तंग युग्मन का परिचय दे सकता है।
व्यंग्यात्मक

1

यह एक बड़ा सवाल है और एक दिलचस्प समस्या है। मैं प्रस्ताव करता हूं कि आप चैन ऑफ रिस्पॉन्सिबिलिटी और डबल डिस्पैच पैटर्न (पैटर्न उदाहरण यहां ) के संयोजन का उपयोग करें

पहले कार्य पदानुक्रम को परिभाषित करने देता है। ध्यान दें कि runडबल डिस्पैच को लागू करने के लिए अब कई तरीके हैं ।

public abstract class RecurringTask {

    public abstract boolean isOccuring(Date date);

    public boolean run(EmailService emailService) {
        return false;
    }

    public boolean run(ExecuteService executeService) {
        return false;
    }
}

public class SendEmailTask extends RecurringTask {

    private String email;

    public SendEmailTask(String email) {
        this.email = email;
    }

    @Override
    public boolean isOccuring(Date date) {
        return true;
    }

    @Override
    public boolean run(EmailService emailService) {
        emailService.runTask(this);
        return true;
    }

    public String getEmail() {
        return email;
    }
}

public class ExecuteTask extends RecurringTask {

    private String program;

    public ExecuteTask(String program) {
        this.program = program;
    }

    @Override
    public boolean isOccuring(Date date) {
        return true;
    }

    public String getName() {
        return program;
    }

    @Override
    public boolean run(ExecuteService executeService) {
        executeService.runTask(this);
        return true;
    }
}

अगला Serviceपदानुक्रम को परिभाषित करता है। हम Serviceजिम्मेदारी की श्रृंखला बनाने के लिए एस का उपयोग करेंगे ।

public abstract class Service {

    private Service next;

    public Service(Service next) {
        this.next = next;
    }

    public void handleRecurringTask(RecurringTask req) {
        if (next != null) {
            next.handleRecurringTask(req);
        }
    }
}

public class ExecuteService extends Service {

    public ExecuteService(Service next) {
        super(next);
    }

    void runTask(ExecuteTask task) {
        System.out.println(String.format("%s running %s with content '%s'", this.getClass().getSimpleName(),
                task.getClass().getSimpleName(), task.getName()));
    }

    public void handleRecurringTask(RecurringTask req) {
        if (!req.run(this)) {
            super.handleRecurringTask(req);
        }
    }
}

public class EmailService extends Service {

    public EmailService(Service next) {
        super(next);
    }

    public void runTask(SendEmailTask task) {
        System.out.println(String.format("%s running %s with content '%s'", this.getClass().getSimpleName(),
                task.getClass().getSimpleName(), task.getEmail()));
    }

    public void handleRecurringTask(RecurringTask req) {
        if (!req.run(this)) {
            super.handleRecurringTask(req);
        }
    }
}

अंतिम टुकड़ा वह है RecurringTaskSchedulerजो लोडिंग और रनिंग प्रक्रिया को ऑर्केस्ट्रा करता है।

public class RecurringTaskScheduler{

    private List<RecurringTask> tasks = new ArrayList<>();

    private Service chain;

    public RecurringTaskScheduler() {
        chain = new EmailService(new ExecuteService(null));
    }

    public void loadTasks() {
        tasks.add(new SendEmailTask("here comes the first email"));
        tasks.add(new SendEmailTask("here is the second email"));
        tasks.add(new ExecuteTask("/root/python"));
        tasks.add(new ExecuteTask("/bin/cat"));
        tasks.add(new SendEmailTask("here is the third email"));
        tasks.add(new ExecuteTask("/bin/grep"));
    }

    public void runTasks(){
        for (RecurringTask task : tasks) {
            if (task.isOccuring(new Date())) {
                chain.handleRecurringTask(task);
            }
        }
    }
}

अब, यहां सिस्टम का प्रदर्शन करने वाला उदाहरण एप्लिकेशन है।

public class App {

    public static void main(String[] args) {
        RecurringTaskScheduler scheduler = new RecurringTaskScheduler();
        scheduler.loadTasks();
        scheduler.runTasks();
    }
}

अनुप्रयोग आउटपुट चलाना:

सामग्री के साथ SendEmailTask ​​चलाने वाला EmailService यहां पहला ईमेल आता है '
EmailService चल रहा है SendEmailTask ​​सामग्री के साथ' यहाँ दूसरा ईमेल है '
ExecuteService चल रहा है सामग्री के साथ ExecuteTask' / root / python -
ExecuteService चल रहा है सामग्री '/ bin / cat' के साथ
SendSmail चल रहा है। सामग्री 'यहां तीसरा ईमेल है'
ExecuteService चल रहा है ExecuteTask सामग्री के साथ '/ बिन / grep'


मेरे पास बहुत सारे टास्क हो सकते हैं । जब भी मैं एक नया कार्य जोड़ता हूं , मुझे बदलने की आवश्यकता है RecurringTask और मुझे इसके सभी उप वर्गों को भी बदलने की आवश्यकता होती है, क्योंकि मुझे सार्वजनिक सार बूलियन रन (OtherService otherService) जैसे नए फ़ंक्शन को जोड़ने की आवश्यकता है । मुझे लगता है कि Option4, विज़िटर पैटर्न जो दोहरे प्रेषण को भी लागू करता है, वही समस्या है।
शेर १०

अच्छी बात। मैंने अपना उत्तर संपादित किया ताकि रन (सेवा) विधियां रिकरिंगटैस्क में परिभाषित हो जाएं और डिफ़ॉल्ट रूप से गलत हो जाएं। इस तरह, जब आपको एक और कार्य वर्ग जोड़ने की आवश्यकता होती है तो आपको भाई-बहन के कार्यों को छूने की आवश्यकता नहीं होती है।
इलवतार
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.