विभिन्न इनपुट मापदंडों के साथ श्रमिकों के लिए सी # डिजाइन पैटर्न


14

मुझे यकीन नहीं है कि कौन सा डिज़ाइन पैटर्न मुझे इस मुद्दे को हल करने में मदद कर सकता है।

मेरे पास एक वर्ग है, 'कोऑर्डिनेटर', जो यह निर्धारित करता है कि किस वर्कर क्लास का उपयोग किया जाना चाहिए - बिना सभी विभिन्न प्रकार के वर्कर्स के बारे में जाने बिना - यह सिर्फ एक वर्करफैक्ट को बुलाता है और आम IWorker इंटरफ़ेस पर कार्य करता है।

तब यह काम करने के लिए उपयुक्त वर्कर को सेट करता है और अपनी 'DoWork' पद्धति का परिणाम देता है।

यह ठीक रहा ... अब तक; हमारे पास एक नए वर्कर वर्ग के लिए एक नई आवश्यकता है, "वर्करबी" जिसके लिए अपने काम को करने के लिए अतिरिक्त मात्रा में जानकारी यानी अतिरिक्त इनपुट पैरामीटर की आवश्यकता होती है।

इसकी तरह हमें अतिरिक्त इनपुट पैरामीटर के साथ एक अधिभार DoWork विधि की आवश्यकता है ... लेकिन फिर सभी मौजूदा श्रमिकों को उस पद्धति को लागू करना होगा - जो गलत लगता है क्योंकि उन श्रमिकों को वास्तव में उस पद्धति की आवश्यकता नहीं है।

मैं इसे कैसे समन्वयक कर सकता हूं कि कोऑर्डिनेटर को इस बात से अनभिज्ञ रखा जाए कि किस वर्कर का उपयोग किया जा रहा है और फिर भी प्रत्येक वर्कर को यह जानकारी प्राप्त करने की अनुमति है कि उसे अपना काम करने की आवश्यकता है लेकिन किसी भी वर्कर को ऐसी चीजें करने की आवश्यकता नहीं है जो उसे करने की आवश्यकता नहीं है?

बहुत सारे मौजूदा वर्कर पहले से हैं।

मैं नए वर्करबी वर्ग की आवश्यकताओं को समायोजित करने के लिए किसी भी मौजूदा कंक्रीट वर्कर को बदलना नहीं चाहता।

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

कोड में स्थिति:

public class Coordinator
{
    public string GetWorkerResult(string workerName, int a, List<int> b, string c)
    {
        var workerFactor = new WorkerFactory();
        var worker = workerFactor.GetWorker(workerName);

        if(worker!=null)
            return worker.DoWork(a, b);
        else
            return string.Empty;
    }
}

public class WorkerFactory
{
    public IWorker GetWorker(string workerName)
    {
        switch (workerName)
        {
            case "WorkerA":
                return new ConcreteWorkerA();
            case "WorkerB":
                return new ConcreteWorkerB();
            default:
                return null;
        }
    }
}

public interface IWorker
{
    string DoWork(int a, List<int> b);
}

public class ConcreteWorkerA : IWorker
{
    public string DoWork(int a, List<int> b)
    {
        // does the required work
        return "some A worker result";
    }
}

public class ConcreteWorkerB : IWorker
{
    public string DoWork(int a, List<int> b, string c)
    {
        // does some different work based on the value of 'c'
        return "some B worker result";
    }

    public string DoWork(int a, List<int> b)
    {
        // this method isn't really relevant to WorkerB as it is missing variable 'c'
        return "some B worker result";
    }    
}

क्या IWorkerइंटरफ़ेस पुराने संस्करण को सूचीबद्ध करता है, या यह है कि एक जोड़ा पैरामीटर के साथ एक नया संस्करण?
जेम्स एफिक्स

क्या आपके कोड आधार में वे स्थान हैं जो वर्तमान में 2 मापदंडों के साथ IWorker का उपयोग कर रहे हैं और तीसरे पैरामीटर को प्लग इन करने की आवश्यकता है या केवल 3 कॉल पैरामीटर का उपयोग करने वाली नई कॉल साइटें हैं?
जेम्सफाइक्स

2
एक पैटर्न के लिए खरीदारी करने के बजाय, समग्र डिज़ाइन पर ध्यान देने की कोशिश करें, भले ही कोई पैटर्न लागू हो या न हो। पढ़ने की सिफारिश की: "पैटर्न के लिए खरीदारी" कैसे बहुत बुरे हैं?

1
आपके कोड के अनुसार, आपको IWorker इंस्टेंस बनने से पहले आवश्यक सभी मापदंडों को पहले से ही पता है। इस प्रकार, आपको उन तर्क को कंस्ट्रक्टर के पास भेजना चाहिए था न कि डॉकवर्क विधि को। IOW, अपने कारखाने वर्ग का उपयोग करें। उदाहरण के निर्माण के विवरण को छुपाना कारखाना वर्ग के अस्तित्व का मुख्य कारण है। यदि आपने वह तरीका अपनाया है तो समाधान तुच्छ है। इसके अलावा, आप जिस तरह से पूरा करने की कोशिश कर रहे हैं उसे पूरा करने की कोशिश कर रहे हैं, यह बुरा है। यह लिस्कोव प्रतिस्थापन सिद्धांत का उल्लंघन करता है।
डंक

1
मुझे लगता है कि आपको दूसरे स्तर पर वापस जाना होगा। Coordinatorअपने GetWorkerResultकार्य में उस अतिरिक्त पैरामीटर को समायोजित करने के लिए पहले से ही परिवर्तित किया जाना था - इसका मतलब है कि एसओएलआईडी के ओपन-क्लोज्ड-सिद्धांत का उल्लंघन किया गया है। परिणामस्वरूप, सभी कोड कॉलिंग Coordinator.GetWorkerResultको भी बदलना पड़ा। तो उस जगह को देखें जहां आप उस फ़ंक्शन को कॉल करते हैं: आप कैसे तय करते हैं कि किस IWorker से अनुरोध करना है? इससे बेहतर समाधान निकल सकता है।
बर्नहार्ड हिलर

जवाबों:


9

आपको तर्कों को सामान्य बनाने की आवश्यकता होगी ताकि वे आधार इंटरफ़ेस और फ़ील्ड या गुणों की एक चर संख्या के साथ एकल पैरामीटर में फिट हो सकें। इस तरह की तरह:

public interface IArgs
{
    //Can be empty
}

public interface IWorker
{
    string DoWork(IArgs args);
}

public class ConcreteArgsA : IArgs
{
    public int a;
    public List<int> b;
}

public class ConcreteArgsB : IArgs
{
    public int a;
    public List<int> b;
    public string c;
}

public class ConcreteWorkerA : IWorker
{
    public string DoWork(IArgs args)
    {
        var ConcreteArgs = args as ConcreteArgsA;
        if (args == null) throw new ArgumentException();
        return "some A worker result";
    }
}

public class ConcreteWorkerB : IWorker
{
    public string DoWork(IArgs args)
    {
        var ConcreteArgs = args as ConcreteArgsB;
        if (args == null) throw new ArgumentException();
        return "some B worker result";
    }
} 

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

यदि आप वास्तव में तर्कों के हर संभव संयोजन के लिए ठोस वस्तुओं का निर्माण नहीं करना चाहते हैं, तो आप इसके बजाय एक टपल का उपयोग कर सकते हैं (मेरी पसंद नहीं है।)

public string GetWorkerResult(string workerName, object args)
{
    var workerFactor = new WorkerFactory();
    var worker = workerFactor.GetWorker(workerName);

    if(worker!=null)
        return worker.DoWork(args);
    else
        return string.Empty;
}

//Sample call
var args = new Tuple<int, List<int>, string>(1234, 
                                             new List<int>(){1,2}, 
                                             "A string");    
GetWorkerResult("MyWorkerName", args);

1
यह उसी तरह है जैसे कि विंडोज़ फॉर्म घटनाओं से कैसे निपटते हैं। 1 "आर्ग्स" पैरामीटर, और एक "इवेंट का स्रोत" पैरामीटर। सभी "args" EventArgs से उप- वर्गित हैं : msdn.microsoft.com/en-us/library/… -> मैं कहूंगा कि यह पैटर्न बहुत अच्छा काम करता है। मुझे सिर्फ "टपल" सुझाव पसंद नहीं है।
मचाडो

if (args == null) throw new ArgumentException();अब एक IWorker के प्रत्येक उपभोक्ता को इसके ठोस प्रकार का पता होना चाहिए - और इंटरफ़ेस बेकार है: आप इससे छुटकारा पा सकते हैं और इसके बजाय कंक्रीट के प्रकारों का उपयोग कर सकते हैं। और यह एक बुरा विचार है, है ना?
बर्नहार्ड हिलर

प्लगवेबल आर्किटेक्चर ( WorkerFactory.GetWorkerकेवल एक रिटर्न प्रकार हो सकता है) के कारण IWorker इंटरफ़ेस की आवश्यकता है । इस उदाहरण के दायरे से बाहर, हम जानते हैं कि कॉलर एक के साथ आने में सक्षम है workerName; संभवतः यह उचित तर्क के साथ भी आ सकता है।
जॉन वू

2

मैंने @ डंक की टिप्पणी के आधार पर समाधान को फिर से इंजीनियर किया है:

... आप पहले से ही जानते हैं कि IWorker उदाहरण बनने से पहले सभी मापदंडों की आवश्यकता है। इस प्रकार, आपको उन तर्क को कंस्ट्रक्टर के पास भेजना चाहिए था न कि डॉकवर्क विधि को। IOW, अपने कारखाने वर्ग का उपयोग करें। उदाहरण के निर्माण के विवरण को छुपाना कारखाना वर्ग के अस्तित्व का मुख्य कारण है।

इसलिए मैंने IWorerFactory.GetWorker विधि में एक IWorker बनाने के लिए आवश्यक सभी संभावित तर्कों को स्थानांतरित कर दिया है और फिर प्रत्येक कार्यकर्ता के पास पहले से ही इसकी आवश्यकता है और समन्वयक बस कार्यकर्ता को कॉल कर सकते हैं। DoWork ();

    public interface IWorkerFactory
    {
        IWorker GetWorker(string workerName, int a, List<int> b, string c);
    }

    public class WorkerFactory : IWorkerFactory
    {
        public IWorker GetWorker(string workerName, int a, List<int> b, string c)
        {
            switch (workerName)
            {
                case "WorkerA":
                    return new ConcreteWorkerA(a, b);
                case "WorkerB":
                    return new ConcreteWorkerB(a, b, c);
                default:
                    return null;
            }
        }
    }

    public class Coordinator
    {
        private readonly IWorkerFactory _workerFactory;

        public Coordinator(IWorkerFactory workerFactory)
        {
            _workerFactory = workerFactory;
        }

        // Adding 'c' breaks Open/Closed principal for the Coordinator and WorkerFactory; but this has to happen somewhere...
        public string GetWorkerResult(string workerName, int a, List<int> b, string c)
        {
            var worker = _workerFactory.GetWorker(workerName, a, b, c);

            if (worker != null)
                return worker.DoWork();
            else
                return string.Empty;
        }
    }

    public interface IWorker
    {
        string DoWork();
    }

    public class ConcreteWorkerA : IWorker
    {
        private readonly int _a;
        private readonly List<int> _b;

        public ConcreteWorkerA(int a, List<int> b)
        {
            _a = a;
            _b = b;
        }

        public string DoWork()
        {
            // does the required work based on 'a' and 'b'
            return "some A worker result";
        }
    }

    public class ConcreteWorkerB : IWorker
    {
        private readonly int _a;
        private readonly List<int> _b;
        private readonly string _c;

        public ConcreteWorkerB(int a, List<int> b, string c)
        {
            _a = a;
            _b = b;
            _c = c;
        }

        public string DoWork()
        {
            // does some different work based on the value of 'a', 'b' and 'c'
            return "some B worker result";
        }
    }

1
आपके पास एक कारखाना तरीका है जो 3 पैरामीटर प्राप्त करता है, भले ही सभी स्थितियों में सभी 3 का उपयोग नहीं किया जा रहा हो। यदि आपके पास एक ऐसी वस्तु C है जो और भी अधिक मापदंडों की आवश्यकता है तो आप क्या करेंगे? क्या आप उन्हें विधि हस्ताक्षर से जोड़ेंगे? यह समाधान IMO
अमोर्फिस

3
अगर मुझे एक नए कंकरीटवर्कर की आवश्यकता है, जिसे अधिक तर्क की आवश्यकता है, तो हाँ, उन्हें गेटवॉकर विधि में जोड़ा जाएगा। हां, फैक्टरी ओपन / बंद प्रिंसिपल के अनुरूप नहीं है - लेकिन कहीं न कहीं कुछ इस तरह का होना चाहिए और मेरी राय में फैक्टरी सबसे अच्छा विकल्प था। मेरा सुझाव है: यह कहने के बजाय कि यह बीमार है, आप वास्तव में वैकल्पिक समाधान पोस्ट करके समुदाय की मदद करेंगे।
जेटी

1

मैं कई चीजों में से एक का सुझाव दूंगा।

यदि आप इनकैप्सुलेशन बनाए रखना चाहते हैं, ताकि कॉलगर्ल्स को श्रमिकों या श्रमिक कारखाने के आंतरिक कामकाज के बारे में कुछ भी पता न हो, तो आपको अतिरिक्त पैरामीटर के लिए इंटरफ़ेस को बदलना होगा। पैरामीटर में एक डिफ़ॉल्ट मान हो सकता है, ताकि कुछ कॉललाइट अभी भी 2 मापदंडों का उपयोग कर सकें। इसके लिए आवश्यक होगा कि किसी भी उपभोग करने वाले पुस्तकालयों को पुन: स्थापित किया जाए।

दूसरे विकल्प के खिलाफ मैं सिफारिश करूंगा, क्योंकि यह एनकैप्सुलेशन को तोड़ता है और आमतौर पर ओओपी खराब है। इसके लिए यह भी आवश्यक है कि आप कम से कम सभी कॉललाइट्स को संशोधित कर सकें ConcreteWorkerB। आप एक वर्ग बना सकते हैं जो IWorkerइंटरफ़ेस को लागू करता है, लेकिन DoWorkएक अतिरिक्त पैरामीटर के साथ एक विधि भी है । फिर अपने कॉललाइट्स के IWorkerसाथ कास्ट करने का प्रयास करें var workerB = myIWorker as ConcreteWorkerB;और फिर DoWorkकंक्रीट प्रकार पर तीन पैरामीटर का उपयोग करें । फिर, यह एक बुरा विचार है, लेकिन यह कुछ ऐसा है जो आप कर सकते हैं।


0

@ जेट, क्या आपने paramsतर्क के उपयोग पर विचार किया है? यह मापदंडों की एक चर राशि को पारित करने की अनुमति देता है।

https://msdn.microsoft.com/en-us/library/w5zay9db(v=vs.71).aspx


Params कीवर्ड समझ सकता है कि क्या DoWork विधि ने प्रत्येक तर्क के साथ एक ही काम किया है और यदि प्रत्येक तर्क एक ही प्रकार का है। अन्यथा, DoWork विधि को यह जांचने की आवश्यकता होगी कि परमेस सरणी में प्रत्येक तर्क सही प्रकार का था - लेकिन हम कहते हैं कि हमारे पास दो तार हैं और प्रत्येक एक अलग उद्देश्य के लिए उपयोग किया गया था, DoWork यह कैसे सुनिश्चित कर सकता है कि यह सही है एक ... यह सरणी में स्थिति के आधार पर ग्रहण करना होगा। मेरी पसंद के हिसाब से सभी ढीले हैं। मुझे लगता है कि @ JohnWu का समाधान तंग है।
जेट
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.