चूंकि Ruby एक ऑब्जेक्ट-ओरिएंटेड भाषा है, हम दुनिया को ऑब्जेक्ट्स के एक सेट के रूप में मॉडल करते हैं। हम कहते हैं कि दो इंटीजर्स (x और y) एक पॉइंट हैं, और एक लाइन में उनमें से दो हैं।
जबकि यह दृष्टिकोण अक्सर उपयोगी होता है, इसकी एक बड़ी समस्या है। यह डेटा की एक व्याख्या को अन्य सभी पर विशेषाधिकार देता है। यह मान लेता है कि x, और y हमेशा एक पॉइंट होंगे और आपको कभी भी उनकी आवश्यकता नहीं होगी कि वे सेल या वेक्टर के रूप में कार्य करें।
जब आपको एक सेल की आवश्यकता होती है तो क्या होता है? खैर, पॉइंट डेटा का मालिक है। तो आप पॉइंट में अपने सेल मेथड्स जोड़ते हैं। समय के साथ आप एक पॉइंट क्लास के साथ समाप्त होते हैं जो ढीले से संबंधित मेथड्स का एक जंक ड्रॉअर है।
जंक-ड्रॉअर क्लासेस इतनी आम हैं कि हम अक्सर उन्हें अपरिहार्य मान लेते हैं और डेवलपमेंट में एक "रीफैक्टरिंग" चरण जोड़ देते हैं। लेकिन क्या होगा अगर हम पहली जगह में समस्या से बच सकते हैं?
वेब डेवलपमेंट का पहला नियम यह है कि हम यूजर क्लास के बारे में बात नहीं करते
किसी भी बैटल-हार्डेंड प्रोडक्शन Rails ऐप के दिल में User नामक एक विशाल राक्षस क्लास होती है।
यह हमेशा मासूमियत से शुरू होता है। आप लोगों को लॉग इन करने देना चाहते हैं। आपको उनका यूज़रनेम और पासवर्ड स्टोर करने की आवश्यकता है, तो आप एक क्लास बनाते हैं:
class User
attr_accessor :username, :password, :email, :address
def authenticate!(password)
...
end
end
यह इतना अच्छा काम करता है कि अंततः लोग आपको पैसा देना चाहते हैं। "ठीक है," हम कहते हैं "एक यूज़र मूल रूप से एक सब्सक्राइबर के समान ही चीज़ है तो हम बस कुछ विशेषताएं और कुछ मेथड्स जोड़ देंगे।"
class User
...
attr_accessor :payment_processor_token, :subscription_plan_id, ...etc
def charge_cc
...
end
end
शानदार! अब हम कुछ वास्तविक पैसा कमा रहे हैं! CEO ने फैसला किया है कि वे यूज़र्स की संपर्क जानकारी के साथ VCards निर्यात करने में सक्षम होना चाहते हैं ताकि सेल्स टीम उन्हें आसानी से SalesForce में आयात कर सके। vim को फायर करने का समय:
class User
...
def export_vcard
...
end
end
हमने क्या किया?
हम एक User क्लास के साथ शुरू हुए, जिसका एकमात्र उद्देश्य ऑथेंटिकेशन को संभालना था। अतिरिक्त मेथड्स और विशेषताओं को जोड़कर, हमने इसे User/Subscriber/Contact फ्रेंकस्टीन हाइब्रिड में बदल दिया।
हम ऐसा क्यों करेंगे? क्या हम खुद का सम्मान डेवलपर्स के रूप में नहीं करते? क्या हमारे माता-पिता ने हमें पर्याप्त प्यार नहीं किया? या हमने खुद को विफलता के लिए स्थापित किया यह मानकर कि यह डेटा वास्तव में एक चीज़ है, एक ऑब्जेक्ट?
हम क्यों कहते हैं कि यूज़रनेम, पासवर्ड और ईमेल एड्रेस का कॉम्बो एक यूज़र है? क्यों नहीं एक सब्सक्राइबर? या एक संपर्क? या एक सेशनहोल्डर?
डेटा के बारे में सच्चाई यह है कि यह डेटा है
डेटा सिर्फ डेटा है। आज आपको इसे प्रेमी की तरह व्यवहार करने की आवश्यकता हो सकती है। कल यह पूर्व-प्रेमी हो सकता है।
यह वह दृष्टिकोण है जो Elixir जैसी फंक्शनल भाषाएं लेती हैं। डेटा को सरल संरचनाओं में संग्रहीत किया जाता है जैसे Ruby के हैश, ऐरे, स्ट्रिंग्स, आदि। जब आप डेटा के साथ कुछ करना चाहते हैं तो आप इसे एक फंक्शन में पास करते हैं। कोई भी परिणाम उस फंक्शन से लौटाए जाते हैं।
यह सरल लगता है, लेकिन यह दृष्टिकोण विभिन्न मॉड्यूल्स में चिंताओं को अलग करना बहुत आसान बनाता है।
यहाँ एक कार्टून है कि हम Elixir में अपना User सिस्टम कैसे बना सकते हैं:
my_user = %{username: "foo", password: ..., phone: ..., payment_token: ...}
my_user = Authentication.authenticate(my_user)
my_user = Subscription.charge(my_user)
my_user = Contact.export_vcard(my_user)
क्योंकि डेटा कोड से अलग है, कोई मॉड्यूल उस डेटा की विशेषाधिकार प्राप्त व्याख्या नहीं रखता।
इसे Ruby पर वापस लाना
चूंकि यह दृष्टिकोण Elixir में इतना अच्छा काम करता है, तो इसे Ruby में क्यों न अपनाएं? हमें User को इसके डेटा के लिए एक सिंपल रैपर बनाने और सभी बिजनेस लॉजिक को मॉड्यूल्स में खींचने से कुछ भी नहीं रोकता।
class User
attr_accessor :username, :password, :email, :address
end
module Authentication
def self.authenticate!(user)
..
end
end
module Subscription
def self.charge(user)
..
end
end
module Contact
def self.export_vcard(user)
..
end
end
आप User क्लास को एक स्ट्रक्ट में भी बदल सकते हैं, या इसे (किंडा) अपरिवर्तनीय बनाने के लिए कोड जोड़ सकते हैं।
तो क्या?
क्या यह एक तुच्छ परिवर्तन लगता है? क्या बात है?
जैसा कि हमने देखा है, विशिष्ट OO दृष्टिकोण की समस्याएं हैं जैसे-जैसे यह पुराना होता है। यह कहकर कि एक निश्चित क्लास मालिक है डेटा, हम समस्याओं में पड़ जाते हैं जब हमें उस डेटा को किसी अन्य तरीके से उपयोग करने की आवश्यकता होती है।
मॉड्यूलर दृष्टिकोण में, हालांकि, व्यवहार जोड़ना कोई समस्या नहीं है। हम बस एक नया मॉड्यूल बनाते हैं जो डेटा की व्याख्या करता है जिस भी तरीके से उसे आवश्यकता होती है। यह करना आसान है क्योंकि डेटा और कार्यक्षमता पूरी तरह से अलग हैं।
अन्य दृष्टिकोण
जंक-ड्रॉअर समस्या को रोकने के अन्य दृष्टिकोण हैं। पारंपरिक रूप से, ऑब्जेक्ट-ओरिएंटेड प्रोग्रामिंग में आप इनहेरिटेंस और सावधानीपूर्वक रीफैक्टरिंग के माध्यम से ऐसा करने का प्रयास करेंगे। 2012 में सैंडी मेट्ज़ ने Practical Object Oriented Design in Ruby प्रकाशित किया जिसने कई लोगों को डिपेंडेंसी इंजेक्शन का उपयोग करके ऑब्जेक्ट्स को कंपोज करना शुरू करने के लिए आश्वस्त किया। हाल ही में, फंक्शनल प्रोग्रामिंग की लोकप्रियता ने Rubyists को अपरिवर्तनीय "डेटा ऑब्जेक्ट्स" के साथ प्रयोग करने के लिए प्रेरित किया है।
ये सभी दृष्टिकोण क्लीन, सुंदर कोड बनाने के लिए उपयोग किए जा सकते हैं। हालांकि तथ्य यह है कि क्लासेस आमतौर पर डेटा के मालिक हैं इसका मतलब है कि क्लास क्या है और क्लास डेटा के साथ क्या करती है के बीच हमेशा तनाव रहेगा।
मुझे संदेह है कि इन दृष्टिकोणों को जंक-ड्रॉअर समस्या से बचने में जो भी सफलता मिलती है, वह डेटा को कोड से डिकॉपल करने का परिणाम है।