स्विच करने के लिए कई तरीकों से लाभ


12

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

यहां हमारे प्रतिस्पर्धी कोड सुझाव दिए गए हैं (उदाहरण के रूप में उपयोग किया जाने वाला php, लेकिन अधिक सार्वभौमिक रूप से लागू किया जा सकता है):

class Switch {
   public function go($arg) {
      switch ($arg) {
         case "one":
            echo "one\n";
         break;
         case "two":
            echo "two\n";
         break;
         case "three":
            echo "three\n";
         break;
         default:
            throw new Exception("Unknown call: $arg");
         break;
      }
   }
}

class Oop {
   public function go_one() {
      echo "one\n";
   }
   public function go_two() {
      echo "two\n";
   }
   public function go_three() {
      echo "three\n";
   }
   public function __call($_, $__) {
      throw new Exception("Unknown call $_ with arguments: " . print_r($__, true));
   }
}

उनके तर्क का एक हिस्सा था "यह (स्विच विधि) सामान्य मामलों में आपके पास सामान्य मामलों की तुलना में बहुत अधिक स्वच्छ तरीका है, जो कि __call () जादू पद्धति में है।"

मैं स्वच्छता के बारे में असहमत हूं और वास्तव में कॉल को प्राथमिकता देता हूं, लेकिन मैं यह सुनना चाहता हूं कि दूसरों को क्या कहना है।

तर्क मैं Oopयोजना के समर्थन में आ सकता हूं :

  • कोड के संदर्भ में थोड़ा क्लीनर आपको लिखना होगा (कम, पढ़ने में आसान, विचार करने के लिए कम कीवर्ड)
  • सभी कार्य एक ही विधि को नहीं सौंपे गए। यहां निष्पादन में बहुत अंतर नहीं है, लेकिन कम से कम पाठ को अधिक कंपार्टमेंट किया गया है।
  • एक ही नस में, एक विशिष्ट विधि के बजाय कक्षा में कहीं भी एक और विधि जोड़ी जा सकती है।
  • तरीके नामांकित हैं, जो अच्छा है।
  • यहां लागू नहीं होता है, लेकिन एक मामले पर विचार करें जहां Switch::go()पैरामीटर के बजाय एक सदस्य पर संचालित होता है। आपको पहले सदस्य को बदलना होगा, फिर विधि को कॉल करना होगा। के लिए Oopआप किसी भी समय स्वतंत्र रूप से तरीकों कॉल कर सकते हैं।

तर्क मैं Switchयोजना के समर्थन में आ सकता हूं :

  • तर्क के लिए, डिफ़ॉल्ट (अज्ञात) अनुरोध से निपटने की क्लीनर विधि
  • कम जादुई लगता है, जिससे अपरिचित डेवलपर्स अधिक सहज महसूस कर सकते हैं

किसी को भी किसी भी पक्ष के लिए जोड़ने के लिए कुछ भी है? मैं उसके लिए एक अच्छा जवाब देना चाहता हूं।


@Justin Satyr मैंने इसके बारे में सोचा था, लेकिन मुझे लगता है कि यह प्रश्न विशेष रूप से कोड के बारे में अधिक है और एक इष्टतम समाधान ढूंढ रहा है और इस प्रकार स्टैकओवरफ़्लो के लिए उपयुक्त है। और जैसा कि @ Yes123 कहते हैं, अधिक लोगों को यहां जवाब देने की संभावना है।

__call खराब है। यह पूरी तरह से प्रदर्शन को मारता है, और आप इसका उपयोग एक ऐसी विधि को कॉल करने के लिए कर सकते हैं जो बाहरी कॉलर्स के लिए निजी होना चाहिए।

Oopप्रत्येक विधि का वर्णन करने के लिए phpdoc रखने की अनुमति देता है, जिसे कुछ आईडीई (जैसे, नेटबीन्स) द्वारा पार्स किया जा सकता है।
बाइनरीएलवी

fluffycat.com/PHP-Design-Patterns/… .. जाहिरा तौर पर स्विच यह सब कुशल नहीं है। -1

@GordonM: क्या होगा यदि प्रश्न के वर्ग में कोई निजी विधियां न हों?
JAB

जवाबों:


10

एक स्विच को ओओपी नहीं माना जाता है क्योंकि अक्सर बहुरूपता चाल कर सकता है।

आपके मामले में, एक OOP कार्यान्वयन यह हो सकता है:

class Oop 
{
  protected $goer;

  public function __construct($goer)
  {
    $this->goer = $goer;
  }

  public function go()
  {
    return $this->goer->go();
  }
}

class Goer
{
  public function go()
  {
    //...
  }
}

class GoerA extends Goer
{
  public function go()
  {
    //...
  }
}

class GoerB extends Goer
{
  public function go()
  {
    //...
  }
}

class GoerC extends Goer
{
  public function go()
  {
    //...
  }
}


$oop = new Oop(new GoerB());
$oop->go();

1
बहुरूपता ही सही उत्तर है। +1
हेन हेनरिक्स

2
यह अच्छा है, लेकिन नकारात्मक पक्ष कोड का अति-प्रसार है। आपके पास प्रत्येक विधि के लिए एक वर्ग होना चाहिए और यह अधिक कोड है .. और अधिक मेमोरी से निपटने के लिए। क्या कोई खुशहाल माध्यम नहीं है?
धमाका गोलियां

8

इस उदाहरण के लिए:

class Switch
{
    public function go($arg)
    {
        echo "$arg\n";
    }
}

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

स्विच स्टेटमेंट अक्सर लापता कक्षाओं या वर्गीकरण का संकेत होते हैं, लेकिन जरूरी नहीं। कभी-कभी एक स्विच स्टेटमेंट सिर्फ एक स्विच स्टेटमेंट होता है


+1 "शब्दार्थ, तंत्र नहीं" के लिए
जेवियर

1

शायद जवाब नहीं, लेकिन नॉन-स्विच कोड के मामले में, ऐसा लगता है कि यह एक बेहतर मैच होगा:

class Oop {
  /**
   * User calls $oop->go('one') then this function will determine if the class has a 
   * method 'go_one' and call that. If it doesn't, then you get your error.
   * 
   * Subclasses of Oop can either overwrite the existing methods or add new ones.
   */
  public function go($arg){

    if(is_callable(array($this, 'go_'. $arg))){
      return call_user_func(array($this, 'go_'. $arg));
    }

    throw new Exception("Unknown call: $arg");
  }

  public function go_one() {
    echo "one\n";
  }
  public function go_two() {
    echo "two\n";
  }
  public function go_three() {
    echo "three\n";
  }
}

पहेली का एक बड़ा हिस्सा वह है जो तब होता है जब आपको बनाने NewSwitchया बनाने की आवश्यकता होती है NewOop। क्या आपके प्रोग्रामर्स को हुप्स के माध्यम से एक विधि या दूसरे से कूदना पड़ता है? क्या होता है जब आपके नियम बदलते हैं, आदि।


मुझे आपका उत्तर पसंद है - वही पोस्ट करने जा रहा था, लेकिन निंजा आपके द्वारा प्राप्त किया गया। मुझे लगता है कि आपको विरासत में मिली सुरक्षित विधियों के साथ समस्याओं से बचने के लिए method_exists को is_callable () में बदलना चाहिए और आप अपने कोड को अधिक पठनीय बनाने के लिए call_user_func को इस $ में बदल सकते हैं -> {'go _'। $ Arg} ()। एक और बात - माबाई एक टिप्पणी जोड़ते हैं कि जादू पद्धति __call खराब क्यों है - यह उस वस्तु या इसके उदाहरण पर is_callable () कार्यक्षमता है क्योंकि यह हमेशा TRUE लौटाएगा।

मैंने अपडेट किया is_callable, लेकिन इस प्रश्न का हिस्सा नहीं होने के बाद से call_user_func को छोड़ दिया, साथ पारित करने के लिए अन्य तर्क हो सकते हैं। लेकिन अब जब यह प्रोग्रामर के पास चला गया है, तो मैं निश्चित रूप से सहमत हूं कि @ एंड्रिया का जवाब बहुत बेहतर है :)
रोब

0

केवल अपने निष्पादन कोड को कार्यों में लगाने के बजाय एक पूर्ण कमांड पैटर्न लागू करें और उनमें से प्रत्येक को अपनी कक्षा में रखें जो एक सामान्य इंटरफ़ेस लागू करता है। यह विधि आपको अपनी कक्षा के विभिन्न "मामलों" को वायर करने के लिए IOC / DI का उपयोग करने की अनुमति देती है और आपको समय के साथ अपने कोड से मामलों को आसानी से जोड़ने और हटाने की अनुमति देती है। यह आपको एक अच्छा कोड भी देता है जो SOLID प्रोग्रामिंग प्रिंसिपलों का उल्लंघन नहीं करता है।


0

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

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


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