यह "एक्सट्यूट अराउंड" मुहावरा (या समान) क्या है जिसके बारे में मैं सुन रहा हूँ? मैं इसका उपयोग क्यों कर सकता हूं, और मैं इसका उपयोग क्यों नहीं करना चाहता?
यह "एक्सट्यूट अराउंड" मुहावरा (या समान) क्या है जिसके बारे में मैं सुन रहा हूँ? मैं इसका उपयोग क्यों कर सकता हूं, और मैं इसका उपयोग क्यों नहीं करना चाहता?
जवाबों:
मूल रूप से यह वह पैटर्न है जहां आप ऐसी चीजों को करने के लिए एक विधि लिखते हैं, जिनकी हमेशा आवश्यकता होती है, जैसे संसाधन आवंटन और क्लीन-अप, और "हम संसाधन के साथ क्या करना चाहते हैं" में कॉलर पास करें। उदाहरण के लिए:
public interface InputStreamAction
{
void useStream(InputStream stream) throws IOException;
}
// Somewhere else
public void executeWithFile(String filename, InputStreamAction action)
throws IOException
{
InputStream stream = new FileInputStream(filename);
try {
action.useStream(stream);
} finally {
stream.close();
}
}
// Calling it
executeWithFile("filename.txt", new InputStreamAction()
{
public void useStream(InputStream stream) throws IOException
{
// Code to use the stream goes here
}
});
// Calling it with Java 8 Lambda Expression:
executeWithFile("filename.txt", s -> System.out.println(s.read()));
// Or with Java 8 Method reference:
executeWithFile("filename.txt", ClassName::methodName);
कॉलिंग कोड को खुले / साफ-सुथरे पक्ष के बारे में चिंता करने की आवश्यकता नहीं है - इसका ध्यान रखा जाएगा executeWithFile
।
जावा में यह स्पष्ट रूप से दर्दनाक था क्योंकि क्लोज़र बहुत खराब थे, जावा 8 लैम्ब्डा अभिव्यक्तियों के साथ शुरू किया जा सकता है जैसे कि कई अन्य भाषाओं (जैसे सी # लैम्ब्डा एक्सप्रेशंस, या ग्रूवी) में लागू किया जा सकता है, और यह विशेष मामला जावा के दूसरे दिन से संभाला जाता है try-with-resources
औरAutoClosable
डे धाराओं के है।
यद्यपि "आवंटित और साफ-सुथरा" विशिष्ट उदाहरण दिया गया है, इसके बहुत सारे अन्य संभावित उदाहरण हैं - लेनदेन की हैंडलिंग, लॉगिंग, कुछ कोडों को अधिक विशेषाधिकारों के साथ क्रियान्वित करना आदि। यह मूल रूप से टेम्पलेट विधि पैटर्न की तरह ही है, लेकिन विरासत के बिना।
मुहावरे के आस-पास का प्रयोग तब किया जाता है जब आप खुद को ऐसा कुछ करते हुए पाते हैं:
//... chunk of init/preparation code ...
task A
//... chunk of cleanup/finishing code ...
//... chunk of identical init/preparation code ...
task B
//... chunk of identical cleanup/finishing code ...
//... chunk of identical init/preparation code ...
task C
//... chunk of identical cleanup/finishing code ...
//... and so on.
अपने अनावश्यक कार्यों को हमेशा "निष्पादित" करने वाले इस निरर्थक कोड को दोहराने से बचने के लिए, आप एक ऐसा वर्ग बनाएंगे, जो स्वचालित रूप से इसकी देखभाल करता है:
//pseudo-code:
class DoTask()
{
do(task T)
{
// .. chunk of prep code
// execute task T
// .. chunk of cleanup code
}
};
DoTask.do(task A)
DoTask.do(task B)
DoTask.do(task C)
यह मुहावरा सभी जटिल अतिरेक कोड को एक स्थान पर ले जाता है, और आपके मुख्य कार्यक्रम को और अधिक पठनीय (और बनाए रखने योग्य) छोड़ देता है!
पर एक नजर डालें इस पोस्ट एक सी # उदाहरण के लिए, और इस लेख एक सी ++ उदाहरण के लिए।
एक आस-पास की पद्धति वह है जहाँ आप एक विधि के लिए मनमाना कोड पास करते हैं, जो सेटअप और / या फाड़ कोड प्रदर्शन कर सकता है और बीच में आपके कोड को निष्पादित कर सकता है।
जावा वह भाषा नहीं है जिसे मैं इसमें करना चाहूंगा। यह तर्क के रूप में एक क्लोजर (या लैम्ब्डा अभिव्यक्ति) को पारित करने के लिए अधिक स्टाइलिश है। हालांकि ऑब्जेक्ट्स निश्चित रूप से क्लोजर के बराबर हैं ।
यह मुझे लगता है कि विधि के आस-पास की विधि नियंत्रण के उलट (डिपेंडेंसी इंजेक्शन) की तरह है कि आप हर बार, जब आप विधि कहते हैं, तो आप तदर्थ को अलग-अलग कर सकते हैं।
लेकिन इसे कंट्रोल कपलिंग के एक उदाहरण के रूप में भी समझा जा सकता है (एक विधि को इसके तर्क से क्या करना है, शाब्दिक रूप से इस मामले में)।
मुझे लगता है कि आपके पास यहां जावा टैग है, इसलिए मैं जावा का उपयोग एक उदाहरण के रूप में करूंगा, भले ही पैटर्न प्लेटफॉर्म-विशिष्ट न हो।
विचार यह है कि कभी-कभी आपके पास कोड होता है जिसमें कोड चलाने से पहले और कोड चलाने के बाद हमेशा एक ही बॉयलरप्लेट शामिल होता है। एक अच्छा उदाहरण JDBC है। वास्तविक क्वेरी चलाने और परिणाम सेट को संसाधित करने से पहले आप हमेशा एक कनेक्शन लेते हैं और एक स्टेटमेंट (या तैयार स्टेटमेंट) बनाते हैं, और फिर आप हमेशा एक ही बॉयलरप्लेट क्लीनअप करते हैं - स्टेटमेंट और कनेक्शन को बंद करना।
निष्पादित-अराउंड के साथ विचार यह है कि यदि आप बॉयलरप्लेट कोड को निकाल सकते हैं तो बेहतर है। यह आपको कुछ टाइपिंग बचाता है, लेकिन कारण गहरा है। यह यहाँ पर दोहराना नहीं है (DRY) सिद्धांत - आप कोड को एक स्थान पर अलग कर देते हैं, इसलिए यदि कोई बग है या आपको इसे बदलने की आवश्यकता है, या आप इसे समझना चाहते हैं, तो यह एक ही स्थान पर है।
इस तरह के फैक्टरिंग-आउट के साथ एक छोटी सी बात यह है कि यद्यपि आपके पास ऐसे संदर्भ हैं जो "पहले" और "बाद" दोनों भागों को देखने की आवश्यकता है। JDBC उदाहरण में यह कनेक्शन और (तैयार) कथन शामिल होगा। इसलिए यह सुनिश्चित करने के लिए कि आप बॉयलरप्लेट कोड के साथ अपना लक्ष्य कोड अनिवार्य रूप से "लपेटें"।
आप जावा में कुछ सामान्य मामलों से परिचित हो सकते हैं। एक सर्वलेट फिल्टर है। एक और सलाह के आसपास AOP है। एक तीसरा स्प्रिंग में विभिन्न xxxTemplate कक्षाएं हैं। प्रत्येक मामले में आपके पास कुछ आवरण वस्तु होती है जिसमें आपका "दिलचस्प" कोड (JDBC क्वेरी और परिणाम सेट प्रसंस्करण कहते हैं) इंजेक्ट किया जाता है। रैपर ऑब्जेक्ट "भाग" से पहले करता है, दिलचस्प कोड को आमंत्रित करता है और फिर "बाद" भाग को करता है।
कोड सैंडविच भी देखें , जो कई प्रोग्रामिंग भाषाओं में इस निर्माण का सर्वेक्षण करता है और कुछ दिलचस्प शोध विचारों की पेशकश करता है। विशिष्ट प्रश्न के बारे में कि कोई इसका उपयोग क्यों कर सकता है, उपरोक्त कागज कुछ ठोस उदाहरण प्रस्तुत करता है:
जब भी कोई कार्यक्रम साझा संसाधनों में हेरफेर करता है तो ऐसी स्थितियां उत्पन्न होती हैं। ताले, सॉकेट्स, फ़ाइलों या डेटाबेस कनेक्शन के लिए एपीआई को स्पष्ट रूप से एक संसाधन को बंद करने या जारी करने के लिए एक कार्यक्रम की आवश्यकता हो सकती है जिसे उसने पहले अधिग्रहित किया था। कचरा संग्रह के बिना एक भाषा में, प्रोग्रामर इसके उपयोग से पहले मेमोरी को आवंटित करने और इसके उपयोग के बाद इसे जारी करने के लिए जिम्मेदार है। सामान्य तौर पर, विभिन्न प्रकार के प्रोग्रामिंग कार्य एक कार्यक्रम को बदलने के लिए बुलाते हैं, उस परिवर्तन के संदर्भ में काम करते हैं, और फिर परिवर्तन को पूर्ववत करते हैं। हम ऐसी स्थितियों को कोड सैंडविच कहते हैं।
और बादमें:
कोड सैंडविच कई प्रोग्रामिंग स्थितियों में दिखाई देते हैं। कई सामान्य उदाहरण दुर्लभ संसाधनों के अधिग्रहण और रिलीज़ से संबंधित हैं, जैसे ताले, फ़ाइल विवरणक, या सॉकेट कनेक्शन। अधिक सामान्य मामलों में, प्रोग्राम स्टेट के किसी भी अस्थायी परिवर्तन के लिए कोड सैंडविच की आवश्यकता हो सकती है। उदाहरण के लिए, GUI- आधारित प्रोग्राम अस्थायी रूप से उपयोगकर्ता इनपुट को अनदेखा कर सकता है, या OS कर्नेल अस्थायी रूप से हार्डवेयर व्यवधान को निष्क्रिय कर सकता है। इन मामलों में पहले की स्थिति को बहाल करने में विफलता से गंभीर कीड़े हो जाएंगे।
कागज यह नहीं बताता है कि इस मुहावरे का उपयोग क्यों न किया जाए, लेकिन यह वर्णन करता है कि भाषा-स्तर की मदद के बिना मुहावरा आसान क्यों है:
अपवादों और उनके संबंधित अदृश्य नियंत्रण प्रवाह की उपस्थिति में दोषपूर्ण कोड सैंडविच सबसे अधिक बार उत्पन्न होते हैं। दरअसल, कोड सैंडविच को प्रबंधित करने के लिए विशेष भाषा सुविधाएँ मुख्य रूप से उन भाषाओं में उत्पन्न होती हैं जो अपवादों का समर्थन करती हैं।
हालांकि, अपवाद केवल दोषपूर्ण कोड सैंडविच का कारण नहीं हैं। जब भी बॉडी कोड में बदलाव किए जाते हैं , तो नए कंट्रोल पाथ उत्पन्न हो सकते हैं जो आफ्टर कोड को बायपास कर देते हैं । सबसे सरल मामले में, एक अनुरक्षक को एक नए दोष का परिचय करने
return
के लिए केवल एक सैंडविच के शरीर में एक बयान जोड़ने की आवश्यकता होती है , जिससे मूक त्रुटियां हो सकती हैं। जब शरीर का कोड बड़ा होता है और पहले और बाद में व्यापक रूप से अलग हो जाता है, तो ऐसी गलतियों से नेत्रहीन का पता लगाना मुश्किल हो सकता है।
मैं समझाने की कोशिश करूँगा, क्योंकि मैं चार साल का था:
उदाहरण 1
सांता शहर आ रहा है। जब भी वे अपनी पीठ के पीछे चाहते हैं, तब तक उनका कल्पित कोड होता है, और जब तक कि वे चीजों को नहीं बदलते तब तक थोड़ा दोहराव मिलता है:
या यह:
.... विज्ञापन एक लाख अलग-अलग प्रस्तुतियों के साथ एक लाख बार देखें: ध्यान दें कि एकमात्र चीज अलग है चरण 2। यदि चरण दो एकमात्र चीज है जो अलग है, तो सांता कोड को डुप्लिकेट क्यों कर रहा है, अर्थात वह चरणों की नकल क्यों कर रहा है 1 और 3 एक लाख बार? एक लाख प्रस्तुत का मतलब है कि वह अनावश्यक रूप से 1 और 3 लाख बार दोहरा रहा है।
आस-पास का निष्पादन उस समस्या को हल करने में मदद करता है। और कोड को खत्म करने में मदद करता है। चरण 1 और 3 मूल रूप से स्थिर हैं, चरण 2 को बदलने का एकमात्र हिस्सा है।
उदाहरण # 2
यदि आप अभी भी इसे प्राप्त नहीं करते हैं, तो यहां एक और उदाहरण है: सैंडविच के बारे में सोचें: बाहर की रोटी हमेशा समान होती है, लेकिन आपके द्वारा चुने गए सैंडविच के प्रकार (.eg हैम, पनीर) के आधार पर अंदर के परिवर्तनों पर क्या होता है। जैम, मूंगफली का मक्खन आदि)। ब्रेड हमेशा बाहर की तरफ होता है और आपको हर प्रकार के सैंडविच के लिए एक अरब बार दोहराने की आवश्यकता नहीं होती है।
अब यदि आप ऊपर दिए गए स्पष्टीकरणों को पढ़ते हैं, तो शायद आपको समझने में आसानी होगी। मुझे आशा है कि इस स्पष्टीकरण से आपको मदद मिली।
यह मुझे रणनीति डिजाइन पैटर्न की याद दिलाता है । ध्यान दें कि मैंने जिस लिंक को इंगित किया है, उसमें पैटर्न के लिए जावा कोड शामिल है।
जाहिर है कि कोई भी इनिशियलाइज़ेशन और क्लीनअप कोड बनाकर "एक्ज़ेक्यूट अराउंड" परफॉर्म कर सकता है और बस एक स्ट्रैटेजी में पास हो जाएगा, जिसे फिर इनिशियलाइज़ेशन और क्लीनअप कोड में लपेटा जाएगा।
जैसा कि कोड पुनरावृत्ति को कम करने के लिए उपयोग की जाने वाली किसी भी तकनीक के साथ, आपको इसका उपयोग तब तक नहीं करना चाहिए जब तक कि आपके पास कम से कम 2 मामले हों जहां आपको इसकी आवश्यकता हो, शायद 3 भी (एक ला वाईजीएनआई सिद्धांत)। ध्यान रखें कि हटाने कोड पुनरावृत्ति रखरखाव को कम कर देता है (कोड की कम प्रतियां का मतलब है कि प्रत्येक प्रतिलिपि में कम से कम समय बिताया गया है), लेकिन रखरखाव (अधिक कुल कोड) को भी बढ़ाता है। इस प्रकार, इस चाल की लागत यह है कि आप अधिक कोड जोड़ रहे हैं।
इस प्रकार की तकनीक केवल आरंभीकरण और सफाई से अधिक के लिए उपयोगी है। यह तब भी अच्छा है जब आप अपने कार्यों को कॉल करना आसान बनाना चाहते हैं (जैसे कि आप इसे विज़ार्ड में उपयोग कर सकते हैं ताकि "अगला" और "पिछले" बटन को यह तय करने के लिए विशाल केस स्टेटमेंट की आवश्यकता न हो कि क्या करना है? अगला / पिछला पृष्ठ।
यदि आप मुहावरेदार मुहावरे चाहते हैं, तो यह है:
//-- the target class
class Resource {
def open () { // sensitive operation }
def close () { // sensitive operation }
//-- target method
def doWork() { println "working";} }
//-- the execute around code
def static use (closure) {
def res = new Resource();
try {
res.open();
closure(res)
} finally {
res.close();
}
}
//-- using the code
Resource.use { res -> res.doWork(); }