एक कोड गंध एक लक्षण है जो इंगित करता है कि डिजाइन में एक समस्या है जो संभावित रूप से बगों की संख्या में वृद्धि करेगी: यह क्षेत्रों के लिए मामला नहीं है, लेकिन क्षेत्र लंबे तरीकों की तरह कोड बदबू पैदा करने में योगदान कर सकते हैं।
जबसे:
एक एंटी-पैटर्न (या एंटीपैटर्न) एक ऐसा पैटर्न है जो सामाजिक या व्यावसायिक संचालन या सॉफ्टवेयर इंजीनियरिंग में उपयोग किया जाता है जो आमतौर पर इस्तेमाल किया जा सकता है लेकिन व्यवहार में अप्रभावी और / या उल्टा होता है।
क्षेत्रों रहे हैं विरोधी पैटर्न। उन्हें अधिक कार्य की आवश्यकता होती है जो गुणवत्ता या कोड की पठनीयता को नहीं बढ़ाता है, जो बग की संख्या को कम नहीं करता है, और जो केवल कोड को रिफ्लेक्टर के लिए अधिक जटिल बना सकता है।
तरीकों के अंदर क्षेत्रों का उपयोग न करें; इसके बजाय रिफ्लेक्टर
तरीके छोटे होने चाहिए । यदि एक विधि में केवल दस लाइनें हैं, तो आप शायद अन्य पांच पर काम करते समय उनमें से पांच को छिपाने के लिए क्षेत्रों का उपयोग नहीं करेंगे।
इसके अलावा, प्रत्येक विधि को एक और एक ही काम करना चाहिए । दूसरी ओर, क्षेत्र अलग-अलग चीजों को अलग करने के लिए अभिप्रेत हैं । यदि आपका तरीका A करता है, तो B, दो क्षेत्रों को बनाना तर्कसंगत है, लेकिन यह एक गलत तरीका है; इसके बजाय, आपको विधि को दो अलग-अलग तरीकों में रिफ्लेक्टर करना चाहिए।
इस मामले में क्षेत्रों का उपयोग करना भी रिफैक्टिंग को और अधिक कठिन बना सकता है। आप कल्पना कीजिए:
private void DoSomething()
{
var data = LoadData();
#region Work with database
var verification = VerifySomething();
if (!verification)
{
throw new DataCorruptedException();
}
Do(data);
DoSomethingElse(data);
#endregion
#region Audit
var auditEngine = InitializeAuditEngine();
auditEngine.Submit(data);
#endregion
}
दूसरे पर ध्यान केंद्रित करने के लिए पहले क्षेत्र को संकुचित करना न केवल जोखिम भरा है: हम प्रवाह को रोकने के अपवाद के बारे में आसानी से भूल सकते हैं ( return
इसके बजाय एक गार्ड क्लॉज हो सकता है , जो स्पॉट करना और भी मुश्किल है), लेकिन एक समस्या भी होगी यदि कोड को इस तरह से फिर से बनाया जाना चाहिए:
private void DoSomething()
{
var data = LoadData();
#region Work with database
var verification = VerifySomething();
var info = DoSomethingElse(data);
if (verification)
{
Do(data);
}
#endregion
#region Audit
var auditEngine = InitializeAuditEngine(info);
auditEngine.Submit(
verification ? new AcceptedDataAudit(data) : new CorruptedDataAudit(data));
#endregion
}
अब, क्षेत्रों का कोई मतलब नहीं है, और आप संभवतः पहले क्षेत्र में कोड को देखे बिना दूसरे क्षेत्र में कोड को पढ़ और समझ नहीं सकते हैं।
एक और मामला जो मुझे कभी-कभी दिखाई देता है, वह यह है:
public void DoSomething(string a, int b)
{
#region Validation of arguments
if (a == null)
{
throw new ArgumentNullException("a");
}
if (b <= 0)
{
throw new ArgumentOutOfScopeException("b", ...);
}
#endregion
#region Do real work
...
#endregion
}
यह उन क्षेत्रों का उपयोग करने के लिए ललचाता है जब तर्क सत्यापन LOC के दसियों पर शुरू होता है, लेकिन इस समस्या को हल करने का एक बेहतर तरीका है: .NET फ्रेमवर्क स्रोत कोड द्वारा उपयोग किया जाने वाला:
public void DoSomething(string a, int b)
{
if (a == null)
{
throw new ArgumentNullException("a");
}
if (b <= 0)
{
throw new ArgumentOutOfScopeException("b", ...);
}
InternalDoSomething(a, b);
}
private void InternalDoSomething(string a, int b)
{
...
}
समूह से बाहर के क्षेत्रों का उपयोग न करें
कुछ लोग उन्हें फ़ील्ड्स, प्रॉपर्टीज़ आदि के साथ समूह बनाने के लिए उपयोग करते हैं । यह दृष्टिकोण गलत है: यदि आपका कोड StyleCop-compliant है, तो फ़ील्ड्स, प्रॉपर्टीज़, प्राइवेट मेथड्स, कंस्ट्रक्टर्स इत्यादि पहले से ही एक साथ समूहीकृत और खोजने में आसान हैं। यदि ऐसा नहीं है, तो यह नियम लागू करने के बारे में सोचना शुरू करने का समय है जो आपके कोडबेस में एकरूपता सुनिश्चित करता है।
अन्य लोग कई समान संस्थाओं को छिपाने के लिए क्षेत्रों का उपयोग करते हैं । उदाहरण के लिए, जब आपके पास सौ फ़ील्ड्स के साथ एक वर्ग होता है (जो कि कम से कम 500 लाइनों को कोड बनाता है यदि आप टिप्पणियों और व्हाट्सएप की गिनती करते हैं), तो आपको उन क्षेत्रों को एक क्षेत्र के अंदर रखने, इसे ढहाने और उनके बारे में भूलने के लिए लुभाया जा सकता है। फिर, आप इसे गलत कर रहे हैं: एक वर्ग में इतने सारे क्षेत्रों के साथ, आपको विरासत का उपयोग करने के बारे में बेहतर सोचना चाहिए या कई वस्तुओं पर ऑब्जेक्ट को स्लाइस करना चाहिए।
अंत में, कुछ लोगों को संबंधित चीजों को एक साथ रखने के लिए क्षेत्रों का उपयोग करने के लिए लुभाया जाता है : अपने प्रतिनिधि के साथ एक घटना, या IO से संबंधित अन्य विधियों के साथ IO से संबंधित एक विधि, आदि। पहले मामले में, यह एक गड़बड़ हो जाता है जिसे बनाए रखना मुश्किल होता है , पढ़ें और समझें। दूसरे मामले में, बेहतर डिजाइन संभवतः कई कक्षाएं बनाने के लिए होगा।
क्या क्षेत्रों के लिए एक अच्छा उपयोग है?
नहीं, एक विरासत उपयोग था: उत्पन्न कोड। फिर भी, कोड जेनरेशन टूल्स को केवल आंशिक कक्षाओं का उपयोग करना होगा। यदि C # में क्षेत्रों का समर्थन है, तो यह ज्यादातर इसलिए है क्योंकि यह विरासत उपयोग करती है, और क्योंकि अब बहुत सारे लोग अपने कोड में क्षेत्रों का उपयोग करते हैं, इसलिए उन्हें मौजूदा कोडबेस को तोड़े बिना उन्हें हटाना असंभव होगा।
के बारे में के रूप में इसके बारे में सोचो goto
। तथ्य यह है कि भाषा या आईडीई एक सुविधा का समर्थन करता है इसका मतलब यह नहीं है कि इसका दैनिक उपयोग किया जाना चाहिए। StyleCop SA1124 नियम स्पष्ट है: आपको क्षेत्रों का उपयोग नहीं करना चाहिए। कभी नहीँ।
उदाहरण
मैं वर्तमान में अपने सहकर्मी के कोड की कोड समीक्षा कर रहा हूं। कोडबेस में बहुत सारे क्षेत्र होते हैं, और वास्तव में दोनों का एक आदर्श उदाहरण है कि कैसे क्षेत्रों का उपयोग न करें और क्यों क्षेत्र खराब कोड का नेतृत्व करते हैं। यहाँ कुछ उदाहरण हैं:
4 000 LOC राक्षस:
मैंने हाल ही में Programmers.SE पर कहीं पढ़ा है। जब किसी फ़ाइल में बहुत अधिक using
s होते हैं ("अनयूज्ड यूज़िंग" कमांड को निष्पादित करने के बाद), तो यह एक अच्छा संकेत है कि इस फ़ाइल के अंदर की क्लास बहुत अधिक कर रही है। वही फ़ाइल के आकार पर लागू होता है।
कोड की समीक्षा करते समय, मैं एक 4 000 LOC फ़ाइल में आया था। ऐसा प्रतीत हुआ कि इस कोड के लेखक ने सैकड़ों बार एक ही 15-पंक्तियों की विधि को कॉपी-पेस्ट किया, चर के नामों और थोड़े विधि को बदलकर। एक साधारण रेगेक्स ने फ़ाइल को 4 000 LOC से 500 LOC तक ट्रिम करने की अनुमति दी, बस कुछ जेनरिक जोड़कर; मुझे पूरा यकीन है कि अधिक चतुर रीफैक्टरिंग के साथ, यह क्लास कुछ दर्जन लाइनों तक सिमट सकती है।
क्षेत्रों का उपयोग करके, लेखक ने खुद को इस तथ्य को अनदेखा करने के लिए प्रोत्साहित किया कि कोड को बनाए रखना और खराब तरीके से लिखा जाना असंभव है, और कोड को रिफ्लेक्टर के बजाय भारी नकल करना।
क्षेत्र "Do A", क्षेत्र "Do B":
एक अन्य उत्कृष्ट उदाहरण एक मॉन्स्टर इनिशियलाइज़ेशन मेथड था, जिसमें केवल टास्क 1 किया गया, फिर टास्क 2, फिर टास्क 3, आदि पाँच या छह कार्य थे जो पूरी तरह से स्वतंत्र थे, प्रत्येक एक कंटेनर क्लास में कुछ इनिशियलाइज़ कर रहा था। उन सभी कार्यों को एक विधि में वर्गीकृत किया गया था, और क्षेत्रों में समूहीकृत किया गया था।
इससे एक फायदा हुआ:
- इस क्षेत्र के नामों को देखकर विधि बहुत स्पष्ट थी। यह कहा जा रहा है, एक ही विधि एक बार refactored मूल के रूप में स्पष्ट हो जाएगा।
दूसरी ओर, मुद्दे कई थे:
यह स्पष्ट नहीं था कि क्या क्षेत्रों के बीच निर्भरता थी। उम्मीद है, चर का पुन: उपयोग नहीं किया गया था; अन्यथा, रखरखाव एक बुरा सपना भी हो सकता है।
विधि का परीक्षण करना लगभग असंभव था। आप आसानी से कैसे जान पाएंगे कि जिस विधि से एक समय में बीस चीजें होती हैं, उन्हें सही तरीके से करता है?
फ़ील्ड्स क्षेत्र, गुण क्षेत्र, निर्माता क्षेत्र:
समीक्षा किए गए कोड में सभी क्षेत्रों को एक साथ समूहीकृत करने वाले बहुत सारे क्षेत्र भी शामिल हैं, आदि। यह एक स्पष्ट समस्या थी: स्रोत कोड विकास।
जब आप एक फ़ाइल खोलते हैं और खेतों की एक विशाल सूची देखते हैं, तो आप पहले कक्षा को फिर से भरने के लिए इच्छुक हैं, फिर कोड के साथ काम करें। क्षेत्रों के साथ, आप सामान ढहने की आदत डालते हैं और इसके बारे में भूल जाते हैं।
एक और समस्या यह है कि यदि आप इसे हर जगह करते हैं, तो आप अपने आप को एक-ब्लॉक क्षेत्र बना पाएंगे, जिसका कोई मतलब नहीं है। यह वास्तव में मेरे द्वारा समीक्षा किए गए कोड में मामला था, जहां #region Constructor
एक निर्माता के बहुत सारे थे ।
अंत में, फ़ील्ड, गुण, निर्माता आदि पहले से ही क्रम में होने चाहिए । यदि वे हैं और वे सम्मेलनों (एक बड़े अक्षर के साथ शुरू होने वाले स्थिरांक, आदि) से मेल खाते हैं, तो यह पहले से ही स्पष्ट है कि किस प्रकार के तत्व बंद हो जाते हैं और अन्य शुरू होते हैं, इसलिए आपको इसके लिए स्पष्ट रूप से क्षेत्र बनाने की आवश्यकता नहीं है।