सत्यापन जांच के साथ नियंत्रण प्रवाह के लिए शैली


27

मैं खुद को इस तरह का एक बहुत कोड लिख रहा हूँ:

int myFunction(Person* person) {
  int personIsValid = !(person==NULL);
  if (personIsValid) {
     // do some stuff; might be lengthy
     int myresult = whatever;
     return myResult;
  }
  else {
    return -1;
  }
}

यह बहुत गन्दा हो सकता है, खासकर अगर कई जाँच शामिल हैं। ऐसे मामलों में, मैंने वैकल्पिक शैलियों के साथ प्रयोग किया है, जैसे कि यह एक:

int netWorth(Person* person) {
  if (Person==NULL) {
    return -1;
  }
  if (!(person->isAlive))  {
    return -1;
  }
  int assets = person->assets;
  if (assets==-1)  {
    return -1;
  }
  int liabilities = person->liabilities;
  if (liabilities==-1) {
    return -1;
  }
  return assets - liabilities;
}

मुझे यहाँ की शैलीगत पसंद के बारे में टिप्पणियों में दिलचस्पी है। [व्यक्तिगत बयानों के विवरण के बारे में बहुत ज्यादा चिंता न करें; यह समग्र नियंत्रण प्रवाह है जो मुझे रुचता है।]


8
मुझे यह इंगित करने की अनुमति दें कि आपके उदाहरण में आपके पास काफी गंभीर विनिर्देश त्रुटि है। यदि, उदाहरण के लिए, संपत्ति == 42 और देयताएं == 43, तो आप व्यक्ति को कोई भी घोषित नहीं करेंगे।
जॉन आर। स्ट्रॉहैम

एक अपवाद को फेंकने और ग्राहक कोड को मान्यताओं को प्रबंधित करने देना बेहतर नहीं होगा?
ट्यूलेंस कोर्डोवा

@ TulainsCórdova अपवाद उपलब्ध नहीं हो सकता है, या शायद अमान्य डेटा असाधारण नहीं है कि स्टैक ट्रेस आदि के निर्माण का प्रदर्शन प्रभाव स्वीकार्य है।
हल्क

जवाबों:


27

इस तरह के मुद्दों के लिए मार्टिन फाउलर ने विशिष्टता पैटर्न प्रस्तावित किया :

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

ऊपर थोड़ा उच्च-भौंकता है (कम से कम मेरे लिए), लेकिन जब मैंने इसे अपने कोड में आज़माया तो यह काफी सुचारू रूप से चला गया और लागू करने और पढ़ने में आसान हो गया।

जिस तरह से मैं इसे देखता हूं, मुख्य विचार "निकालने" कोड है जो चेक को समर्पित पद्धति (वस्तुओं) / वस्तुओं में करता है।

अपने netWorthउदाहरण के साथ , यह इस प्रकार दिख सकता है:

int netWorth(Person* person) {
  if (isSatisfiedBySpec(person)) {
    return person->assets - person->liabilities;
  }
  log("person doesn't satisfy spec");
  return -1;
}

#define BOOLEAN int // assuming C here
BOOLEAN isSatisfiedBySpec(Person* person) {
  return Person != NULL
      && person->isAlive
      && person->assets != -1
      && person->liabilities != -1;
}

आपका मामला इतना सरल प्रतीत होता है कि सभी जाँचें एक ही विधि में एक सादे सूची में फिट होने के लिए ठीक लगती हैं। मुझे अक्सर इसे बेहतर पढ़ने के लिए अधिक विधियों में विभाजित करना पड़ता है।

मैं आमतौर पर समर्पित ऑब्जेक्ट में "स्पेक" संबंधित तरीकों को समूह / एक्सट्रैक्ट करता हूं, हालांकि आपका मामला इसके बिना ठीक दिखता है।

  // ...
  Specification s, *spec = initialize(s, person);
  if (spec->isSatisfied()) {
    return person->assets - person->liabilities;
  }
  log("person doesn't satisfy spec");
  return -1;
  // ...

स्टैक ओवरफ्लो में यह प्रश्न ऊपर उल्लेखित एक के अलावा कुछ लिंक की सिफारिश करता है: विनिर्देश पैटर्न उदाहरण । विशेष रूप से, उत्तर डिमकास्ट्स को एक उदाहरण के पूर्वाभ्यास के लिए 'लर्निंग द स्पेसिफिकेशन पैटर्न' का सुझाव देते हैं और एरिक इवांस और मार्टिन फाउलर द्वारा लिखित "स्पेसिफिकेशन" पेपर का उल्लेख करते हैं ।


8

मुझे अपने स्वयं के फ़ंक्शन के लिए सत्यापन को स्थानांतरित करना आसान लगता है, यह अन्य कार्यों के इरादे को साफ रखने में मदद करता है, इसलिए आपका उदाहरण इस तरह होगा।

int netWorth(Person* person) { 
    if(validPerson(person)) {
        int assets = person->assets;
        int liabilities = person->liabilities;
        return assets - liabilities;
    }
    else {
        return -1;
    }
}

bool validPerson(Person* person) { 
    if(person!=NULL && person->isAlive
      && person->assets !=-1 && person->liabilities != -1)
        return true;
    else
        return false;
}

2
क्यों आप की क्या ज़रूरत है ifमें validPerson? person!=NULL && person->isAlive && person->assets !=-1 && person->liabilities != -1इसके बजाय बस लौटें ।
डेविड हैमेन

3

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

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

अपने उदाहरण के इस परावर्तन पर विचार करें:

int myFunction(Person* person) {
  int personIsValid = !(person==NULL);
  if (personIsValid) {
     return myFunctionWork(person)
  }
  else {
    return -1;
  }
}

int myFunction(Person *person) {
  assert( person != NULL);  
  // Do work and return
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.