कार्यों का नक्शा बनाम स्विच स्टेटमेंट


21

मैं एक ऐसी परियोजना पर काम कर रहा हूं जो अनुरोधों को संसाधित करती है, और अनुरोध के दो घटक हैं: कमांड और पैरामीटर। प्रत्येक कमांड के लिए हैंडलर बहुत सरल है (<10 लाइनें, अक्सर <5)। कम से कम 20 कमांड हैं, और संभावना 50 से अधिक होगी।

मैं समाधान के एक जोड़े के साथ आया हूँ:

  • एक बड़ा स्विच / यदि -अन्य आदेशों पर
  • कार्यों के लिए कमांड का नक्शा
  • स्टेटिक क्लास / सिंगलेट्स के लिए कमांड का नक्शा

प्रत्येक कमांड थोड़ी सी त्रुटि की जाँच करता है, और केवल एक बिट जिसे अमूर्त किया जा सकता है, मापदंडों की संख्या के लिए जाँच कर रहा है, जो प्रत्येक कमांड के लिए परिभाषित है।

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

मैं प्रत्येक के लिए निम्नलिखित समर्थक / चोर सूची के साथ आया हूँ:

स्विच

  • पेशेवरों
    • सभी फ़ंक्शन को एक फ़ंक्शन में रखता है; चूंकि वे सरल हैं, इसलिए यह एक दृश्य खोज तालिका बनाता है
    • छोटे कार्यों / वर्गों के टन के साथ स्रोत को अव्यवस्थित करने की आवश्यकता नहीं है जो केवल एक ही स्थान पर उपयोग किए जाएंगे
  • विपक्ष
    • बहुत लम्बा
    • प्रोग्रामेटिक रूप से कमांड्स जोड़ना मुश्किल है (डिफ़ॉल्ट केस का उपयोग करके चेन की आवश्यकता है)

नक्शा कमांड -> फ़ंक्शन

  • पेशेवरों
    • छोटे, काटने का आकार
    • प्रोग्रामेटिक रूप से कमांड जोड़ / हटा सकते हैं
  • विपक्ष
    • यदि इन-लाइन किया जाता है, तो समान रूप से स्विच के रूप में
    • यदि इन-लाइन नहीं किया जाता है, तो बहुत सारे फ़ंक्शन केवल एक ही स्थान पर उपयोग किए जाते हैं

मैप कमांड्स -> स्टेटिक क्लास / सिंगलटन

  • पेशेवरों
    • साधारण त्रुटि जाँच को संभालने के लिए बहुरूपता का उपयोग कर सकते हैं (केवल 3 पंक्तियों की तरह, लेकिन फिर भी)
    • मानचित्र के समान लाभ -> फ़ंक्शन समाधान
  • विपक्ष
    • बहुत छोटे वर्गों के बहुत से परियोजना अव्यवस्था होगी
    • कार्यान्वयन सभी एक ही स्थान पर नहीं है, इसलिए कार्यान्वयन को स्कैन करना उतना आसान नहीं है

अतिरिक्त नोट:

मैं इसे गो में लिख रहा हूं, लेकिन मुझे नहीं लगता कि समाधान भाषा-विशिष्ट है। मैं एक अधिक सामान्य समाधान की तलाश कर रहा हूं क्योंकि मुझे अन्य भाषाओं में कुछ ऐसा ही करने की आवश्यकता हो सकती है।

एक कमांड एक स्ट्रिंग है, लेकिन मैं आसानी से इसे एक नंबर पर मैप कर सकता हूं यदि सुविधाजनक हो। फ़ंक्शन हस्ताक्षर कुछ इस तरह है:

Reply Command(List<String> params)

गो में शीर्ष-स्तरीय फ़ंक्शंस हैं, और जिन अन्य प्लेटफ़ॉर्म पर मैं विचार कर रहा हूं उनमें शीर्ष-स्तरीय फ़ंक्शंस भी हैं, इसलिए दूसरे और तीसरे विकल्प के बीच अंतर है।


8
मानचित्र कार्यों के लिए आदेश देता है और उन्हें कॉन्फ़िगरेशन से रनटाइम पर लोड करता है। कमांड पैटर्न का उपयोग करें।
स्टीवन एवर्स

3
बहुत सारे छोटे कार्य करने से डरें नहीं। आमतौर पर, कई छोटे कार्यों का एक संग्रह एक विशाल समारोह की तुलना में अधिक बनाए रखने योग्य होता है।
बार्ट वैन इनगेन शेनौ

8
इस टॉपसी-टरवी दुनिया के साथ क्या है, जहां लोग टिप्पणियों में सवाल जवाब करते हैं और जवाबों में अधिक जानकारी मांगते हैं?
pdr

4
@SteveEvers: यदि इसे विस्तार की आवश्यकता नहीं है, तो यह एक उत्तर है, चाहे कितना भी छोटा क्यों न हो। यदि ऐसा होता है, और आपके पास समय नहीं है या जो भी है, तो उसे किसी और को जवाब देने के लिए छोड़ दें (हमेशा ऐसा लगता है कि जवाब लिखने के लिए धोखा देना है जो एक टिप्पणी की पुष्टि करता है जिसमें पहले से ही आधा दर्जन upvotes हैं)। व्यक्तिगत रूप से, मुझे लगता है कि इसे विस्तार की आवश्यकता है, ओपी वास्तव में जानना चाहता है कि सबसे अच्छा समाधान सबसे अच्छा समाधान क्यों है।
पीडीआर

1
@pdr - सही है। मेरा झुकाव कार्यों के लिए आदेशों का एक नक्शा था, लेकिन मैं सीएस डिजाइन पाठ्यक्रम में एक अपेक्षाकृत जूनियर प्रोग्रामर हूं। मेरे प्रोफेसर को बहुत सारी कक्षाएं पसंद हैं, इसलिए कम से कम 2 वैध समाधान हैं। मैं समुदाय का पसंदीदा जानना चाहता था।
बीटगैमिट

जवाबों:


14

यह एक मानचित्र (2 या 3 प्रस्तावित समाधान) के लिए एक महान फिट है। मैंने इसे दर्जनों बार उपयोग किया है, और यह सरल और प्रभावी है। मैं वास्तव में इन समाधानों के बीच अंतर नहीं करता हूं; महत्वपूर्ण बात यह है कि चाबियाँ के रूप में फ़ंक्शन नामों के साथ एक नक्शा है।

मेरी राय में, नक्शे के दृष्टिकोण का प्रमुख लाभ यही है तालिका डेटा है। इसका मतलब है कि इसे चारों ओर से पारित किया जा सकता है, संवर्धित किया जा सकता है, या अन्यथा रनटाइम में संशोधित किया जा सकता है; अतिरिक्त कार्यों को कोड करना भी आसान है जो नए, रोमांचक तरीकों से मानचित्र की व्याख्या करते हैं। यह मामला / स्विच समाधान के साथ संभव नहीं होगा।

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

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


4

आपकी समस्या कमांड डिज़ाइन पैटर्न को बहुत अच्छी तरह से उधार देती है । तो मूल रूप से आपके पास एक आधार Commandइंटरफ़ेस होगा और फिर कई CommandImplवर्ग होंगे जो उस इंटरफ़ेस को लागू करेंगे। इंटरफ़ेस अनिवार्य रूप से सिर्फ एक एकल विधि की जरूरत है doCommand(Args args)। आपके पास Argsकक्षा के एक उदाहरण के माध्यम से दिए गए तर्क हो सकते हैं । इस तरह से आप क्लूनी के बजाय बहुरूपता की शक्ति का लाभ उठाते हैं यदि / और कथन। साथ ही यह डिज़ाइन आसानी से एक्स्टेंसिबल है।


3

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

यदि आप स्विच स्टेटमेंट (या विज़िटर पैटर्न) का उपयोग करते हैं तो नए कार्यों को जोड़ना आसान है (क्योंकि आप एक ही फ़ंक्शन में सब कुछ मिटाते हैं) लेकिन नए मामलों को जोड़ने के लिए कठिन है (क्योंकि आपको वापस जाने और पुराने कार्यों को संपादित करने की आवश्यकता है)

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

आपके मामले में आपके पास केवल एक तरीका है, जिसे आपको समर्थन करने की आवश्यकता है (एक अनुरोध को सफल) लेकिन बहुत सारे संभावित मामलों (प्रत्येक अलग कमांड)। चूंकि नए मामलों को जोड़ना आसान है, इसलिए नए तरीकों को जोड़ना अधिक महत्वपूर्ण है, बस प्रत्येक अलग कमांड के लिए एक अलग वर्ग बनाएं।


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

उदाहरण के लिए, आप आमतौर पर डेलिगेशन पर भरोसा करने के बजाय त्रुटि जाँच भाग करने के लिए प्रतिनिधिमंडल का उपयोग कर सकते हैं (मेरा उदाहरण जावास्क्रिप्ट में है क्योंकि O को गो सिंटैक्स नहीं पता है, मुझे आशा है कि आपको कोई आपत्ति नहीं है)

function make_command(real_command){
    return function(x){
        if(check_arguments(x)){
            return real_command(x);
        }else{
            //handle error here
        }
    }
 }

 command1 = make_command(function(x){ 
     //do something
 })

 command2 = make_command(function(x){
     //do something else
 })

 command1(17);
 commnad2(42);

बेशक, यह उदाहरण मानता है कि प्रत्येक मामले के लिए रैपर फ़ंक्शन या पैरेंट क्लास चेक तर्कों का एक समझदार तरीका है। इस बात पर निर्भर करता है कि चीजों को कमांड के अंदर check_arguments पर कॉल करने के लिए कितना सरल हो सकता है (क्योंकि प्रत्येक कमांड को अलग-अलग तर्क, विभिन्न कमांड प्रकार, आदि के कारण चेकिंग फ़ंक्शन को विभिन्न तर्कों के साथ कॉल करने की आवश्यकता हो सकती है)

tl; dr: सभी समस्याओं को हल करने का कोई सबसे अच्छा तरीका नहीं है। "चीजों को काम करने" के नजरिए से, महत्वपूर्ण तत्वों को लागू करने और गलतियों से बचाने के तरीकों में अपने अमूर्त बनाने पर ध्यान केंद्रित करें। "भविष्य-प्रूफिंग" परिप्रेक्ष्य से, यह ध्यान रखें कि कोड के किन हिस्सों को बढ़ाए जाने की अधिक संभावना है।


1

मैंने कभी भी गो का उपयोग नहीं किया है, एसी # प्रोग्रामर के रूप में मैं शायद निम्नलिखित पंक्ति में नीचे जाऊंगा, उम्मीद है कि यह आर्किटेक्चर फिट होगा जो आप कर रहे हैं।

मैं निष्पादित करने के लिए मुख्य फ़ंक्शन के साथ प्रत्येक के लिए एक छोटा वर्ग / ऑब्जेक्ट बनाऊंगा, प्रत्येक को इसके स्ट्रिंग प्रतिनिधित्व को जानना चाहिए। यह आपको सुगमता प्रदान करता है जो ऐसा लगता है जैसे आप चाहते हैं कि कार्यों की संख्या बढ़ जाएगी। ध्यान दें कि मैं स्टैटिक्स का उपयोग नहीं करूंगा, जब तक कि आपको वास्तव में आवश्यकता न हो, वे बहुत अधिक लाभ नहीं देते हैं।

मैं तो एक कारखाना है जो जानता है कि कैसे इन वर्गों को चलाने के समय में उन्हें अलग-अलग विधानसभाओं आदि से लोड करने के लिए बदलना होगा, इसका मतलब है कि आपको एक ही परियोजना में उन सभी की आवश्यकता नहीं है।

इस प्रकार यह भी परीक्षण के लिए अधिक मॉड्यूलर बनाता है और कोड को अच्छा और छोटा बनाना चाहिए, जिसे बाद में बनाए रखना आसान होता है।


0

आप अपने आदेश / कार्यों का चयन कैसे करते हैं?

यदि सही फ़ंक्शन का चयन करने का कुछ "चतुर" तरीका है, तो जाने का तरीका है - इसका मतलब है कि आप मुख्य तर्क को संशोधित किए बिना नए समर्थन (शायद एक बाहरी पुस्तकालय में) जोड़ सकते हैं।

इसके अलावा, अलग-अलग स्विच स्टेटमेंट की तुलना में अलग-अलग फ़ंक्शन को अलग करना आसान है।

अंत में, केवल एक ही स्थान पर उपयोग किया जा रहा है - आप पा सकते हैं कि एक बार जब आप 50 तक पहुंच जाते हैं तो विभिन्न कार्यों के विभिन्न बिट्स का पुन: उपयोग किया जा सकता है?


आदेश अद्वितीय तार हैं। जरूरत पड़ने पर मैं इन्हें पूर्णांक में मैप कर सकता हूं।
बीटगैमिट

0

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

अलग-अलग उपयोग के मामलों को आसानी से जोड़ा जा सकता है और मौजूदा श्रृंखला के (या अंत में) लिंक के बीच एक नया लिंक जोड़कर हटाया जा सकता है।

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

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.