अस्थायी चर बनाम लाइन लंबाई की आवश्यकताएं


10

मैं मार्टिन फाउलर की रिफैक्टरिंग पढ़ रहा हूं । यह आम तौर पर उत्कृष्ट है लेकिन फाउलर की सिफारिशों में से एक को थोड़ा परेशानी का कारण लगता है।

Fowler अनुशंसा करता है कि आप अस्थायी चर को किसी क्वेरी से प्रतिस्थापित करते हैं, इसलिए इसके बजाय:

double getPrice() {
    final int basePrice = _quantity * _itemPrice;
    final double discountFactor;
    if (basePrice > 1000) discountFactor = 0.95;
    else discountFactor = 0.98;
    return basePrice * discountFactor;
}

आप एक सहायक विधि में बाहर खींच:

double basePrice() {
    return _quantity * _itemPrice;
}

double getPrice() {
    final double discountFactor;
    if (basePrice() > 1000) discountFactor = 0.95;
    else discountFactor = 0.98;
    return basePrice() * discountFactor;
}

सामान्य तौर पर मैं इस बात को छोड़कर सहमत हूं कि अस्थायी चर का उपयोग करने का एक कारण यह है कि कोई रेखा बहुत लंबी है। उदाहरण के लिए:

$host = 'https://api.twilio.com';
$uri = "$host/2010-04-01/Accounts/$accountSid/Usage/Records/AllTime";
$response = Api::makeRequest($uri);

अगर मैंने इनलाइनिंग की कोशिश की, तो लाइन 80 अक्षरों से अधिक लंबी हो जाएगी।

वैकल्पिक रूप से मैं कोड की श्रृंखलाओं के साथ समाप्त होता हूं, जो खुद को पढ़ने के लिए उतना आसान नहीं हैं:

$params = MustacheOptions::build(self::flattenParams($bagcheck->getParams()));

दोनों में सामंजस्य बनाने के लिए कुछ रणनीतियाँ क्या हैं?


10
80 चर मेरे मॉनिटर के 1 में से 1 / 3rd है। क्या आप सुनिश्चित हैं कि lines० चार लाइनों को चिपकाना अभी भी आपके लिए सार्थक है?
जे.के.

10
हां, उदाहरण के लिए देखें programmers.stackexchange.com/questions/604/…
केविन बुर्के

आपका $hostऔर $uriउदाहरण एक तरह से विरोधाभास है, हालांकि - जब तक कि मेजबान को एक सेटिंग या अन्य इनपुट से पढ़ा नहीं जा रहा था, मैं उन्हें एक ही लाइन पर रहना पसंद करूंगा, भले ही वह लपेटे या किनारे से न जाए।
इजाकाता २१'११ ४:०३

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

जबकि मुझे लगता है कि 80 वर्ण की सीमा थोड़ी अधिक है, समान सीमा (100?) उचित है। उदाहरण के लिए, मैं अक्सर पोर्ट्रेट ओरिएंटेड मॉनिटर पर प्रोग्रामिंग करना पसंद करता हूं, इसलिए अतिरिक्त लंबी लाइनें कष्टप्रद हो सकती हैं (कम से कम अगर वे आम हैं)।
थॉमस एडिंग

जवाबों:


16


1. कैसे लाइन की लंबाई प्रतिबंध मौजूद है ताकि आप अधिक कोड देख सकें / समझ सकें । वे अभी भी मान्य हैं।
2. अंधे अधिवेशन पर निर्णय पर जोर दें ।
3. प्रदर्शन से अनुकूलन करने तक अस्थायी चर से बचें ।
4. बहु-पंक्ति कथनों में संरेखण के लिए गहरे इंडेंटेशन का उपयोग करने से बचें।
5. विचार सीमाओं के साथ कई लाइनों में लंबे बयानों को तोड़ें :

// prefer this
var distance = Math.Sqrt(
    Math.Pow(point2.GetX() - point1.GetX(), 2) + // x's
    Math.Pow(point2.GetY() - point1.GetY(), 2)   // y's
);

// over this
var distance = Math.Sqrt(Math.Pow(point2.GetX() -
    point1.GetX(), 2) + Math.Pow(point2.GetY() -
    point1.GetY(), 2)); // not even sure if I typed that correctly.

रीज़निंग
मेरे (डिबगिंग) के मुख्य स्रोत अस्थायी चर के साथ समस्या यह है कि वे परस्पर भिन्न होते हैं। अर्थात्, मुझे लगता है कि जब मैं कोड लिखता हूं, तो वे एक मूल्य होते हैं, लेकिन यदि फ़ंक्शन जटिल है, तो कोड के कुछ अन्य टुकड़े उनके राज्य के रास्ते को बदल देते हैं। (या रूपांतरण, जहां चर की स्थिति समान रहती है, लेकिन क्वेरी का परिणाम बदल गया है)।

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

आपके द्वारा दिए गए उदाहरण (जावा और ... PHP?) दोनों मल्टी-लाइन स्टेटमेंट के लिए अनुमति देते हैं। यदि लाइनें लंबी हो जाती हैं, तो उन्हें तोड़ दें। Jquery का स्रोत इसे चरम सीमा तक ले जाता है। (पहला कथन 69 रेखा पर चलता है!) ऐसा नहीं है कि मैं आवश्यक रूप से सहमत हूं, लेकिन टेम्पर्ड वर्जन का उपयोग करने की तुलना में आपके कोड को पठनीय बनाने के अन्य तरीके हैं।

कुछ उदाहरण
1. अजगर के लिए PEP 8 स्टाइल गाइड (सबसे सुंदर उदाहरण नहीं)
2. नाशपाती स्टाइल गाइड पर पॉल एम जोन्स (सड़क तर्क के बीच में)
3. ओरेकल लाइन की लंबाई + रैपिंग सम्मेलनों (80 वर्णों को ध्यान में रखते हुए उपयोगी स्ट्रेटेजम्स)
4. एमडीएन जावा प्रैक्टिस (अधिवेशन में प्रोग्रामर के फैसले पर जोर)


1
समस्या का दूसरा हिस्सा यह है कि एक अस्थायी चर अक्सर इसके मूल्य को रेखांकित करता है। छोटे दायरे के ब्लॉक में समस्या नहीं है, लेकिन बड़े लोगों में, हाँ, एक बड़ी समस्या है।
रॉस पैटरसन

8
यदि आप अस्थायी रूप से संशोधित होने के बारे में चिंतित हैं, तो इस पर एक प्रस्ताव रखें।
थॉमस ईडिंग

3

मुझे लगता है, अस्थायी चर के बजाय सहायक तरीकों का उपयोग करने के लिए सबसे अच्छा तर्क मानव पठनीयता है। यदि आप, एक मानव के रूप में, हेल्पर-विधि-श्रृंखला को अस्थायी संस्करण से पढ़ने में अधिक परेशानी महसूस करते हैं, तो मुझे कोई कारण नहीं दिखता कि आपको उन्हें क्यों निकालना चाहिए।

(मुझे सही जवाब दो अगर मैं गलत हूँ)


3

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

मुझे आपके द्वारा पोस्ट किए गए उदाहरणों पर ध्यान दें।

$host = 'https://api.twilio.com';
$uri = "$host/2010-04-01/Accounts/$accountSid/Usage/Records/AllTime";
$response = Api::makeRequest($uri);

मेरा अवलोकन यह है कि सभी टवीलियो एप कॉल "https://api.twilio.com/2010-04-1/" से शुरू होंगे, और इस प्रकार एक बहुत ही स्पष्ट पुन: प्रयोज्य कार्य होना है:

$uri = twilioURL("Accounts/$accountSid/Usage/Records/AllTime")

वास्तव में, मुझे लगता है कि URL बनाने का एकमात्र कारण अनुरोध करना है, इसलिए मैं यह करूंगा:

$response = TwilioApi::makeRequest("Accounts/$accountSid/Usage/Records/AllTime")

वास्तव में, कई यूआरएल वास्तव में "अकाउंट्स / $ अकाउंटसिड" से शुरू होते हैं, इसलिए मैं संभवतः इसे भी निकालूंगा:

$response = TwilioApi::makeAccountRequest($accountSid, "Usage/Records/AllTime")

और अगर हम twilio api को एक ऑब्जेक्ट बनाते हैं जो खाता संख्या रखता है, तो हम कुछ ऐसा कर सकते हैं:

$response = $twilio->makeAccountRequest("Usage/Records/AllTime")

एक $ टवीलियो ऑब्जेक्ट का उपयोग करने से यूनिट परीक्षण को आसान बनाने का लाभ मिलता है। मैं वस्तु को एक अलग $ टवीलियो ऑब्जेक्ट दे सकता हूं जो वास्तव में टवीलियो को वापस नहीं बुलाता है, जो तेज होगा और टवीलियो के लिए अजीब चीजें नहीं करेगा।

दूसरे को देखते हैं

$params = MustacheOptions::build(self::flattenParams($bagcheck->getParams()));

यहाँ मैं या तो इसके बारे में सोचूंगा:

$params = MustacheOptions::buildFromParams($bagcheck->getParams());

या

$params = MustacheOptions::build($bagcheck->getFlatParams());

या

$params = MustacheOptions::build(flatParams($backCheck));

जिसके आधार पर अधिक पुन: प्रयोज्य मुहावरा है।


1

वास्तव में, मैं इस मामले में प्रख्यात श्री फाउलर से असहमत हूं।

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

हालांकि, बराबर, अक्सर अधिक वैचारिक मूल्य "मूल्य पुन: उपयोग" होता है। श्री Fowler इन निकाले गए तरीकों को अस्थायी चर "क्वेरी" को बदलने के लिए कहते हैं। खैर, क्या अधिक कुशल है; किसी डेटाबेस की क्वेरी करना कई बार आपको किसी विशेष मूल्य की आवश्यकता होती है, या एक बार क्वेरी करने और परिणाम को संग्रहीत करने के लिए (मान लेते हैं कि मूल्य स्थिर है कि आप इसे बदलने की उम्मीद नहीं करेंगे)?

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

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

double? _basePrice; //not sure if Java has C#'s "nullable" concept
double basePrice(bool forceCalc)
{
   if(forceCalc || !_basePrice.HasValue)
      return _basePrice = _quantity * _itemPrice;
   return _basePrice.Value;
}

अब, इस तुच्छ गणना के लिए, जो बचाए गए कार्य की तुलना में अधिक कार्य किया गया है, और इसलिए मैं आमतौर पर अस्थायी चर के साथ चिपके रहने की सलाह देता हूं; हालाँकि, अधिक जटिल गणनाओं के लिए जिन्हें आप आमतौर पर कई बार चलाने से बचना चाहते हैं, और आपको कोड में कई स्थानों की आवश्यकता होती है, यह वह तरीका है जिससे आप इसे करेंगे।


1

हेल्पर के तरीकों में एक जगह होती है, लेकिन आपको डेटा की निरंतरता सुनिश्चित करने, और चर के दायरे में अनावश्यक वृद्धि के बारे में सावधान रहना होगा।

उदाहरण के लिए, अपने स्वयं के उदाहरण का हवाला देते हैं:

double getPrice() {
    final double discountFactor;
    if (basePrice() > 1000) discountFactor = 0.95;      <--- first call
    else discountFactor = 0.98;
    return basePrice() * discountFactor;                <--- second call
}

जाहिर है दोनों _quantityऔर _itemPriceवैश्विक चर रहे हैं (या कम से कम श्रेणी स्तर पर) और इसलिए वहाँ के लिए उनमें से संशोधित बाहर होने के लिए एक संभावित हैgetPrice()

इसलिए पहली कॉल के लिए basePrice()दूसरी कॉल से एक अलग मूल्य वापस करने की क्षमता है !

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


आपको Reductio ad absurdum से भी बचना होगा - क्या discountFactorविधि की गणना की जानी चाहिए ? तो आपका उदाहरण बनता है:

double getPrice()
{
    final double basePrice      = calculateBasePrice();
    final double discountFactor = calculateDiscount( basePrice );

    return basePrice * discountFactor;
}

एक निश्चित स्तर से परे विभाजन वास्तव में कोड को कम पठनीय बनाता है।


कोड को कम पठनीय बनाने के लिए +1। पार्टिशन कोड को हल करने की कोशिश कर रहे व्यापार समस्या को छिपा सकते हैं। ऐसे विशेष मामले हो सकते हैं जहां getPrice () में एक कूपन लगाया जाता है, लेकिन अगर वह फ़ंक्शन कॉल की श्रृंखला में गहरा छिपा होता है, तो व्यावसायिक नियम भी छिपा होता है।
रिएक्टगुलर

0

यदि आप नामित मापदंडों (ऑब्जेक्टिव, पायथन, रूबी, आदि) के साथ भाषा में काम करते हैं, तो अस्थायी संस्करण कम उपयोगी नहीं हैं।

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

हालांकि, आप की तरह, मैं स्पष्टता और लाइन की लंबाई के विचार के लिए अस्थायी चर का उपयोग करता हूं।

मैंने यह भी देखा है कि प्रोग्रामर PHP में निम्नलिखित करते हैं। यह दिलचस्प और डिबगिंग के लिए बढ़िया है, लेकिन यह थोड़ा अजीब है।

$rs = DB::query( $query = "SELECT * FROM table" );
if (DEBUG) echo $query;
// do something with $rs

0

इस सिफारिश के पीछे तर्क यह है कि आप अपने आवेदन में कहीं और उसी प्री-कॉम्पटिशन का उपयोग करने में सक्षम होना चाहते हैं। रीफ़्रेशिंग पैटर्न कैटलॉग में क्वेरी के साथ बदलें अस्थायी देखें :

नई विधि का उपयोग अन्य विधियों में किया जा सकता है

    double basePrice = _quantity * _itemPrice;
    if (basePrice > 1000)
        return basePrice * 0.95;
    else
        return basePrice * 0.98;

           http://i.stack.imgur.com/mKbQM.gif

    if (basePrice() > 1000)
        return basePrice() * 0.95;
    else
        return basePrice() * 0.98;
...
double basePrice() {
    return _quantity * _itemPrice;
}

इस प्रकार, आपके मेजबान और यूआरआई उदाहरण में, मैं केवल इस सिफारिश को लागू करूंगा यदि मैं उसी यूआरआई या मेजबान परिभाषा का पुन: उपयोग करने की योजना बनाता हूं।

यदि यह मामला है, तो नामस्थान के कारण, मैं वैश्विक uri () या होस्ट () विधि को परिभाषित नहीं करूंगा, लेकिन अधिक जानकारी वाला एक नाम, जैसे twilio_host (), या आर्काइव_रेक्वेस्ट_यूरी ()।

फिर, लाइन की लंबाई के मुद्दे के लिए, मुझे कई विकल्प दिखाई देते हैं:

  • एक स्थानीय चर बनाएं, जैसे uri = archive_request_uri()

तर्क: वर्तमान पद्धति में, आप चाहते हैं कि URI का उल्लेख हो। यूआरआई की परिभाषा अभी भी कारक है।

  • स्थानीय पद्धति को परिभाषित करें, जैसे uri() { return archive_request_uri() }

यदि आप अक्सर फाउलर की सिफारिश का उपयोग करते हैं, तो आपको पता होगा कि उरी () विधि एक ही पैटर्न है।

यदि भाषा विकल्प के कारण आपको 'स्व' के साथ स्थानीय पद्धति का उपयोग करने की आवश्यकता होती है, तो मैं स्पष्टता के लिए (पायथन में, वर्तमान विधि के अंदर uri फ़ंक्शन को परिभाषित करता हूं) पहले समाधान की सिफारिश करूंगा।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.