এই দৃশ্যে কি ভিজিটর প্যাটার্নটি বৈধ?


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(){

    }
}

এবং আমার বেশ কয়েকটি ক্লাস রয়েছে যা রিকারিং টাস্ক থেকে উত্তরাধিকার সূত্রে প্রাপ্ত । এর মধ্যে একটির নাম সেন্ডইমেইল টাস্ক

public class SendEmailTask : RecurringTask{
    private Email email;

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

    public override void Run(){
        // need to send out email
    }
}

এবং আমার একটি ইমেল সার্ভিস রয়েছে যা আমাকে ইমেল প্রেরণে সহায়তা করতে পারে।

শেষ শ্রেণিটি রিকারিং টাস্কসিডুলার , এটি ক্যাশে বা ডাটাবেস থেকে কাজগুলি লোড করার জন্য এবং কার্যটি চালনার জন্য দায়ী।

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

আমাদের কোনও সত্তাকে কোনও পরিষেবা ইনজেকশন করা উচিত কিনা তা নিয়ে ইতিমধ্যে কিছু আলোচনা রয়েছে এবং বেশিরভাগ মানুষ সম্মত হন যে এটি একটি ভাল অনুশীলন নয়। এই নিবন্ধটি দেখুন ।

বিকল্প 2: যদি ... পুনরাবৃত্তি টাস্কশেল্ডারে অন্যথায়

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

আমাকে বলা হয়েছে যদি ... উপরের মতো অন্যরকম castালাই ওও হয় না, এবং আরও সমস্যা আনবে।

বিকল্প 3: রানের স্বাক্ষর পরিবর্তন করুন এবং পরিষেবাবান্ডেল তৈরি করুন ।

public class ServiceBundle{
    public EmailService EmailService{get;set}
    public CleanDiskService CleanDiskService{get;set;}
    // and other services for other recurring tasks

}

এই ক্লাসটি পুনরাবৃত্তকারী টাস্কশেল্ডুলারে ইনজেক্ট করুন

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){
        //
    }
}

এবং আমাদের রান পদ্ধতির স্বাক্ষরও পরিবর্তন করতে হবে । চালান পদ্ধতি SendEmailTask হয়

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

এটা তোলে ভিজিটর প্যাটার্ন একটি আদর্শ বাস্তবায়ন, এবং পরিদর্শক মধ্যে ইনজেকশনের করা হবে RecurringTaskScheduler

সংক্ষেপে: এই চারটি পদ্ধতির মধ্যে কোনটি আমার দৃশ্যের জন্য সেরা? এবং এই সমস্যার জন্য অপশন 3 এবং অপশন 4 এর মধ্যে কোনও বড় পার্থক্য রয়েছে?

অথবা আপনার এই সমস্যা সম্পর্কে আরও ভাল ধারণা আছে? ধন্যবাদ!

5/22/2015 আপডেট করুন : আমি মনে করি অ্যান্ডির উত্তরটি আমার উদ্দেশ্যটিকে খুব ভালভাবে সংক্ষেপ করে; যদি আপনি এখনও সমস্যাটি সম্পর্কে নিজেই বিভ্রান্ত হন তবে আমি প্রথমে তার পোস্টটি পড়ার পরামর্শ দিই।

আমি সবেমাত্র জানতে পেরেছিলাম যে আমার সমস্যা বার্তা প্রেরণের সমস্যার সাথে খুব মিল , যা অপশন 5-এ নিয়ে যায়।

বিকল্প 5 : আমার সমস্যাটিকে বার্তা প্রেরণে রূপান্তর করুন ।
আমার সমস্যা এবং বার্তা প্রেরণের সমস্যাটির মধ্যে একটি থেকে একের ম্যাপিং রয়েছে :

বার্তা প্রেরণকারী : ইম্যাসেজ গ্রহণ করুন এবং তাদের সম্পর্কিত হ্যান্ডলারের কাছে চিত্রের সাব ক্লাস প্রেরণ করুন । Ur পুনরাবৃত্তি টাস্ক শিডুলার

ভাবনা : একটি ইন্টারফেস বা বিমূর্ত শ্রেণি। Ur রিকারিং টাস্ক

MessageA থেকে বর্ধিত করে iMessage , কিছু অতিরিক্ত তথ্য হচ্ছে। → সেন্ডইমেইল টাস্ক

MessageB : আরেকটি উপশ্রেণী iMessage । → ক্লিনডিস্কটাস্ক

ম্যাসেজএইচ্যান্ডলার : ম্যাসেজএ গ্রহণ করার পরে , এটি হ্যান্ডেল করুন → সেন্ডইমেলটাস্কহ্যান্ডলার, এতে ইমেল সার্ভিস রয়েছে, এবং যখন এটি ইমেল পাঠাবে তখন ইমেলটি প্রেরণ করবে

MessageBHandler : হিসাবে একই MessageAHandler কিন্তু হাতল MessageB পরিবর্তে। → ক্লিনডিস্কটাস্কহ্যান্ডলার

সবচেয়ে শক্তিশালী অংশটি হ'ল বিভিন্ন হ্যান্ডলারের কাছে বিভিন্ন ধরণের ইমেসেজ প্রেরণ করা যায় । এখানে একটি দরকারী লিঙ্ক

আমি সত্যিই এই পদ্ধতির পছন্দ করি, এটি পরিষেবা সহ আমার সত্তাকে কলুষিত করে না এবং এর কোনও God শ্বর শ্রেণি নেই।


আপনি কোনও ভাষা বা প্ল্যাটফর্ম ট্যাগ করেননি, তবে ক্রোনটি অনুসন্ধান করার পরামর্শ দিচ্ছি । আপনার প্ল্যাটফর্মে একইভাবে কাজ করে এমন একটি লাইব্রেরি থাকতে পারে (যেমন জ্যাক্রন যা ধরণের অবনতি বলে মনে হয়)। চাকরি ও কাজের সময় নির্ধারণ বেশিরভাগ ক্ষেত্রেই একটি সমাধান সমস্যা: আপনি নিজের ঘূর্ণায়মান হওয়ার আগে আপনি অন্য বিকল্পগুলির দিকে নজর রেখেছেন? সেগুলি ব্যবহার না করার জন্য কি কোনও কারণ ছিল?

@ স্নোমান আমরা পরে একটি পরিপক্ক লাইব্রেরিতে যেতে পারি। এটি আমার ম্যানেজারের উপর নির্ভর করে। আমি এই প্রশ্নটি পোস্ট করার কারণটি হ'ল আমি এই 'ধরণের' সমস্যা সমাধানের একটি উপায় খুঁজতে চাই। আমি এই ধরণের সমস্যা একাধিকবার দেখেছি এবং একটি দুর্দান্ত সমাধান খুঁজে পাইনি। তাই আমি ভাবছি যে আমি কিছু ভুল করেছি কিনা।
Sher10ck

যথেষ্ট পরিমাণে, আমি যদি সম্ভব হয় তবে সবসময় কোড পুনরায় ব্যবহারের পরামর্শ দেওয়ার চেষ্টা করি।

1
SendEmailTaskআমার কাছে সত্তার চেয়েও কোনও সেবার মতো মনে হয়। আমি দ্বিধা এবং দ্বিধা ছাড়াই বিকল্প 1 এ যাব।
বার্ট ভ্যান ইনজেন শেহেনো

3
দর্শনার্থীর জন্য (আমার কাছে) কী অনুপস্থিত তা হ'ল acceptদর্শকদের শ্রেণিবদ্ধ কাঠামো । দর্শনার্থীর জন্য অনুপ্রেরণা হ'ল আপনার কয়েকটি সামগ্রীতে অনেক শ্রেণির ধরণের রয়েছে যা দেখার দরকার এবং প্রতিটি নতুন কার্যকারিতা (অপারেশন) এর জন্য তাদের কোডটি সংশোধন করা সুবিধাজনক নয়। আমি এখনও এই সামগ্রিক অবজেক্টগুলি কী তা দেখতে পাচ্ছি না এবং ভিজিটর উপযুক্ত নয় বলে মনে করি। যদি এটি হয় তবে আপনার প্রশ্নটি সম্পাদনা করা উচিত (যা দর্শকদের বোঝায়)।
ফুহরম্যানেটর

উত্তর:


4

আমি বলব বিকল্প 1 হ'ল সর্বোত্তম পথ। কারণ আপনি এটি খারিজ করা উচিত নয় যে SendEmailTaskহয় না কোনো সত্তার। সত্তা হ'ল ডেটা এবং স্ট্যাটাস সম্পর্কিত একটি অবজেক্ট। আপনার ক্লাস এর খুব কম আছে। আসলে, এটি কোনও সত্তা নয়, তবে এটি একটি সত্তা ধারণ করে : Emailআপনি যে জিনিসটি সংরক্ষণ করছেন। তার মানে হল যে Emailকোনও পরিষেবা নেওয়া উচিত নয়, বা কোনও #Sendপদ্ধতি থাকা উচিত নয় । পরিবর্তে, আপনার এমন পরিষেবা থাকা উচিত যা সত্তা গ্রহণ করে, যেমন আপনার EmailService। সুতরাং আপনি ইতিমধ্যে সেবা সত্তা থেকে দূরে রাখার ধারণা অনুসরণ করছেন।

যেহেতু SendEmailTaskকোনও সত্তা নয়, তাই এটিতে ইমেল এবং পরিষেবাটি ইনজেক্ট করা পুরোপুরি ঠিক আছে, এবং এটি কনস্ট্রাক্টরের মাধ্যমে করা উচিত। কনস্ট্রাক্টর ইনজেকশন দিয়ে, আমরা নিশ্চিত হতে পারি যে SendEmailTaskএটি কার্য সম্পাদনের জন্য সর্বদা প্রস্তুত।

এখন আসুন কেন অন্য বিকল্পগুলি (বিশেষত সোলিডের প্রতি শ্রদ্ধা সহ ) করা উচিত নয়।

বিকল্প 2

আপনাকে ঠিকই বলা হয়েছে যে এ জাতীয় ধরণের ব্রাঞ্চ করা রাস্তায় আরও মাথাব্যথা নিয়ে আসবে। আসুন কেন তাকান। প্রথমত, ifক্লাস্টার এবং বৃদ্ধি পেতে ঝোঁক। আজ, ইমেলগুলি পাঠানো একটি কাজ, আগামীকাল, প্রতিটি বিভিন্ন ধরণের শ্রেণির জন্য আলাদা পরিষেবা বা অন্য আচরণ প্রয়োজন। ifউক্তিটি পরিচালনা করা দুঃস্বপ্নে পরিণত হয়। যেহেতু আমরা প্রকারে শাখা করছি (এবং এক্ষেত্রে সুস্পষ্ট প্রকারের ), আমরা আমাদের ভাষায় নির্মিত টাইপ সিস্টেমটি বিভক্ত করছি।

বিকল্প 2 একক দায়বদ্ধতা (এসআরপি) নয় কারণ পূর্বে পুনঃব্যবহারযোগ্য RecurringTaskSchedulerএখন এই সমস্ত বিভিন্ন ধরণের কাজগুলি এবং তাদের প্রয়োজনীয় বিভিন্ন ধরণের পরিষেবা এবং আচরণ সম্পর্কে জানতে হবে। এই শ্রেণীর পুনরায় ব্যবহার করা অনেক কঠিন। এটি ওপেন / ক্লোজড (ওসিপি) নয়। কারণ এই ধরণের টাস্ক বা সেই এক (বা এই ধরণের পরিষেবা বা সেই এক) সম্পর্কে আপনার জানা দরকার, কাজ বা পরিষেবাদির ক্ষেত্রে পৃথকীকরণের পরিবর্তনগুলি এখানে পরিবর্তনকে বাধ্য করতে পারে। একটি নতুন কাজ যোগ করবেন? একটি নতুন পরিষেবা যুক্ত করবেন? ইমেল হ্যান্ডেল করার উপায় পরিবর্তন করুন? পরিবর্তন RecurringTaskScheduler। কারণ কাজের ধরণের বিষয়টি গুরুত্বপূর্ণ, এটি লিসকভ সাবস্টিটিউশন (এলএসপি) মেনে চলে না। এটি কেবল একটি টাস্ক পেয়ে কাজটি করতে পারে না। এটি টাইপের জন্য জিজ্ঞাসা করতে হবে এবং টাইপের ভিত্তিতে এটি করুন বা এটি করুন। কার্যগুলিতে পার্থক্যগুলি encapsulate করার পরিবর্তে, আমরা সেগুলিতে সমস্ত কিছু টানছি 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 হয় যে আপনার সিস্টেমে সেবা। এটি ওসিপিও নয়। নতুন পরিষেবা যুক্ত করার অর্থ হল পরিবর্তিত হওয়া ServiceBundle, এবং পরিবর্তনের ServiceBundleঅর্থ অন্য কোথাও কার্যগুলিতে বৈষম্যমূলক পরিবর্তন। ServiceBundleএর ইন্টারফেস (আইএসপি) আলাদা করে না। এটির এই সমস্ত পরিষেবার একটি বিস্তৃত ইন্টারফেস রয়েছে, এবং কারণ এই পরিষেবাগুলির কেবল সরবরাহকারী, আমরা এর ইন্টারফেসটিকে এটি প্রদত্ত সমস্ত পরিষেবার ইন্টারফেস অন্তর্ভুক্ত করতে বিবেচনা করতে পারি। কার্যগুলি নির্ভরতা বিপর্যয় (ডিআইপি) এর সাথে আর মেনে চলে না, কারণ তাদের নির্ভরতাগুলি এর পিছনে আবদ্ধ ServiceBundle। এটি ন্যূনতম জ্ঞানের মূলধারা (ওরফে দ্য ডিমিটারের আইন) -এরও আনুগত্য করে না কারণ জিনিসগুলি তাদের চেয়ে অনেক বেশি জিনিস সম্পর্কে জানে।

বিকল্প 4

পূর্বে, আপনার কাছে প্রচুর ছোট ছোট জিনিস ছিল যা স্বতন্ত্রভাবে পরিচালনা করতে সক্ষম হয়েছিল। বিকল্প 4 এই সমস্ত বস্তু গ্রহণ করে এবং সেগুলিকে একক Visitorবস্তুতে মিশ্রিত করে । এই অবজেক্টটি আপনার সমস্ত কাজের উপর godশ্বরের অবজেক্ট হিসাবে কাজ করে। এটি আপনার RecurringTaskবস্তুগুলি রক্তস্বল্প ছায়ায় হ্রাস করে যা কেবল দর্শকদের কাছে ডাকে। সমস্ত আচরণের দিকে চলে যায় Visitor। আচরণ বদলাতে হবে? একটি নতুন টাস্ক যুক্ত করা দরকার? পরিবর্তন Visitor

আরও চ্যালেঞ্জিং অংশটি হ'ল, কারণ বিভিন্ন আচরণের সমস্তগুলি একটি একক শ্রেণিতেই রয়েছে, কিছু বহুবর্ষীয়ভাবে অন্য সমস্ত আচরণের সাথে টানছে। উদাহরণস্বরূপ, আমরা ইমেল প্রেরণের দুটি পৃথক পদ্ধতি থাকতে চাই (তাদের আলাদা আলাদা সার্ভার ব্যবহার করা উচিত?)। আমরা কীভাবে এটি করব? আমরা একটি IVisitorইন্টারফেস তৈরি করতে পারি এবং এটি বাস্তবায়িত করতে পারি, #Visit(ClearDiskTask)আমাদের আসল দর্শকের মতো সম্ভাব্যভাবে সদৃশ কোড । তারপরে যদি আমরা কোনও ডিস্ক সাফ করার একটি নতুন উপায়ে নিয়ে আসে তবে আমাদের আবার প্রয়োগ করতে হবে এবং নকল করতে হবে। তারপরে আমরা উভয় ধরণের পরিবর্তন চাই। আবার প্রয়োগ করুন এবং সদৃশ করুন। এই দুটি পৃথক, পৃথক আচরণ নিখরচায়ভাবে সংযুক্ত।

এর পরিবর্তে আমরা কেবল সাবক্লাস করতে পারি Visitor? নতুন ইমেল আচরণ সহ সাবক্লাস, নতুন ডিস্ক আচরণ সহ সাবক্লাস। এখন পর্যন্ত কোনও সদৃশ! দু'জনের সাথে সাবক্লাস? এখন একটি বা অন্যটিকে নকল করা দরকার (বা উভয়ই যদি এটি আপনার পছন্দ হয়)।

আসুন বিকল্প 1 এর সাথে তুলনা করুন: আমাদের একটি নতুন ইমেল আচরণ প্রয়োজন। আমরা এমন একটি নতুন তৈরি করতে RecurringTaskপারি যা নতুন আচরণ করে, এর নির্ভরতাগুলিতে ইনজেকশন দেয় এবং এটিতে কাজকর্মের সংগ্রহে যুক্ত করতে পারে RecurringTaskScheduler। আমাদের ডিস্ক ক্লিয়ারিং সম্পর্কেও কথা বলার দরকার নেই, কারণ সেই দায়িত্ব পুরোপুরি অন্য কোথাও। আমাদের কাছে এখনও ওও সরঞ্জামগুলির সম্পূর্ণ অ্যারে রয়েছে। উদাহরণস্বরূপ, আমরা লগিংয়ের মাধ্যমে সেই কার্যটি সাজাতে পারি।

বিকল্প 1 আপনাকে সর্বনিম্ন ব্যথা দেয় এবং এ পরিস্থিতিটি পরিচালনা করার সবচেয়ে সঠিক উপায়।


Otion2,3,4 এ আপনার বিশ্লেষণ চমত্কার! এটি সত্যই আমাকে অনেক সাহায্য করে। তবে অপশন 1 এর জন্য, আমি যুক্তি দিয়ে বলব যে * সেন্ডইমেইল টাস্ক * একটি সত্তা। এটির আইডি রয়েছে, এটির পুনরাবৃত্ত প্যাটার্ন রয়েছে এবং অন্যান্য দরকারী তথ্য যা ডিবিতে সংরক্ষণ করা উচিত। আমি অ্যান্ডি আমার উদ্দেশ্য ভালভাবে সংক্ষিপ্ত বিবরণ। * EMailTaskDifinitions * এর মতো নামটি আরও উপযুক্ত। আমি আমার পরিষেবা কোড দিয়ে আমার সত্তাকে দূষিত করতে চাই না। ইউফোরিক কিছু সমস্যার কথা উল্লেখ করেছে যদি আমি সত্তায় কোনও পরিষেবা ইনজেক্ট করি। আমি আমার প্রশ্নও আপডেট করেছি এবং অপশন 5 অন্তর্ভুক্ত করেছি, যা আমার মনে হয় এখন পর্যন্ত সেরা সমাধান।
Sher10ck

@ Sher10ck আপনি যদি SendEmailTaskকোনও ডাটাবেস থেকে আপনার জন্য কনফিগারেশনটি টানতে থাকেন তবে সেই কনফিগারেশনটি একটি পৃথক কনফিগারেশন শ্রেণি হওয়া উচিত যা আপনার মধ্যেও প্রবেশ করা উচিত SendEmailTask। আপনি যদি আপনার থেকে ডেটা SendEmailTaskতৈরি করে থাকেন তবে আপনার স্টেট স্টোর করার জন্য একটি মেমেন্টো অবজেক্ট তৈরি করা উচিত এবং এটি আপনার ডাটাবেসে রাখা উচিত।
cbojar

আমি ডিবি থেকে কনফিগারেশন টান করতে হবে, যাতে তোমার দর্শন লগ করা উভয় ইনজেকশনের ইঙ্গিত করা হয় EMailTaskDefinitionsএবং EmailServiceমধ্যে SendEmailTask? তারপরে RecurringTaskScheduler, আমার SendEmailTaskRepositoryকারও মতো দায়বদ্ধতার প্রয়োজন যাটির দায়বদ্ধতা এবং পরিষেবা লোড করা এবং সেগুলি ইনজেক্ট করা SendEmailTask। কিন্তু আমি এখন তর্ক করবে RecurringTaskSchedulerচাহিদা যে কাজটি সংগ্রহস্থলের প্রয়োগ জানতে, মত CleanDiskTaskRepository। এবং RecurringTaskSchedulerপ্রতিবার আমার একটি নতুন টাস্ক (সময়সূচীর মধ্যে ভান্ডার যুক্ত করার জন্য) বদলাতে হবে।
Sher10ck

@ Sher10ck RecurringTaskSchedulerকেবলমাত্র একটি সাধারণীকরণ করা টাস্ক সংগ্রহস্থলের ধারণা সম্পর্কে সচেতন হওয়া উচিত এবং এ RecurringTask। এটি করে এটি বিমূর্ততার উপর নির্ভর করতে পারে। টাস্ক সংগ্রহস্থলগুলির নির্মাণকারীর মধ্যে ইনজেকশনের ব্যবস্থা করা যেতে পারে RecurringTaskScheduler। তারপরে বিভিন্ন সংগ্রহস্থলগুলি কেবল তখনই জানা উচিত যেখানে RecurringTaskSchedulerইনস্ট্যান্ট করা হয় (বা কোনও কারখানায় লুকিয়ে রেখে সেখান থেকে ডাকা যেতে পারে)। কারণ এটি কেবল বিমূর্তির উপর নির্ভর করে, RecurringTaskSchedulerপ্রতিটি নতুন কাজের সাথে পরিবর্তন করার প্রয়োজন নেই। এটাই নির্ভরতা বিপরীতার মূলমন্ত্র।
cbojar

3

আপনার কি বিদ্যমান লাইব্রেরি যেমনঃ স্প্রিং কোয়ার্টজ বা স্প্রিং ব্যাচ (আপনার প্রয়োজনের সাথে সবচেয়ে বেশি ফিট করে তা আমি নিশ্চিত নই) দেখে নিই?

আপনার প্রশ্নের উত্তর:

আমি মনে করি সমস্যাটি হ'ল, আপনি কোনও বহির্মুখী পদ্ধতিতে কিছু মেটাডেটা চালিয়ে যেতে চান, সুতরাং একটি ই-মেইল টাস্কে ই-মেইল ঠিকানা বরাদ্দ থাকে, একটি লগ-টাস্ক একটি লগ-স্তর ইত্যাদি থাকে। আপনি মেমোরিতে বা আপনার ডাটাবেজে তাদের একটি তালিকা সঞ্চয় করতে পারেন তবে আলাদা আলাদা উদ্বেগগুলির জন্য আপনি সত্ত্বাকে পরিষেবা-কোড দিয়ে দূষিত করতে চান না।

আমার প্রস্তাবিত সমাধান:

আমি চলমান- এবং টাস্কটির ডেটা-অংশ আলাদা করে রাখব, যেমন, TaskDefinitionএবং এ TaskRunner। টাস্কডিফাইনিশনে একটি টাস্করুনার বা কারখানার সাথে একটি উল্লেখ রয়েছে যা একটি তৈরি করে (উদাহরণস্বরূপ যদি কিছু সেটআপ এসএমটিপি-হোস্টের মতো প্রয়োজন হয়)। কারখানাটি একটি নির্দিষ্ট একটি - এটি কেবলমাত্র পরিচালনা করতে পারে EMailTaskDefinitionএবং কেবলমাত্র EMailTaskRunnerএস এর উদাহরণ দেয় । এইভাবে এটি আরও ওও এবং নিরাপদ পরিবর্তন করুন - আপনি যদি নতুন কোনও টাস্ক টাইপ প্রবর্তন করেন তবে আপনাকে একটি নতুন নির্দিষ্ট কারখানার (বা একটি পুনরায় ব্যবহার) প্রবর্তন করতে হবে, আপনি যদি সংকলন করতে না পারেন তবে।

এইভাবে আপনি নির্ভরতার সাথে শেষ করবেন: সত্তা স্তর -> পরিষেবা স্তর এবং আবার ফিরে, কারণ রানার সত্তায় সঞ্চিত তথ্য প্রয়োজন এবং সম্ভবত ডিবিতে তার অবস্থানে আপডেট করতে চায়।

আপনি জেনেরিক কারখানাটি ব্যবহার করে এই বৃত্তটি ভেঙে ফেলতে পারেন, যা একটি TaskDefinition গ্রহণ করে এবং একটি নির্দিষ্ট TaskRunner প্রদান করে তবে এর জন্য প্রচুর ifs প্রয়োজন require আপনি এমন কোনও রানারকে খুঁজে পেতে প্রতিবিম্বটি ব্যবহার করতে পারেন যা একইরূপে আপনার সংজ্ঞা হিসাবে পরিচিত, তবে সতর্ক থাকুন এই পদ্ধতির কিছুটা পারফরম্যান্স ব্যয় হতে পারে এবং রানটাইম ত্রুটির কারণ হতে পারে।

পিএস আমি এখানে জাভা ধরে নিচ্ছি। আমি মনে করি এটি নেট এ একই রকম। এখানে প্রধান সমস্যা ডাবল বাঁধাই b

ভিজিটর প্যাটার্নে

আমি মনে করি এটি শুদ্ধ ডাবল বাঁধাইয়ের উদ্দেশ্যে নয়, রানটাইমের সময় বিভিন্ন ধরণের ডেটা অবজেক্টের জন্য একটি অ্যালগরিদম বিনিময় করার জন্য ব্যবহার করা হয়েছিল বলে মনে করি। উদাহরণস্বরূপ, যদি আপনার কাছে বিভিন্ন ধরণের বিমা এবং বিভিন্ন ধরণের গণনা থাকে, যেমন বিভিন্ন দেশগুলির এটির প্রয়োজন। তারপরে আপনি একটি নির্দিষ্ট গণনা পদ্ধতি বেছে নিন এবং এটি বেশ কয়েকটি বিমাতে প্রয়োগ করুন।

আপনার ক্ষেত্রে আপনি একটি নির্দিষ্ট টাস্ক-স্ট্র্যাটেজি (উদাহরণস্বরূপ ইমেল) বেছে নেবেন এবং এটি আপনার সমস্ত কাজে প্রয়োগ করবেন, যা ভুল কারণ এটি সমস্তই ইমেল কাজ নয়।

PS আমি এটি পরীক্ষা করে দেখিনি, তবে আমি মনে করি আপনার বিকল্প 4 টিও কাজ করবে না, কারণ এটি আবার ডাবল-বন্ডিং।


আপনি আমার উদ্দেশ্যটি খুব ভালভাবে সংক্ষিপ্ত করে রাখবেন, ধন্যবাদ! আমি বৃত্তটি ভাঙতে চাই। কারণ TaskDefiniton কে TaskRunner বা কারখানার একটি রেফারেন্স রাখার ক্ষেত্রে অপশন 1 এর মতোই সমস্যা রয়েছে। আমি ফ্যাক্টরি বা টাস্করুনারকে পরিষেবা হিসাবে বিবেচনা করি । যদি টাস্কডিফাইনিশনগুলির প্রয়োজনগুলি তাদের কাছে একটি রেফারেন্স রাখে, আপনি হয় এই পরিষেবাটি টাস্ক ডেফিনিশনে ইনজেক্ট করেছেন বা কিছু স্থির পদ্ধতি ব্যবহার করেছেন, যা আমি এড়াতে চাইছি।
Sher10ck

1

আমি নিবন্ধটির সাথে পুরোপুরি একমত নই। পরিষেবাদিগুলি (নিখরচায় তাদের "এপিআই") বিজনেস ডোমেনের গুরুত্বপূর্ণ দল এবং যেমন ডোমেন মডেলের মধ্যে উপস্থিত থাকবে। এবং ব্যবসায় ডোমেনে সত্তাগুলির সাথে একই ব্যবসায় ডোমেনে অন্য কিছু উল্লেখ করার ক্ষেত্রে কোনও সমস্যা নেই।

এক্স যখন ওয়াইকে মেইল ​​প্রেরণ করে When

ব্যবসায়ের নিয়ম। এবং এটি করার জন্য, পরিষেবাটি যা মেল প্রেরণ করা প্রয়োজন। এবং সত্তা হ্যান্ডলগুলি When Xএই পরিষেবা সম্পর্কে জেনে রাখা উচিত।

তবে বাস্তবায়নে কিছু সমস্যা আছে। সত্তার ব্যবহারকারীর পক্ষে এটি স্বচ্ছ হওয়া উচিত, সত্তা কোনও পরিষেবা ব্যবহার করছে। সুতরাং কনস্ট্রাক্টরে পরিষেবা যুক্ত করা ভাল জিনিস নয়। এটিও একটি সমস্যা, যখন আপনি ডাটাবেস থেকে সত্তাকে ডিসরিয়ালাইজ করছেন, কারণ আপনাকে সত্তার ডেটা এবং পরিষেবাগুলির উদাহরণ উভয়ই সেট করতে হবে। আমি মনে করতে পারি সেরা সমাধান হ'ল সত্তা তৈরি হওয়ার পরে সম্পত্তি ইনজেকশন ব্যবহার করা। হতে পারে যে কোনও সত্তার প্রতিটি সদ্য নির্মিত ইভেন্টকে "প্রাথমিককরণ" পদ্ধতিতে যেতে বাধ্য করা যা সত্তার জন্য প্রয়োজনীয় সমস্ত সত্তাকে সংক্রামিত করে।


আপনি কোন নিবন্ধটির সাথে উল্লেখ করছেন যে আপনি একমত নন? তবে ডোমেন মডেলটিতে আকর্ষণীয় দৃষ্টিভঙ্গি। সম্ভবত আপনি এটিকে দেখতে পাচ্ছেন, যদিও লোকেরা সাধারণত পরিষেবাগুলিকে সত্তায় মিশ্রণ এড়ায়, কারণ এটি খুব শীঘ্রই একটি শক্ত দম্পতি তৈরি করবে।
অ্যান্ডি

@ অ্যান্ডি তার প্রশ্নের মধ্যে উল্লেখ করা শের 10 একটি। এবং আমি দেখতে পাচ্ছি না কীভাবে এটি একটি শক্ত দম্পতি তৈরি করবে। কোনও খারাপভাবে লিখিত কোড আঁট সংযোগের সূচনা করতে পারে।
ইওফোরিক

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

অ্যাপ্লিকেশন আউটপুট চালানো:

ইমেল সার্ভিস কনটেন্ট সহ সেন্ডইমেইল টাস্ক চলছে 'এখানে প্রথম ইমেল এসেছে'
কনটেন্ট সহ সেন্ডইমেইল টাস্ক চালানো ইমেল সার্ভিস 'এখানে দ্বিতীয় ইমেল'
এক্সট্রেট সার্ভিস কনটেন্ট সহ
এক্সিকিউটিটাস্ক চলছে '/ বিন / বিড়ালের'
এক্সিকিউটিভ সার্ভিস '/ বিন / বিড়ালের সাথে এক্সিকিউটটাস্ক চলছে ' ইমেইল সার্ভিস এর সাথে সেন্ডইমেইল টাস্ক চলছে সামগ্রী 'এখানে তৃতীয় ইমেল'
এক্সিকিউট সার্ভিস কনটেন্ট সহ এক্সিকিউটিটাস্ক চলছে '/ বিন / গ্রেপ


আমার অনেক টাস্ক থাকতে পারে । প্রতিটি সময় আমি একটি নতুন যোগ টাস্ক , আমি পরিবর্তন করতে হবে RecurringTask এবং আমি এছাড়াও, কারণ আমি মত একটি নতুন ফাংশন যোগ করতে হবে তার সমস্ত উপ শ্রেণীর পরিবর্তন করতে হবে প্রকাশ্য বিমূর্ত বুলিয়ান Run (OtherService otherService) । আমি মনে করি অপশন 4, দর্শনার্থী প্যাটার্ন যা ডাবল প্রেরণকেও বাস্তবায়ন করে একই ধরণের সমস্যা রয়েছে।
Sher10ck

ভাল যুক্তি. আমি আমার উত্তরটি সম্পাদনা করেছি যাতে রান (পরিষেবা) পদ্ধতিগুলি রিকারিং টাস্কে সংজ্ঞায়িত হয় এবং ডিফল্টরূপে মিথ্যা ফিরে আসে। এইভাবে, যখন আপনাকে অন্য কোনও টাস্ক ক্লাস যুক্ত করার দরকার পড়বে তখন আপনাকে ভাইবালিক কার্যগুলি স্পর্শ করার দরকার নেই।
iluwatar
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.