कैसे कई स्विच मामलों के साथ एक आवेदन refactor करने के लिए?


10

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

मैंने कुछ सोचा और इस विचार के साथ आया। मैं रणनीति पैटर्न का उपयोग करता हूं। स्विच केस होने के बजाय, मेरे पास स्ट्रेटेजी पूर्णांक होने के साथ स्ट्रैटेजी ऑब्जेक्ट का मैप है। एक बार विधि लागू होने पर, यह ऑब्जेक्ट को देखेगा और ऑब्जेक्ट के लिए जेनेरिक विधि को कॉल करेगा। इस तरह मैं स्विच केस निर्माण का उपयोग करने से बच सकता हूं।

तुम क्या सोचते हो?


2
वर्तमान कोड के साथ वास्तविक समस्या क्या है?
फिलिप केंडल

जब आपको इनमें से कोई एक परिवर्तन करना हो तो क्या होता है? क्या आपको एक switchमामला जोड़ना है और अपने जटिल सिस्टम में पहले से मौजूद विधि को कॉल करना है, या क्या आपको विधि और उसके कॉल दोनों का आविष्कार करना है?
किलन फ़ॉथ

@KilianFoth को मैं एक रखरखाव डेवलपर के रूप में यह परियोजना विरासत में मिली है और अभी तक इसमें कोई बदलाव नहीं करना पड़ा है। हालांकि मैं जल्द ही बदलाव करूंगा, इसलिए मैं अब रिफ्लेक्टर करना चाहता हूं। लेकिन आपके सवाल का जवाब देने के लिए, बाद के लिए हाँ।
कौशिक चक्रवर्ती

2
मुझे लगता है कि आपको क्या हो रहा है, इसका एक संक्षिप्त उदाहरण दिखाने की आवश्यकता है।
whatsisname

1
@ कौशिकचक्रवर्ती: फिर स्मृति से एक उदाहरण बनाते हैं। ऐसी परिस्थितियां हैं जहां 250+ केस uber- स्विच उपयुक्त है, और ऐसे मामले हैं जहां स्विच खराब है, चाहे कितने भी मामले हों। शैतान विवरण में है और हमारे पास कोई विवरण नहीं है।
whatsisname

जवाबों:


13

स्विच में अब 50 मामले हैं और हर बार मुझे एक और मामला जोड़ने की जरूरत है, मैं कंपकंपी करता हूं।

मुझे बहुरूपता पसंद है। मैं ठोस प्यार करता हूँ। मुझे शुद्ध वस्तु उन्मुख प्रोग्रामिंग पसंद है। मुझे यह देखकर बुरा लगता है कि उन्हें बुरा माना गया क्योंकि वे हठधर्मिता से लागू होते हैं।

आपने रणनीति को फिर से तैयार करने के लिए एक अच्छा मामला नहीं बनाया है। जिस तरह से रिफलेक्टिंग का एक नाम है। इसे पॉलीमॉर्फिज्म के साथ रिप्लेस कंडिशनल कहा जाता है ।

मैंने c2.com से आपके लिए कुछ उचित सलाह ली है :

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

आपके पास 50 मामलों के साथ एक स्विच है और आपका विकल्प 50 वस्तुओं का उत्पादन करना है। ओह और ऑब्जेक्ट निर्माण कोड की 50 लाइनें। यह प्रगति नहीं है। क्यों नहीं? क्योंकि यह रीफैक्टरिंग 50 से संख्या को कम करने के लिए कुछ भी नहीं करता है। आप इस रीफैक्टरिंग का उपयोग तब करते हैं जब आपको लगता है कि आपको कहीं और उसी इनपुट पर एक और स्विच स्टेटमेंट बनाने की आवश्यकता है। जब यह रिफैक्टिंग मदद करता है क्योंकि यह 50 में वापस 100 हो जाता है।

जब तक आप "स्विच" की बात कर रहे हैं, जैसे कि आपके पास केवल यही है, मैं इसकी अनुशंसा नहीं करता। अब केवल रिफैक्टरिंग से आने वाला लाभ यह है कि यह उन अवसरों को कम करता है जो कुछ गॉफबॉल आपके 50 केस स्विच को कॉपी और पेस्ट करेंगे।

मैं जो सिफारिश करता हूं, वह इन 50 मामलों को करीब से देख रहा है। मेरा मतलब 50 है? वास्तव में? आपको यकीन है कि आपको कई मामलों की आवश्यकता है? आप यहां बहुत कुछ करने की कोशिश कर रहे होंगे।


तुम जो कह रहे हो मैं उससे सहमत हूँ। कोड में बहुत अधिक अतिरेक हैं, यह हो सकता है कि बहुत सारे मामले आवश्यक भी न हों लेकिन सरसरी नज़र से ऐसा प्रतीत नहीं होता है। प्रत्येक मामले में एक विधि होती है जो कई प्रणालियों को कॉल करती है और परिणामों को एकत्रित करती है और कॉलिंग कोड पर वापस आती है। प्रत्येक वर्ग स्वयं निहित है, एक काम करता है और मुझे डर है कि मैं उच्च सामंजस्य सिद्धांत का उल्लंघन करूंगा, क्या मैं मामलों की संख्या को कम करने के लिए था।
कौशिक चक्रवर्ती

2
मैं उच्च सामंजस्य का उल्लंघन किए बिना 50 प्राप्त कर सकता हूं और चीजों को स्वयं में निहित रख सकता हूं। मैं सिर्फ एक नंबर के साथ ऐसा नहीं कर सकता। मुझे 2, 5, और 5 की आवश्यकता होगी। यही कारण है कि इसे फैक्टरिंग कहा जाता है। गंभीरता से, अपने पूरे आर्किटेक्चर को देखें और देखें कि क्या आप इस 50 केस नर्क से बाहर निकलने का कोई रास्ता नहीं तलाश सकते। उन्हें नए रूपों में शामिल नहीं करना।
कैंडिड_ऑरेंज

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

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

@ "आप इस रिफैक्टरिंग का उपयोग तब करते हैं जब आपको लगता है कि आपको एक ही इनपुट पर एक और स्विच स्टेटमेंट कहीं और बनाने की आवश्यकता है। क्या आप इसे विस्तृत कर सकते हैं? जैसा कि मेरे पास एक समान मामला है जहां मेरे पास स्विच के मामले हैं लेकिन विभिन्न परतों पर जैसे कि हमारे पास है?" परियोजना पहले प्राधिकरण, सत्यापन, सीआरयूडी प्रक्रियाएं फिर डीएओ। तो हर परत में एक ही इनपुट यानी एक पूर्णांक पर स्विच के मामले होते हैं, लेकिन अलग-अलग कार्यों को करते हैं जैसे कि, वैध। तो क्या हमें प्रत्येक प्रकार के एक वर्ग का निर्माण करना चाहिए जिसमें विभिन्न विधियाँ हों? क्या हमारा मामला "आप एक ही इनपुट पर एक ही स्विच को दोहराकर" कहने की कोशिश कर रहे हैं?
सिद्धार्थ त्रिखा

9

अकेले रणनीति वस्तुओं का एक मानचित्र, जिसे आपके कोड के कुछ फ़ंक्शन में आरंभीकृत किया गया है, जहाँ आपके पास कोड की कई पंक्तियाँ दिखती हैं

     myMap.Add(1,new Strategy1());
     myMap.Add(2,new Strategy2());
     myMap.Add(3,new Strategy3());

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

     case 1:
          MyClass1.Doit1(someParameters);
          break;
     case 2:
          MyClass2.Doit2(someParameters);
          break;
     case 3:
          MyClass3.Doit3(someParameters);
          break;

हालाँकि, यह तब भी आपको इस कोड फ़ाइल को संपादित करने के बोझ से मुक्त नहीं करेगा, जब भी कोई नया नंबर जोड़ना होगा। इस दृष्टिकोण का वास्तविक लाभ एक अलग है:

  • मानचित्र का आरंभ अब प्रेषण कोड से अलग हो जाता है जो वास्तव में एक विशिष्ट संख्या से संबंधित फ़ंक्शन को कॉल करता है, और बाद में उन 50 पुनरावृत्तियों में कोई भी अधिक नहीं होता है, यह बस जैसा दिखेगा myMap[number].DoIt(someParameters)। इसलिए जब भी कोई नया नंबर आता है और ओपन-क्लोज्ड सिद्धांत के अनुसार इसे लागू किया जा सकता है, तो इस प्रेषण कोड को छूने की आवश्यकता नहीं है। इसके अलावा, जब आपको आवश्यकताएं प्राप्त होती हैं, जहां आपको प्रेषण कोड का विस्तार करने की आवश्यकता होती है, तो आपको 50 स्थानों को किसी भी स्थान पर बदलना नहीं होगा, लेकिन केवल एक।

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

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


जबकि मैं विनम्रता से हर स्विच को बहुरूपता के साथ बदलने के लिए अनिच्छुक हूं, मैं कहूंगा कि जिस तरह से यहां डॉक ब्राउन ने सुझाव दिया है उसका उपयोग करके अतीत में मेरे लिए बहुत अच्छा काम किया है। आप एक ही इंटरफ़ेस को लागू जब कृपया की जगह Doit1, Doit2एक के साथ आदि, Doitविधि कई अलग अलग कार्यान्वयन है।
कैंडिड_ऑरेंज

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

1
@KevinKrumwiede: आप जो सुझाव देते हैं, उसका अर्थ है कि पूर्णांक के लिए एक प्रतिस्थापन के रूप में, कार्यक्रम में खुद को चारों ओर से रणनीति बनाना। हालाँकि, जब प्रोग्राम कुछ बाहरी डेटा स्रोत से इनपुट के रूप में पूर्णांक लेता है, तो सिस्टम के कम से कम एक स्थान पर पूर्णांक से संबंधित रणनीति के लिए एक मानचित्रण होना होता है।
डॉक्टर ब्राउन

डॉक्टर ब्राउन के सुझाव पर विस्तार: आप एक कारखाना भी बना सकते हैं जिसमें रणनीति ऑब्जेक्ट बनाने के लिए तर्क होगा, क्या आपको इस तरह से जाने का निर्णय लेना चाहिए। उस ने कहा, CandiedOrange द्वारा प्रदान किया गया जवाब मेरे लिए सबसे ज्यादा मायने रखता है।
व्लादिमीर स्टोकिक

@DocBrown "अगर आप इनपुट प्रतीक के प्रकार पर नियंत्रण रखते हैं तो मैं इसके साथ मिल रहा था।"
केविन क्रुमविडे

0

मैं @DocBrown द्वारा उत्तर में उल्लिखित रणनीति के पक्ष में हूं

मैं उत्तर में सुधार का सुझाव देने जा रहा हूं।

कॉल करता है

 myMap.Add(1,new Strategy1());
 myMap.Add(2,new Strategy2());
 myMap.Add(3,new Strategy3());

वितरित किया जा सकता है। आपको दूसरी रणनीति जोड़ने के लिए उसी फ़ाइल पर वापस जाने की ज़रूरत नहीं है, जो ओपन-क्लोज्ड सिद्धांत का बेहतर तरीके से पालन करता है।

कहते हैं कि आप Strategy1फ़ाइल Strategy1.cpp में लागू करें । आप इसमें कोड के निम्नलिखित ब्लॉक कर सकते हैं।

namespace Strategy1_Impl
{
   struct Initializer
   {
      Initializer()
      {
         getMap().Add(1, new Strategy1());
      }
   };
}
using namespace Strategy1_Impl;

static Initializer initializer;

आप प्रत्येक StategyN.cpp फ़ाइल में समान कोड दोहरा सकते हैं। जैसा कि आप देख सकते हैं, कि बार-बार कोड होगा। कोड दोहराव को कम करने के लिए, आप एक टेम्पलेट का उपयोग कर सकते हैं, जिसे सभी Strategyवर्गों के लिए सुलभ फ़ाइल में डाला जा सकता है ।

namespace StrategyHelper
{
   template <int N, typename StrategyType> struct Initializer
   {
      Initializer()
      {
         getMap().Add(N, new StrategyType());
      }
   };
}

उसके बाद, आपको Strategy1.cpp में केवल एक चीज का उपयोग करना है:

static StrategyHelper::Initializer<1, Strategy1> initializer;

StrategyN.cpp में इसी लाइन है:

static StrategyHelper::Initializer<N, StrategyN> initializer;

आप ठोस रणनीति वर्गों के लिए एक वर्ग टेम्पलेट का उपयोग करके टेम्पलेट्स का उपयोग दूसरे स्तर तक ले जा सकते हैं।

class Strategy { ... };

template <int N> class ConcreteStrategy;

और फिर, इसके बजाय Strategy1, का उपयोग करें ConcreteStrategy<1>

template <> class ConcreteStrategy<1> : public Strategy { ... };

StrategyS को रजिस्टर करने के लिए हेल्पर क्लास बदलें :

namespace StrategyHelper
{
   template <int N> struct Initializer
   {
      Initializer()
      {
         getMap().Add(N, new ConcreteStrategy<N>());
      }
   };
}

Strateg1.cpp में कोड को इसमें बदलें:

static StrategyHelper::Initializer<1> initializer;

StrategN.cpp में कोड बदलें:

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