जब एनम एक कोड गंध नहीं हैं?


17

दुविधा

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

जैसे, मैं दिशानिर्देशों और / या उपयोग के मामलों की तलाश कर रहा हूं जहां एनम एक कोड गंध नहीं हैं और वास्तव में एक वैध निर्माण है।

सूत्रों का कहना है:

"चेतावनी, अंगूठे के एक नियम के रूप में, एनम कोड की बदबू आ रही है और बहुरूपी कक्षाओं में जाना चाहिए। [classes]" सेमैन, मार्क, .Net, २०११, पी। 342

[[] मार्टिन फाउलर एट अल।, रीफैक्टरिंग: इम्प्रूविंग ऑफ द डिजाइनिंग ऑफ मौजूदा कोड (न्यूयॉर्क: एडिसन-वेस्ले, १ ९९९), ow२।

प्रसंग

मेरी दुविधा का कारण एक ट्रेडिंग एपीआई है। वे मुझे इस विधि को भेजकर टिक डेटा की एक धारा देते हैं:

void TickPrice(TickType tickType, double value)

कहाँ पे enum TickType { BuyPrice, BuyQuantity, LastPrice, LastQuantity, ... }

मैंने इस एपीआई के चारों ओर एक आवरण बनाने की कोशिश की है क्योंकि इस एपीआई के लिए परिवर्तनों को तोड़ना जीवन का तरीका है। मैं अपने रैपर पर प्रत्येक अंतिम प्राप्त टिक प्रकार के मूल्य पर नज़र रखना चाहता था और मैंने ऐसा किया है:

Dictionary<TickType,double> LastValues

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

Enums के DON'Ts को खोजना आसान है, लेकिन DOs, इतना आसान नहीं है, और अगर लोग अपनी विशेषज्ञता, पेशेवरों और विपक्षों को साझा कर सकते हैं, तो मैं इसकी सराहना करूंगा।

दूसरा विचार

कुछ निर्णय और कार्य इन पर आधारित होते हैं TickTypeऔर मैं एनम / स्विच स्टेटमेंट को खत्म करने का तरीका नहीं सोच सकता। मैं जिस साफ-सुथरे समाधान के बारे में सोच सकता हूं, वह कारखाने का उपयोग कर रहा है और उसके आधार पर क्रियान्वयन लौटाता है TickType। तब भी मेरे पास एक स्विच स्टेटमेंट होगा जो एक इंटरफ़ेस के कार्यान्वयन को वापस करता है।

नीचे सूचीबद्ध नमूना वर्गों में से एक है, जहां मुझे संदेह है कि मैं एक एनम गलत का उपयोग कर सकता हूं:

public class ExecutionSimulator
{
  Dictionary<TickType, double> LastReceived;
  void ProcessTick(TickType tickType, double value)
  {
    //Store Last Received TickType value
    LastReceived[tickType] = value;

    //Perform Order matching only on specific TickTypes
    switch(tickType)
    {
      case BidPrice:
      case BidSize:
        MatchSellOrders();
        break;
      case AskPrice:
      case AskSize:
        MatchBuyOrders();
        break;
    }       
  }
}

26
मैं एक कोड गंध होने के बारे में सुना नहीं है। क्या आप एक संदर्भ शामिल कर सकते हैं? मुझे लगता है कि वे संभावित मूल्यों की सीमित संख्या के लिए बहुत बड़ा अर्थ रखते हैं
रिचर्ड टिंगल

25
कौन सी किताबें कह रही हैं कि एनम एक कोड गंध हैं? बेहतर पुस्तकें प्राप्त करें।
जैक्सबी

3
तो सवाल का जवाब "अधिकांश समय" होगा।
gnasher729

5
एक अच्छा, प्रकार सुरक्षित मूल्य जो अर्थ बताता है? यह गारंटी देता है कि एक शब्दकोश में उचित कुंजियों का उपयोग किया जाता है? यह एक कोड गंध कब है?

7
संदर्भ के बिना मुझे लगता है कि एक बेहतर enums as switch statements might be a code smell ...
शब्दांकन

जवाबों:


31

Enums का उपयोग उन मामलों के लिए किया जाता है जब आपने शाब्दिक रूप से हर संभव मान लिया है जो एक चर ले सकता है। कभी। सप्ताह के दिनों या महीनों के महीनों या हार्डवेयर रजिस्टर के मानों जैसे उपयोग के मामलों पर विचार करें। ऐसी चीजें जो एक साधारण मूल्य द्वारा दोनों अत्यधिक स्थिर और प्रतिनिधित्व करने योग्य हैं।

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


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

उन उत्तरों में से एक है जिसकी मुझे तलाश थी। जब मैं जिस विधि से लपेट रहा होता हूं, तब मैं उससे बच नहीं सकता, क्योंकि उद्देश्य एक जगह में इन Enum को केंद्रीकृत करना है। मुझे रैपर ऐसे बनाना है कि अगर कभी एपीआई इन एनमों को बदले, तो मुझे केवल रैपर बदलना होगा, न कि रैपर के उपभोक्ताओं को।
स्ट्रोमैन

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

@ मुझे लगता है कि आपने मेरी टिप्पणी को गलत समझा। मैंने कभी भी समान व्यवहार वाले कई प्रकारों का सुझाव नहीं दिया - जो कि पागल होगा।
जूल्स

1
यहां तक ​​कि अगर आपने "हर संभव मूल्य की गणना की है" तो इसका मतलब यह नहीं है कि प्रकार में प्रति अतिरिक्त कार्यक्षमता कभी नहीं होगी। यहां तक ​​कि महीने की तरह कुछ अन्य उपयोगी गुण हो सकते हैं, जैसे दिनों की संख्या। एक एनम का उपयोग करना तुरंत उस अवधारणा को रोकता है जिसे आप बढ़ा रहे हैं। यह मूल रूप से एक विरोधी-ओओपी तंत्र / पैटर्न है।
डेव कजिनो

17

सबसे पहले, एक कोड-गंध का मतलब यह नहीं है कि कुछ गलत है। इसका मतलब है कि कुछ गलत हो सकता है। enumबदबू आती है क्योंकि इसका अक्सर दुरुपयोग होता है, लेकिन इसका मतलब यह नहीं है कि आपको उनसे बचना होगा। बस आप अपने आप enumको बेहतर समाधानों के लिए टाइपिंग , स्टॉप और चेक करें।

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

आपके मामले में, टिक टाइप अलग-अलग व्यवहारों के अनुरूप नहीं है। वे विभिन्न प्रकार की घटनाएं या वर्तमान स्थिति के विभिन्न गुण हैं। इसलिए मुझे लगता है कि यह एक एनम के लिए आदर्श जगह है।


क्या आप कह रहे हैं कि जब एनमों में प्रविष्टियाँ (जैसे रंग) के रूप में संज्ञाएँ होती हैं, तो वे संभवतः सही ढंग से उपयोग की जाती हैं। और जब वे क्रिया (कनेक्टेड, प्रतिपादन) कर रहे हैं तो यह संकेत है कि इसका दुरुपयोग किया जा रहा है?
स्ट्रोमैन

2
@ स्ट्रोम्स, व्याकरण सॉफ्टवेयर आर्किटेक्चर के लिए एक भयानक गाइड है। अगर टिकटम एक एनम के बजाय एक इंटरफ़ेस था, तो तरीके क्या होंगे? मुझे कोई दिखाई नहीं दे रहा है, इसीलिए यह मेरे लिए एक बहुत ही अच्छा मामला है। अन्य मामले, जैसे स्थिति, रंग, बैकएंड, आदि, मैं तरीकों के साथ आ सकता हूं, और यही कारण है कि वे इंटरफेस हैं।
विंस्टन एवर्ट

@WinstonEwert और संपादित के साथ, ऐसा लगता है कि वे कर रहे हैं अलग अलग व्यवहार।

ओह। तो अगर मैं एक एनम के लिए एक विधि के बारे में सोच सकता हूं, तो यह एक इंटरफ़ेस के लिए एक उम्मीदवार हो सकता है? यह एक बहुत अच्छी बात हो सकती है।
स्ट्रोमैन

1
@stromms, क्या आप स्विच के अधिक उदाहरण साझा कर सकते हैं, इसलिए मैं एक बेहतर विचार प्राप्त कर सकता हूं कि आप टिकटाइप का उपयोग कैसे करते हैं?
विंस्टन इर्वर्ट

8

डेटा संचारित करते समय कोई कोड गंध नहीं होती है

IMHO, जब enumsयह इंगित करने के लिए डेटा का उपयोग कर प्रेषित करता है कि किसी क्षेत्र में मानों के प्रतिबंधित (शायद ही कभी बदलते) सेट से मान हो सकता है तो अच्छा है।

मैं इसे मनमाना stringsया प्रसारित करने के लिए बेहतर मानता हूं ints। स्पेलिंग और कैपिटलाइज़ेशन में भिन्नता के कारण स्ट्रिंग्स की समस्या हो सकती है। Ints रेंज मूल्यों के प्रसारण के लिए अनुमति देते हैं और थोड़ा अर्थ विज्ञान (है जैसे मैं प्राप्त 3अपने व्यापार सेवा से, इसका क्या मतलब है? LastPrice? LastQuantity? कुछ और?

वस्तुओं को स्थानांतरित करना और श्रेणी पदानुक्रमों का उपयोग करना हमेशा संभव नहीं होता है; उदाहरण के लिए, यह प्राप्त करने की अनुमति नहीं देता है कि किस वर्ग को भेजा गया है।

मेरी परियोजना में सेवा संचालन के प्रभावों के लिए एक वर्ग पदानुक्रम का उपयोग करती है, कक्षा पदानुक्रम DataContractसे ऑब्जेक्ट के माध्यम से संचारित करने से पहले एक union-जैसे ऑब्जेक्ट में कॉपी किया जाता है जिसमें enumटाइप इंगित करने के लिए एक होता है । ग्राहक DataContractएक enumमान प्राप्त करता है और switchसही प्रकार के ऑब्जेक्ट का निर्माण करने के लिए मूल्य का उपयोग करके एक पदानुक्रम में एक वर्ग का ऑब्जेक्ट बनाता है ।

एक अन्य कारण है कि कोई वर्ग की वस्तुओं को संचारित नहीं करना चाहेगा कि LastPriceग्राहक की तुलना में इस सेवा को प्रेषित वस्तु (जैसे कि ) के लिए पूरी तरह से अलग व्यवहार की आवश्यकता हो सकती है । उस मामले में कक्षा और उसके तरीकों को भेजना अवांछित है।

स्विच स्टेटमेंट खराब हैं?

IMHO, एक एकल- switch स्थापन जो एक के आधार पर विभिन्न कंस्ट्रक्टरों को कॉल करता enumहै, कोड गंध नहीं है। यह जरूरी नहीं कि अन्य तरीकों से बेहतर या बदतर हो, जैसे कि टाइपनेम पर प्रतिबिंब आधार; यह वास्तविक स्थिति पर निर्भर करता है।

enumसभी जगह पर स्विच होना एक कोड गंध है, विकल्प प्रदान करता है जो अक्सर बेहतर होते हैं:

  • एक पदानुक्रम के विभिन्न वर्गों के ऑब्जेक्ट का उपयोग करें जिनके पास ओवरराइड विधियां हैं; यानी बहुरूपता।
  • आगंतुक पैटर्न का उपयोग करें जब पदानुक्रम में कक्षाएं शायद ही कभी बदलती हैं और जब (कई) संचालन को पदानुक्रम में कक्षाओं के लिए शिथिल युग्मित किया जाना चाहिए।

दरअसल, टिकटाइप को एक के रूप में तार के माध्यम से प्रेषित किया जा रहा है int। मेरा रैपर वह है जो उपयोग करता है TickTypeजो प्राप्त से डाली जाती है int। कई इवेंट्स वाइल्ड डिफरेंट सिग्नेचर वाले इस टिकटेप का इस्तेमाल करते हैं जो विभिन्न रिक्वेस्ट के जवाब होते हैं। क्या intविभिन्न कार्यों के लिए लगातार उपयोग करना आम बात है ? उदाहरण के TickPrice(int type, double value)लिए 1,3, और 6 का उपयोग करता है, typeजबकि TickSize(int type, double value2,4, और 5 का उपयोग करता है? क्या यह भी दो घटनाओं में अलग करने के लिए समझ में आता है?
स्ट्रोमैन

2

क्या होगा अगर आप अधिक जटिल प्रकार के साथ गए:

    abstract class TickType
    {
      public abstract string Name {get;}
      public abstract double TickValue {get;}
    }

    class BuyPrice : TickType
    {
      public override string Name { get { return "Buy Price"; } }
      public override double TickValue { get { return 2.35d; } }
    }

    class BuyQuantity : TickType
    {
      public override string Name { get { return "Buy Quantity"; } }
      public override double TickValue { get { return 4.55d; } }
    }
//etcetera

तब आप अपने प्रकारों को प्रतिबिंब से लोड कर सकते हैं या इसे स्वयं निर्मित कर सकते हैं, लेकिन यहां जा रही प्राथमिक बात यह है कि आप SOLID के ओपन क्लोज प्रिंसिपल को पकड़ रहे हैं


1

टी एल; डॉ

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

मेरे विरासत कोड के रिफ्लेक्टर टन के रूप में मुझे माफ करना। / आह; ^ डी


लेकिन क्यों?

बहुत अच्छी समीक्षा क्यों लॉस एंजिल्स से यहाँ मामला है :

// calculate the service fee
public double CalculateServiceFeeUsingEnum(Account acct)
{
    double totalFee = 0;
    foreach (var service in acct.ServiceEnums) { 

        switch (service)
        {
            case ServiceTypeEnum.ServiceA:
                totalFee += acct.NumOfUsers * 5;
                break;
            case ServiceTypeEnum.ServiceB:
                totalFee += 10;
                break;
        }
    }
    return totalFee;
} 

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

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

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

public interface ICalculateServiceFee
{
    double CalculateServiceFee(Account acct);
}

...

अब हम इंटरफ़ेस के हमारे ठोस कार्यान्वयन को बना सकते हैं और उन्हें [खाते] संलग्न कर सकते हैं।

public class Account{
    public int NumOfUsers{get;set;}
    public ICalculateServiceFee[] Services { get; set; }
} 

public class ServiceA : ICalculateServiceFee
{
    double feePerUser = 5; 

    public double CalculateServiceFee(Account acct)
    {
        return acct.NumOfUsers * feePerUser;
    }
} 

public class ServiceB : ICalculateServiceFee
{
    double serviceFee = 10;
    public double CalculateServiceFee(Account acct)
    {
        return serviceFee;
    }
} 

एक और कार्यान्वयन मामला ...

लब्बोलुआब यह है कि यदि आपके पास व्यवहार है जो एनम मूल्यों पर निर्भर करता है, तो इसके बजाय एक समान इंटरफ़ेस या माता-पिता वर्ग के अलग-अलग कार्यान्वयन क्यों नहीं हैं जो यह सुनिश्चित करता है कि मूल्य मौजूद है? मेरे मामले में, मैं REST स्थिति कोड के आधार पर विभिन्न त्रुटि संदेशों को देख रहा हूं। के बजाय...

private static string _getErrorCKey(int statusCode)
{
    string ret;

    switch (statusCode)
    {
        case StatusCodes.Status403Forbidden:
            ret = "BRANCH_UNAUTHORIZED";
            break;

        case StatusCodes.Status422UnprocessableEntity:
            ret = "BRANCH_NOT_FOUND";
            break;

        default:
            ret = "BRANCH_GENERIC_ERROR";
            break;
    }

    return ret;
}

... शायद मुझे कक्षाओं में स्थिति कोड लपेटने चाहिए।

public interface IAmStatusResult
{
    int StatusResult { get; }    // Pretend an int's okay for now.
    string ErrorKey { get; }
}

फिर हर बार मुझे IAmStatusResult के एक नए प्रकार की आवश्यकता होती है, मैं इसे कोड करता हूं ...

public class UnauthorizedBranchStatusResult : IAmStatusResult
{
    public int StatusResult => 403;
    public string ErrorKey => "BRANCH_UNAUTHORIZED";
}

... और अब मैं यह सुनिश्चित कर सकता हूं कि पहले वाला कोड महसूस करता है कि यह एक IAmStatusResultदायरे में है और entity.ErrorKeyअधिक जटिल, डेडेंड के बजाय इसका संदर्भ देता है _getErrorCode(403)

और, इससे भी महत्वपूर्ण बात यह है कि हर बार जब मैं एक नए प्रकार का रिटर्न मूल्य जोड़ता हूं, तो इसे संभालने के लिए किसी अन्य कोड को जोड़ने की आवश्यकता नहीं है । Whaddya जानते हैं, enumऔर switchसंभावना कोड बदबू आ रही थी।

फायदा।


कैसे के बारे में जहां, उदाहरण के लिए, मेरे पास बहुरूपी कक्षाएं हैं और मैं कॉन्फ़िगर करना चाहता हूं कि मैं एक कमांड लाइन पैरामीटर के माध्यम से किसका उपयोग कर रहा हूं? कमांड लाइन -> enum -> स्विच / मैप -> कार्यान्वयन एक बहुत वैध उपयोग मामला लगता है। मुझे लगता है कि महत्वपूर्ण बात यह है कि एनुम का उपयोग ऑर्केस्ट्रेशन के लिए किया जाता है, न कि सशर्त तर्क के कार्यान्वयन के रूप में।
एंट पी।

@AntP क्यों नहीं, इस मामले में, StatusResultमूल्य का उपयोग करें ? आप तर्क दे सकते हैं कि एनम उस उपयोग के मामले में एक मानव-यादगार शॉर्टकट के रूप में उपयोगी है, लेकिन मैं शायद अभी भी एक कोड गंध को कॉल करूंगा, क्योंकि अच्छे विकल्प हैं जिन्हें बंद संग्रह की आवश्यकता नहीं है।
रफिन

क्या विकल्प? एक स्ट्रिंग? यह "मनमुटावों को बहुरूपता से प्रतिस्थापित किया जाना चाहिए" के दृष्टिकोण से एक मनमाना भेद है - या तो दृष्टिकोण एक मूल्य को एक प्रकार की मैपिंग करने की आवश्यकता है; इस मामले में एनम से बचने से कुछ भी हासिल नहीं होता है।
एंट पी।

@AntP मुझे यकीन नहीं है कि जहां तार यहाँ पार कर रहे हैं। यदि आप किसी इंटरफ़ेस पर किसी प्रॉपर्टी को संदर्भित करते हैं, तो आप उस इंटरफ़ेस को इधर-उधर फेंक सकते हैं, जिसकी आवश्यकता है और जब आप उस इंटरफ़ेस का नया (या मौजूदा) कार्यान्वयन लागू करते हैं तो उस कोड को कभी अपडेट न करें । यदि आपके पास कोई है enum, तो आपको हर बार किसी मूल्य को जोड़ने या हटाने के लिए कोड को अपडेट करना होगा, और संभावित रूप से बहुत सारी जगहों पर निर्भर करता है कि आपने अपने कोड को कैसे संरचित किया है। एक Enum के बजाय बहुरूपता का उपयोग करना इस तरह से होता है जैसे आपका कोड Boyce-Codd Normal Form में सुनिश्चित करता है
रफिन

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

0

एनुम का उपयोग कोड गंध है या नहीं यह संदर्भ पर निर्भर करता है। मुझे लगता है कि यदि आप अभिव्यक्ति समस्या पर विचार करते हैं तो आप अपने प्रश्न का उत्तर देने के लिए कुछ विचार प्राप्त कर सकते हैं । तो, आपके पास विभिन्न प्रकारों का एक संग्रह है और उन पर संचालन का एक संग्रह है, और आपको अपना कोड व्यवस्थित करने की आवश्यकता है। दो सरल विकल्प हैं:

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

कौन सा उपाय बेहतर है?

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

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

यदि आप उपरोक्त दोनों विकल्पों में से किसी में भी अपनी समस्या को वर्गीकृत नहीं कर सकते हैं, तो आप अधिक परिष्कृत समाधान देख सकते हैं (उदाहरण के लिए फिर से विकिपीडिया पृष्ठ पर थोड़ी चर्चा के लिए और आगे पढ़ने के लिए कुछ संदर्भ)।

तो, आपको यह समझने की कोशिश करनी चाहिए कि आपका आवेदन किस दिशा में विकसित हो सकता है और फिर एक उपयुक्त समाधान चुनें।

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

नीचे पंक्ति: enums जरूरी एक कोड गंध नहीं है।


ध्यान दें कि प्रश्न C #, एक OOP भाषा के संदर्भ में पूछा गया था। यह भी, कि ऑपरेशन या प्रकार के आधार पर समूहीकरण एक ही सिक्के के दो पहलू दिलचस्प हैं, लेकिन मैं एक को दूसरे के मुकाबले "बेहतर" होने के रूप में नहीं देखता हूं जैसा कि आप वर्णन करते हैं। कोड की परिणामी राशि समान है, और ओओपी भाषाएँ पहले से ही प्रकार के आयोजन की सुविधा प्रदान करती हैं। यह काफी अधिक सहज (पढ़ने में आसान) कोड भी पैदा करता है।
डेव कजिनो

@DaveCousineau: "मैं एक को दूसरे से" बेहतर "होने के रूप में नहीं देखता जितना आप वर्णन करते हैं": मैंने यह नहीं कहा कि एक हमेशा दूसरे से बेहतर होता है। मैंने कहा कि संदर्भ के आधार पर एक दूसरे से बेहतर है। उदाहरण के संचालन और अधिक या कम तय कर रहे हैं और आप नए प्रकार जोड़ने की योजना बनाते है, तो (उदाहरण के लिए एक जीयूआई तय साथ paint(), show(), close() resize()संचालन और कस्टम विगेट्स) तो वस्तु उन्मुख दृष्टिकोण अर्थ में बेहतर है कि यह भी प्रभावित किए बिना एक नए प्रकार जोड़ने की अनुमति देता बहुत मौजूदा कोड (आप मूल रूप से एक नया वर्ग लागू करते हैं, जो एक स्थानीय परिवर्तन है)।
जियोर्जियो

मैं या तो "बेहतर" होने के रूप में नहीं देखता हूं जैसा कि आपने वर्णित किया है कि मैं यह नहीं देखता कि यह स्थिति पर निर्भर करता है। आपको जो कोड लिखना है वह दोनों परिदृश्यों में बिल्कुल समान है। अंतर केवल इतना है कि एक तरीका ओओपी (एनम) का विरोधी है और एक नहीं है।
डेव कजिनो

@DaveCousineau: आपके द्वारा लिखे जाने वाले कोड की मात्रा संभवतः एक ही है, स्थानीयता (स्रोत कोड में वे स्थान जिन्हें आपको बदलना और पुनर्मूल्यांकन करना है) में भारी परिवर्तन होता है। उदाहरण के लिए OOP में, यदि आप एक इंटरफ़ेस में एक नई विधि जोड़ते हैं, तो आपको उस इंटरफ़ेस को लागू करने वाले प्रत्येक वर्ग के लिए एक कार्यान्वयन जोड़ना होगा।
जियोर्जियो

क्या यह केवल चौड़ाई बनाम गहराई की बात नहीं है? 10 फ़ाइलों में 1 विधि या 1 फ़ाइल में 10 विधियाँ जोड़ना।
डेव कज़िनो
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.