PHP में अनुबंध प्रोग्रामिंग

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

परिचय


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

कोड अनुबंध


चूंकि कॉन्ट्रैक्ट प्रोग्रामिंग को ऑब्जेक्ट-ओरिएंटेड भाषा के लिए विकसित किया गया था, इसलिए यह अनुमान लगाना मुश्किल नहीं है कि कॉन्ट्रैक्ट्स के लिए मुख्य काम करने वाले तत्व कक्षाएं, तरीके और गुण हैं।

पूर्व शर्त

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

निराधार न होने के लिए, आइए एक उदाहरण देखें:

class BankAccount { protected $balance = 0.0; /** * Deposits fixed amount of money to the account * * @param float $amount */ public function deposit($amount) { if ($amount <= 0 || !is_numeric($amount)) { throw new \InvalidArgumentException("Invalid amount of money"); } $this->balance += $amount; } } 


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

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

 class BankAccount { protected $balance = 0.0; /** * Deposits fixed amount of money to the account * * @param float $amount */ public function deposit($amount) { assert('$amount>0 && is_numeric($amount); /* Invalid amount of money /*'); $this->balance += $amount; } } 

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

Postconditions

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

 class BankAccount { protected $balance = 0.0; /** * Deposits fixed amount of money to the account * * @param float $amount */ public function deposit($amount) { $__old = clone $this; assert('$amount>0 && is_numeric($amount); /* Invalid amount of money /*'); $this->balance += $amount; assert('$this->balance == $__old->balance+$amount; /* Contract violation /*'); } } 


मूल्य वापस करने वाले तरीकों के लिए पोस्टकंडिशन का वर्णन करने में एक और निराशा हमें इंतजार कर रही है:

 class BankAccount { protected $balance = 0.0; /** * Returns current balance */ public function getBalance() { return $this->balance; } } 

यहां अनुबंध की स्थिति का वर्णन कैसे करें कि विधि को वर्तमान संतुलन वापस करना चाहिए? चूंकि विधि के शरीर के बाद की स्थिति संतुष्ट है, हम अपने चेक कामों से पहले return पर ठोकर खाएंगे। इसलिए, आपको $__result चर में परिणाम को बचाने के लिए विधि कोड बदलना होगा और फिर $this->balance साथ तुलना करनी $this->balance :

 class BankAccount { protected $balance = 0.0; /** * Returns current balance */ public function getBalance() { $__result = $this->balance; assert('$__result == $this->balance; /* Contract violation /*'); return $__result; } } 


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

अपरिवर्तनशीलताओं

यह हमारे लिए एक और महत्वपूर्ण प्रकार के अनुबंध पर विचार करने के लिए बना हुआ है: आक्रमणकारियों Invariants विशेष परिस्थितियां हैं जो किसी वस्तु की अभिन्न स्थिति का वर्णन करती हैं। आक्रमणकारियों की एक महत्वपूर्ण विशेषता यह है कि उन्हें हमेशा क्लास में किसी भी सार्वजनिक विधि को कॉल करने और कंस्ट्रक्टर को कॉल करने के बाद चेक किया जाता है। चूंकि अनुबंध वस्तु की स्थिति को निर्धारित करता है, और सार्वजनिक तरीकों से राज्य को बाहर से बदलने का एकमात्र तरीका है, हमें ऑब्जेक्ट का पूरा विनिर्देश मिलता है। हमारे उदाहरण के लिए, एक शर्त एक अच्छी चाल चल सकती है: खाता शेष कभी भी शून्य से कम नहीं होना चाहिए। हालांकि, PHP में आक्रमणकारियों के साथ स्थिति पोस्टकॉन्डिशन से भी बदतर है: किसी वर्ग के सभी सार्वजनिक तरीकों से आसानी से चेक जोड़ने का कोई तरीका नहीं है ताकि किसी भी सार्वजनिक विधि को कॉल करने के बाद, आप अपरिवर्तनीय में आवश्यक स्थिति की जांच कर सकें। $__old वस्तु की पिछली स्थिति और $__old परिणाम की वापसी परिणाम तक पहुंचने का कोई तरीका नहीं है। आक्रमणकारियों के बिना, कोई अनुबंध नहीं होता है, इसलिए लंबे समय तक इस कार्यक्षमता को लागू करने के लिए कोई उपकरण और तकनीक नहीं थी।

नई सुविधाएँ


PHP में अनुबंध प्रोग्रामिंग के लिए एक प्रायोगिक DbC फ्रेमवर्क PhpDeal से मिलो।
गो के बाद ! फ्रेमवर्क विकसित किया गया था PHP में पहलू-उन्मुख प्रोग्रामिंग के लिए AOP , मेरा दिमाग स्वचालित पैरामीटर सत्यापन के बारे में घूम रहा था, स्थितियों की जाँच कर रहा था और बहुत कुछ। PHP.Internals पर चर्चा अनुबंध प्रोग्रामिंग के लिए एक परियोजना बनाने के लिए एक ट्रिगर के रूप में सेवा की। हैरानी की बात है कि एओपी की मदद से, समस्या को केवल कुछ चरणों में हल किया गया था: एक पहलू का वर्णन करना आवश्यक था जो अनुबंध एनोटेशन के साथ चिह्नित विधियों के निष्पादन को बाधित करेगा और विधि को बुलाए जाने से पहले या बाद में आवश्यक जांच करेगा।

आइए एक नज़र डालते हैं कि इस ढांचे के साथ अनुबंधों का उपयोग कैसे किया जा सकता है:

 use PhpDeal\Annotation as Contract; /** * Simple trade account class * @Contract\Invariant("$this->balance > 0") */ class Account implements AccountContract { /** * Current balance * * @var float */ protected $balance = 0.0; /** * Deposits fixed amount of money to the account * * @param float $amount * * @Contract\Verify("$amount>0 && is_numeric($amount)") * @Contract\Ensure("$this->balance == $__old->balance+$amount") */ public function deposit($amount) { $this->balance += $amount; } /** * Returns current balance * * @Contract\Ensure("$__result == $this->balance") * @return float */ public function getBalance() { return $this->balance; } } 


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

Preconditions Verify एनोटेशन का उपयोग करके निर्दिष्ट किए जाते हैं और उन जाँचों को निर्धारित करते हैं जो उस समय की जाती हैं जब विधि को कॉल किया जाता है, लेकिन इससे पहले कि बॉडी स्वयं निष्पादित हो। Preconditions वर्ग विधि के दायरे में काम करते हैं, इसलिए उनकी निजी संपत्तियों सहित सभी संपत्तियों तक पहुंच होती है, और विधि मापदंडों तक भी पहुंच होती है।

पोस्टकंडिशन को एनोटेशन द्वारा परिभाषित किया जाता है, जिसे अनुबंध प्रोग्रामिंग के संदर्भ में मानक रूप से Ensure किया जाता है। कोड में विधि के समान ही स्कोप है; इसके अलावा, विधि निष्पादन से पहले ऑब्जेक्ट की स्थिति के साथ $__old और चर $__result जिसमें मान इस विधि से लौटाया गया था, उपलब्ध हैं।

एओपी के उपयोग के लिए धन्यवाद, यहां तक ​​कि अपरिवर्तनीयों को लागू करना संभव हो गया है - उन्हें क्लास डॉक में इनवायरेंट एनोटेशन के रूप में सुरुचिपूर्ण ढंग से वर्णित किया गया है और पोस्टकंडिशन के समान व्यवहार करते हैं, लेकिन सभी तरीकों के लिए।

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

 use PhpDeal\Annotation as Contract; /** * Simple trade account contract */ interface AccountContract { /** * Deposits fixed amount of money to the account * * @param float $amount * * @Contract\Verify("$amount>0 && is_numeric($amount)") * @Contract\Ensure("$this->balance == $__old->balance+$amount") */ public function deposit($amount); /** * Returns current balance * * @Contract\Ensure("$__result == $this->balance") * * @return float */ public function getBalance(); } 


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

निष्कर्ष


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

आपका ध्यान के लिए धन्यवाद!

संबंधित लिंक:
  1. विकिपीडिया - अनुबंध प्रोग्रामिंग
  2. PHP में अनुबंध प्रोग्रामिंग के लिए PhDDeal रूपरेखा
  3. फ्रेमवर्क गो! PHP में पहलू-उन्मुख प्रोग्रामिंग के लिए AOP

Source: https://habr.com/ru/post/In214371/


All Articles