DanLevy.net

पाइपलाइन के मास्टर: स्टेट पास करना

नमस्ते क्लोजर, मेरे पुराने मित्र।

Hero image for पाइपलाइन के मास्टर: स्टेट पास करना

पाइपलाइन के मास्टर: स्टेट पास करना

क्या आपने फंक्शनल पाइपलाइन का उपयोग करते हुए स्टेट पास करने में चुनौतियों का सामना किया है?

आपके कोड का संगठन (या उसकी कमी) सीधे तौर पर इस बात को प्रभावित करता है कि स्टेट कितनी आसानी से पास की जा सकती है।

इस लेख में हम पाइपलाइन के माध्यम से स्टेट पास करने की एक प्रभावी तकनीक का पता लगाएंगे। इस प्रक्रिया में हम अपने कोड के संगठन और पठनीयता में सुधार करेंगे।

निम्नलिखित “वास्तविक” स्निपेट इस लेख के लिए हमारा फोकस होगा: एक चेकआउट फंक्शन, जो userId और products की एक array स्वीकार करता है। यह एक Promise-chain लौटाता है जो क्रम में 4 फंक्शन्स निष्पादित करता है।

const checkout = (userId: number, products: number[]) => {
return getProductsSubtotal(userId, products)
.then(subTotal => applyTaxes(userId, subTotal))
.then(total => purchaseProducts(userId, total))
.then(result => sendReceipt(userId, result));
};

रुको एक सेकंड, यह कोड वास्तव में काफी अच्छा है, जहाँ तक JS में पाइपलाइन्स का सवाल है!

यह कुछ सूक्ष्म समस्याओं से ग्रस्त है जो बड़ी समस्याओं में मिल सकती हैं।

एक समस्या यह है कि हम userId को प्रत्येक (तार्किक रूप से संबंधित) फंक्शन में बार-बार पास कर रहे हैं। अब इसे एक और समस्या के साथ जोड़ें जिसे डेवलपर्स और TypeScript दोनों आसानी से चूक जाते हैं: न्यूमेरिक आर्गुमेंट्स को पलटने से आसानी से एक साइलेंट बग बन जाता है। (applyTaxes और purchaseProducts देखें। क्या userId पहले जाता है या amount?)

इससे पहले कि हम तय करें कि इस कोड को कैसे बेहतर बनाया जाए, आइए कुछ पक्ष/विपक्ष की पहचान करें।

पक्ष और विपक्ष

पक्ष

विपक्ष

समाधान, भाग 1: एक मॉड्यूल बनाएं!

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

CartHelpers फैक्टरी

CartHelpers मॉड्यूल का उदाहरण, जहाँ userId को एक बार पास किया जाता है, और सभी मेथड्स सिंगल-आर्गुमेंट हैं।

const CartHelpers = (userId: number) => {
return {
getProductsSubtotal: products => getProductsSubtotal(userId, products),
applyTaxes: subTotal => applyTaxes(userId, subTotal),
purchaseProducts: total => purchaseProducts(userId, total),
sendReceipt: invoice => sendReceipt(userId, invoice)
};
};

CartHelpers क्लास

यदि क्लासेस आपकी चीज़ हैं, तो अनुकूलित करना आसान है:

class CartHelpers {
constructor(userId) {
this.userId = userId;
}
getProductsSubtotal = products => getProductsSubtotal(this.userId, products);
applyTaxes = subTotal => applyTaxes(this.userId, subTotal);
purchaseProducts = total => purchaseProducts(this.userId, total);
sendReceipt = invoice => sendReceipt(this.userId, invoice);
}

कुछ तत्काल लाभ:

संबंधित फंक्शन्स को समूहीकृत करके, हम एक्सपोज्ड सरफेस एरिया को कम करने का अवसर बनाते हैं (उदाहरण checkout(), CartHelpers की ‘पब्लिक’ मेथड्स।)

कम सरफेस एरिया === कम कॉग्निटिव लोड, बेहतर टेस्टिंग और मेंटेनेबिलिटी। डिज़ाइन सिस्टम को इरादे और फोकस के साथ बनाएं। ✨

Checkout और CartHelpers का उपयोग

आइए देखें कि checkout() फंक्शन अब कैसा दिखता है:

export const checkout = ({ userId, products }) => {
const cart = CartHelpers(userId);
return Promise.resolve(products)
.then(products => cart.getProductsSubtotal(products))
.then(subTotal => cart.applyTaxes(subTotal))
.then(total => cart.purchaseProducts(total))
.then(result => cart.sendReceipt(result));
};
आगे के सुधारों के साथ Checkout

क्या इसे और बेहतर बनाया जा सकता है? हाँ! हमें बिल्कुल भी आर्गुमेंट्स को दोहराने की आवश्यकता नहीं है!

जब किसी फंक्शन के आर्गुमेंट्स पिछले फंक्शन्स के आउटपुट द्वारा प्रदान किए जाते हैं, तो आप कोड को और भी सरल बना सकते हैं।

export const checkout = ({ userId, products }) => {
const cart = CartHelpers(userId);
// 🌈 फंक्शन्स लेगो की तरह स्टैक होते हैं और सामान्य "मानव शब्दों" की तरह पढ़े जाते हैं! 💅
return Promise.resolve(products)
.then(cart.getProductsSubtotal)
.then(cart.applyTaxes)
.then(cart.purchaseProducts)
.then(cart.sendReceipt);
};

यदि पैरामीटर्स को सिंगल (ऑब्जेक्ट) आर्गुमेंट्स में मिलाना अप्राकृतिक लगता है, तो अपने फंक्शन्स को तोड़ने या उन्हें अधिक उचित रूप से स्कोप्ड मॉड्यूल्स में मिलाने पर विचार करें।

कहाँ से शुरू करें?

संबंधित फंक्शन्स खोजें, और उन्हें एक साथ समूहित करें। (उदाहरण CartHelpers।)

संभावित लॉजिकल मॉड्यूल्स खोजने की चुनौती का एक हिस्सा सबसे पहले संबंधित कोड की पहचान करना है।

फंक्शन्स को संबंधित क्या बनाता है?

एक बढ़िया तरकीब: फंक्शन पैरामीटर्स में दोहराव खोजें। पूछें कि क्या कोई संबंध खेल में है? या कोई अंतर्निहित जिम्मेदारी?

हालाँकि मॉड्यूल्स को डिज़ाइन करने का कोई एक “सही उत्तर” नहीं है, संगठन के लिए 2-3 विकल्पों की पहचान करने में मदद मिलती है - एक आउटलाइन खींचें, “काल्पनिक” कोड लिखें, पूछें “क्या यह खुशी लाता है?”

आपको लग सकता है कि cart.sendReceipt() पेमेंट-संबंधित मेथड्स के साथ नहीं belongs। शायद customerNotifications.sendReceipt() कस्टमर मैसेजिंग के लिए एक बेहतर घर है। यदि CartHelper महत्व में पर्याप्त ऊँचा है, तो यह आंतरिक रूप से सभी आवश्यक सर्विसेज, जैसे customerNotifications को कॉल करने वाले कंट्रोलर के रूप में कार्य कर सकता है।

आप कैसे जानते हैं कि आप मदद कर रहे हैं?

यदि ad-hoc आर्गुमेंट्स को समाप्त करते हुए पठनीयता को नुकसान नहीं होता है, बधाई हो!!! आपने संभवतः एक स्पष्ट और टिकाऊ स्कोप वाले मॉड्यूल का निर्माण किया है!

तो, यह सवाल खड़ा होता है, हम फंक्शनैलिटी कहाँ जोड़ते हैं?

मेरे अनुभव में फंक्शनैलिटी जोड़ते समय मूल्यांकन करने के लिए 2 प्राथमिक रणनीतियाँ हैं:

  1. मौजूदा मेथड को एक्सटेंड/रिफैक्टर करें। (जब नया कोड मौजूदा कोड के काफी करीब हो।)
  2. चेन में वांछित स्थान पर एक नया (5वाँ) फंक्शन बनाएं। (यह मानते हुए कि नया कोड मौजूदा फंक्शन्स से असंबंधित है।)

अंततः यह तय करना आसान हो जाता है कि नई फंक्शनैलिटी कहाँ belongs। (उदाहरण cart.applyDiscounts(), cart.applyTaxes(), rewards.getBalance()।)

निष्कर्ष

किसी जटिल पाइपलाइन के माध्यम से स्टेट पास करना मुश्किल हो सकता है। हालाँकि, थोड़े रिफैक्टर अभ्यास के साथ, आप अपने आप को अधिक पठनीय कोड लिखते हुए पाएंगे, कम कॉग्निटिव लोड के साथ।

प्रश्न? टिप्पणियाँ? चिंताएँ? बेझिझक संपर्क करें @justsml या email

श्रृंखला में अगले भाग के लिए बने रहें

हम अपने मॉड्यूल में स्टेट को बाह्य करने और फंक्शनैलिटी को बढ़ाने का पता लगाएंगे!

संबंधित पठन