मोटे मॉडल को बनाए रखना मुश्किल होता है। वे, बेशक, तर्क से बेहतर डोमेन तर्क के साथ बरबाद होते हैं, लेकिन, एक नियम के रूप में,
एकल जिम्मेदारी सिद्धांत (SRP) का उल्लंघन करते हैं। "उपयोगकर्ता जो कुछ भी करता है" एक ही जिम्मेदारी नहीं है।
परियोजना की शुरुआत में, एसआरपी का पालन करना आसान है। लेकिन समय के साथ, मॉडल व्यावसायिक तर्क के लिए एक वास्तविक जगह बन जाते हैं। और दो साल बाद, उपयोगकर्ता मॉडल में 500 से अधिक लाइनें और 50 तरीके सार्वजनिक रूप से हैं।
डिजाइन का लक्ष्य बढ़ते हुए अनुप्रयोग को छोटी छोटी वस्तुओं और मॉड्यूल में रखना है। फैट मॉडल, पतला नियंत्रक रिफैक्टिंग में पहला कदम है, तो चलो दूसरा करते हैं।
आप शायद सोचते हैं कि रेल में OOP का उपयोग करना कठिन है। मैंने भी ऐसा सोचा। लेकिन कुछ स्पष्टीकरण और प्रयोग के बाद, मुझे एहसास हुआ कि रेल वास्तव में OOP के साथ खिलवाड़ नहीं करता है। रेल सम्मेलनों को बदलने के लायक नहीं है। लेकिन हम ओओपी और सर्वोत्तम प्रथाओं का उपयोग कर सकते हैं जहां रेल में समझौते नहीं होते हैं।
मॉडल को मॉड्यूल में न तोड़ें
इसके बिना चलते हैं। यदि वे केवल एक मॉडल से जुड़ते हैं तो मुझे मॉड्यूल में विधियाँ डालने की स्वीकृति नहीं है। इस तरह से मॉड्यूल का उपयोग करना कमरे में सभी चीजों को बिस्तर के नीचे और कोठरी में धकेलने जैसा है। बेशक, मॉडल में कोड छोटा हो जाता है, लेकिन इस तरह के कोड को डीबग करना और रिफैक्ट करना मुश्किल है।
अब रिफलेक्टरिंग के बारे में।
1. मूल्य वस्तुओं को हाइलाइट करें
मूल्य वस्तुएं मात्राओं के भंडारण के लिए सरल
वस्तुएं हैं, जैसे कि धन या तिथियों की एक श्रृंखला, जिनमें से समानता उनके मूल्यों पर निर्भर करती है। तिथि, URI, Pathname रूबी मानक पुस्तकालय से उदाहरण हैं, लेकिन आप अपने स्वयं को परिभाषित कर सकते हैं।
यदि आपके पास कई विशेषताएँ और उनसे संबद्ध तर्क हैं, तो रेल वैल्यू ऑब्जेक्ट एक बढ़िया समाधान है। उदाहरण के लिए, मेरे एसएमएस एक्सचेंज में फोननंबर था। ऑनलाइन स्टोर के लिए पैसा चाहिए। कोड जलवायु की एक रेटिंग है - वर्ग रेटिंग। मैं स्ट्रिंग का उपयोग कर सकता हूं, लेकिन रेटिंग में डेटा और तर्क दोनों को परिभाषित किया गया है:
class Rating include Comparable def self.from_cost(cost) if cost <= 2 new("A") elsif cost <= 4 new("B") elsif cost <= 8 new("C") elsif cost <= 16 new("D") else new("F") end end def initialize(letter) @letter = letter end def better_than?(other) self > other end def <=>(other) other.to_s <=> to_s end def hash @letter.hash end def eql?(other) to_s == other.to_s end def to_s @letter.to_s end end
और कॉन्स्टैंटस्नापशॉट की रेटिंग है:
class ConstantSnapshot < ActiveRecord::Base
इस दृष्टिकोण के लाभ:
- ConstantSnapshot से लिया गया रेटिंग तर्क
- # शश और # अक्ल के तरीकों से? आप हैश के रूप में रेटिंग का उपयोग कर सकते हैं। कोड जलवायु Enumerable # group_by का उपयोग करके कक्षाओं को रैंक करने के लिए इसका उपयोग करता है।
2. हाइलाइट सेवा ऑब्जेक्ट
मैं सेवा ऑब्जेक्ट बनाता हूँ यदि क्रिया:
- कठिन (उदाहरण के लिए, लेखा अवधि के बाद सभी पुस्तकों को बंद करना)
- कई मॉडल का उपयोग करता है (उदाहरण के लिए, ऑर्डर, क्रेडिट कार्ड और ग्राहक वस्तुओं का उपयोग करके एक ऑनलाइन स्टोर खरीद)
- एक बाहरी सेवा के साथ बातचीत है (उदाहरण के लिए, सोशल नेटवर्क एपीआई का उपयोग करके)
- यह वास्तव में एक मॉडल से संबंधित नहीं है (उदाहरण के लिए, सभी अप्रचलित डेटा को हटाना)।
- एक से अधिक तरीकों से प्रदर्शन किया जा सकता है (उदाहरण के लिए, उपयोगकर्ता प्रमाणीकरण)। यह एक रणनीति पैटर्न है GoF।
उदाहरण के लिए, आप UserAuthenticator में उपयोगकर्ता # प्रामाणिक विधि बना सकते हैं:
class UserAuthenticator def initialize(user) @user = user end def authenticate(unencrypted_password) return false unless @user if BCrypt::Password.new(@user.password_digest) == unencrypted_password @user else false end end end
और SessionController इस तरह दिखेगा:
class SessionsController < ApplicationController def create user = User.where(email: params[:email]).first if UserAuthenticator.new(user).authenticate(params[:password]) self.current_user = user redirect_to dashboard_path else flash[:alert] = "Login failed." render "new" end end end
3. फॉर्म ऑब्जेक्ट चुनें
एक फॉर्म सबमिट करते समय कई मॉडल बदल जाते हैं, तर्क को फॉर्म ऑब्जेक्ट में ले जाया जा सकता है। यह
एक्सेप्ट_नरेस्टेड_एट्यूएंट्स_फोर्स की तुलना में बहुत अधिक क्लीनर है, जिसे
आईएमएचओ , आमतौर पर हटाने की जरूरत है। यहाँ पंजीकरण फॉर्म का एक उदाहरण दिया गया है जिसे कंपनी और उपयोगकर्ता बनाते हैं:
class Signup include Virtus extend ActiveModel::Naming include ActiveModel::Conversion include ActiveModel::Validations attr_reader :user attr_reader :company attribute :name, String attribute :company_name, String attribute :email, String validates :email, presence: true
मैंने ActiveRecord जैसे व्यवहार के साथ विशेषताएँ प्राप्त करने के लिए
वर्टस का उपयोग किया। नियंत्रक में, मैं यह कर सकता हूँ:
class SignupsController < ApplicationController def create @signup = Signup.new(params[:signup]) if @signup.save redirect_to dashboard_path else render "new" end end end
साधारण मामलों के लिए, यह इस तरह से काम करता है। यदि डेटा सहेजने का तर्क जटिल है, तो आप इस दृष्टिकोण को सेवा ऑब्जेक्ट के साथ जोड़ सकते हैं। एक बोनस के रूप में: यहाँ आप मान्यताओं को रख सकते हैं, और स्मीयर मॉडल सत्यापन को नहीं।
4. क्वेरी ऑब्जेक्ट हाइलाइट करें
आपके मॉडल को कम करने वाले जटिल SQL प्रश्नों के लिए, क्वेरी ऑब्जेक्ट हाइलाइट करें। प्रत्येक क्वेरी ऑब्जेक्ट एक व्यावसायिक नियम निष्पादित करता है। उदाहरण के लिए, एक क्वेरी ऑब्जेक्ट जो परित्यक्त खातों को लौटाता है:
class AbandonedTrialQuery def initialize(relation = Account.scoped) @relation = relation end def find_each(&block) @relation. where(plan: nil, invites_count: 0).find_each(&block) end end
मेल भेजने के लिए पृष्ठभूमि नौकरी में इस्तेमाल किया जा सकता है:
AbandonedTrialQuery.new.find_each do |account| account.send_offer_for_support end
ActiveRecord :: संबंध रेल 3 में प्रथम श्रेणी की वस्तुएं हैं, इसलिए उन्हें क्वेरी ऑब्जेक्ट में इनपुट पैरामीटर के रूप में पारित किया जा सकता है। और हम संबंध और क्वेरी ऑब्जेक्ट के संयोजन का उपयोग कर सकते हैं:
old_accounts = Account.where("created_at < ?", 1.month.ago) old_abandoned_trials = AbandonedTrialQuery.new(old_accounts)
ऐसी कक्षाओं के पृथक परीक्षण से दूर न हों। परीक्षणों में ऑब्जेक्ट और डेटाबेस दोनों का उपयोग करके सुनिश्चित करें कि उत्तर सही है और N + 1 SQL क्वेरी जैसे कोई अप्रत्याशित प्रभाव नहीं हैं।
5. देखें वस्तुओं का चयन करें
यदि कोई विधि केवल डेटा प्रदर्शित करने के लिए आवश्यक है, तो यह मॉडल से संबंधित नहीं होना चाहिए। अपने आप से पूछें: "यदि आवेदन में उदाहरण के लिए, एक आवाज इंटरफ़ेस है, तो क्या इस विधि की आवश्यकता होगी?"। यदि नहीं, तो इसे सहायक या दृश्य वस्तु पर ले जाएं।
उदाहरण के लिए, कोड जलवायु में एक डोनट चार्ट एक परियोजना में सभी वर्गों की रेटिंग दिखाता है (उदाहरण के लिए,
कोड जलवायु पर रेल ), और परियोजना कोड के एक स्नैपशॉट पर आधारित है:
class DonutChart def initialize(snapshot) @snapshot = snapshot end def cache_key @snapshot.id.to_s end def data
सबसे अधिक बार, एक व्यू ऑब्जेक्ट के साथ, मेरे पास एक ईआरबी टेम्पलेट (एचएएमएल / एसएलआईएम) है। इसलिए, अब मैं पटरियों में
दो चरण दृश्य पैटर्न के आवेदन को समझता हूं।
6. हाइलाइट नीति ऑब्जेक्ट
कभी-कभी जटिल रीड ऑपरेशन अपनी वस्तुओं के लायक होते हैं। इस मामले में, मैं एक पॉलिसी ऑब्जेक्ट बनाता हूं। यह आपको उन मॉडलों से तर्क बनाने की अनुमति देता है जो सीधे मॉडल से संबंधित नहीं हैं। उदाहरण के लिए, जो उपयोगकर्ता सक्रिय हैं:
class ActiveUserPolicy def initialize(user) @user = user end def active? @user.email_confirmed? && @user.last_login_at > 14.days.ago end end
पॉलिसी ऑब्जेक्ट एक व्यावसायिक नियम का वर्णन करता है: एक उपयोगकर्ता को सक्रिय माना जाता है यदि उसके मेल की पुष्टि की जाती है और उसने दो सप्ताह पहले पहले लॉग इन किया था। आप व्यावसायिक नियमों के एक सेट के लिए नीति ऑब्जेक्ट का उपयोग कर सकते हैं, जैसे कि एक लेखक, जो बताता है कि उपयोगकर्ता के पास कौन सा डेटा है।
नीति ऑब्जेक्ट सेवा ऑब्जेक्ट के समान होते हैं, लेकिन मैं लिखने के संचालन और पढ़ने के लिए नीति ऑब्जेक्ट के लिए सेवा ऑब्जेक्ट का उपयोग करता हूं। वे क्वेरी ऑब्जेक्ट्स के समान हैं, लेकिन क्वेरी ऑब्जेक्ट SQL क्वेरी निष्पादित करते हैं, और पॉलिसी ऑब्जेक्ट स्मृति में लोड किए गए मॉडल का उपयोग करते हैं।
7. हाइलाइट डेकोरेटर्स
सज्जाकार आपको मौजूदा तरीकों का उपयोग करने की अनुमति देते हैं: वे कॉलबैक के समान हैं। सज्जाकार उन मामलों में उपयोगी होते हैं जहां एक कॉलबैक को कुछ शर्तों के तहत निष्पादित किया जाना चाहिए या यदि यह मॉडल में शामिल है तो यह प्रदूषित करता है।
ब्लॉग पर लिखी गई टिप्पणी को टिप्पणी लेखक के फेसबुक वॉल पर पोस्ट किया जा सकता है, लेकिन इसका मतलब यह नहीं है कि इस तर्क को टिप्पणी वर्ग में परिभाषित किया जाना चाहिए। एक संकेत है कि आपके पास बहुत अधिक कॉलबैक हैं धीमी और नाजुक परीक्षण या कई स्थानों पर इन कॉलबैक को स्थिर करने की आवश्यकता है।
उदाहरण के लिए, डेकोरेटर में फेसबुक पर पोस्ट करने के तर्क को बाहर कैसे करें:
class FacebookCommentNotifier def initialize(comment) @comment = comment end def save @comment.save && post_to_wall end private def post_to_wall Facebook.post(title: @comment.title, user: @comment.author) end end
और नियंत्रक में:
class CommentsController < ApplicationController def create @comment = FacebookCommentNotifier.new(Comment.new(params[:comment])) if @comment.save redirect_to blog_path, notice: "Your comment was posted." else render "new" end end end
सज्जाकार सेवा की वस्तुओं से भिन्न होते हैं, जिसमें वे मौजूदा तरीकों का उपयोग करते हैं। FacebookCommentNotifier इंस्टेंसेस का इस्तेमाल उसी तरह से किया जाता है जैसे कि कमेंट इंस्टेंस में किया जाता है। रूबी मेटाप्रोग्रामिंग का उपयोग करके
डेकोरेटर बनाना आसान बनाता है।
निष्कर्ष में
रेल अनुप्रयोगों में मॉडल में जटिलता के प्रबंधन के लिए कई तकनीकें हैं। वे पटरियों की जगह नहीं लेते हैं। ActiveRecord एक महान पुस्तकालय है, लेकिन आपको केवल इस पर भरोसा नहीं करना चाहिए। मॉडल से कुछ तर्क निकालने के लिए इन तकनीकों का उपयोग करने का प्रयास करें, और आपके एप्लिकेशन आसान हो जाएंगे।
आप देख सकते हैं कि ये पैटर्न काफी सरल हैं। ऑब्जेक्ट्स केवल रूबी ऑब्जेक्ट्स हैं। यह वही है जो मैं आपको बताना चाहता था। सभी कार्यों को एक ढांचे या पुस्तकालयों के साथ हल करने की आवश्यकता नहीं है।
मूल लेख
यहाँ है ।