आज की पोस्ट में, हम Facade नाम के एक सॉफ्टवेयर डिजाइन पैटर्न पर गौर करेंगे। जब मैंने पहली बार इसे अपनाया, तो यह थोड़ा अजीब लगा, लेकिन जितना अधिक मैंने इसे अपने रेल ऐप में इस्तेमाल किया, उतना ही मैं इसकी उपयोगिता की सराहना करने लगा। इससे भी महत्वपूर्ण बात यह है कि इसने मुझे अपने कोड का अधिक अच्छी तरह से परीक्षण करने, अपने नियंत्रकों को साफ करने, मेरे विचारों के तर्क को कम करने और मुझे किसी एप्लिकेशन के कोड की समग्र संरचना के बारे में अधिक स्पष्ट रूप से सोचने की अनुमति दी।
एक सॉफ्टवेयर विकास पैटर्न होने के नाते, मुखौटा ढांचा अज्ञेयवादी है लेकिन मैं यहां जो उदाहरण प्रदान करूंगा, वे रूबी ऑन रेल्स के लिए हैं। हालाँकि, मैं आपको इस लेख को पढ़ने और आपके द्वारा उपयोग किए जा रहे ढांचे की परवाह किए बिना उन्हें आज़माने के लिए प्रोत्साहित करता हूँ। मुझे यकीन है कि एक बार जब आप इस पैटर्न से परिचित हो जाएंगे, तो आपको अपने कोडबेस के कई हिस्सों में इसका उपयोग करने के अवसर दिखाई देने लगेंगे।
आगे की हलचल के बिना, आइए इसमें गोता लगाएँ!
एमवीसी पैटर्न के साथ समस्या
MVC (मॉडल-व्यू-कंट्रोलर) पैटर्न एक सॉफ्टवेयर डेवलपमेंट पैटर्न है जो 1970 के दशक का है। यह सॉफ्टवेयर इंटरफेस डिजाइन करने के लिए एक युद्ध-परीक्षण समाधान है, प्रोग्रामिंग चिंताओं को तीन मुख्य समूहों में अलग करता है जो एक दूसरे के बीच एक अद्वितीय तरीके से संवाद करते हैं।
2000 के दशक की शुरुआत में कई बड़े वेब ढांचे एमवीसी पैटर्न के साथ उनकी नींव के रूप में उभरे। स्प्रिंग (जावा के लिए), Django (पायथन के लिए) और रूबी ऑन रेल्स (रूबी के लिए), सभी अपने मूल में परस्पर तत्वों की इस त्रिमूर्ति के साथ जाली थे। सॉफ़्टवेयर से उत्पन्न स्पेगेटी-कोड की तुलना में जो इसका उपयोग नहीं करता था, एमवीसी पैटर्न सॉफ्टवेयर विकास और इंटरनेट दोनों के विकास में एक बड़ी उपलब्धि और महत्वपूर्ण मोड़ था।
संक्षेप में, मॉडल-व्यू-कंट्रोलर पैटर्न निम्नलिखित के लिए अनुमति देता है:एक उपयोगकर्ता दृश्य पर एक क्रिया करता है। दृश्य एक नियंत्रक के लिए एक अनुरोध ट्रिगर करता है जो संभावित रूप से मॉडल बना/पढ़/अपडेट या हटा सकता है। मॉडल लेनदेन नियंत्रक को वापस प्रतिक्रिया करता है, जो बदले में कुछ परिवर्तन प्रस्तुत करता है जो उपयोगकर्ता दृश्य में दिखाई देगा।
इस प्रोग्रामिंग पैटर्न के बहुत सारे पेशेवर हैं। कुछ को सूचीबद्ध करने के लिए:
- यह चिंताओं को अलग करके कोड रखरखाव में सुधार करता है
- यह अधिक परीक्षण क्षमता की अनुमति देता है (मॉडल, दृश्य और नियंत्रकों को अलग-अलग परीक्षण किया जा सकता है)
- यह सॉलिड के एकल उत्तरदायित्व सिद्धांत को लागू करके अच्छे कोडिंग प्रथाओं को प्रोत्साहित करता है:"एक वर्ग के पास बदलने का केवल एक कारण होना चाहिए।"
अपने समय के लिए एक अभूतपूर्व उपलब्धि, डेवलपर्स ने जल्द ही महसूस किया कि एमवीसी पैटर्न भी कुछ हद तक सीमित था। एचएमवीसी (पदानुक्रमित मॉडल-व्यू-कंट्रोलर), एमवीए (मॉडल-व्यू-एडाप्टर), एमवीपी (मॉडल-व्यू-प्रस्तुतकर्ता), एमवीवीएम (मॉडल-व्यू-व्यूमॉडल) और अन्य जैसे वेरिएंट उभरने लगे, जो सभी की मांग थी। MVC पैटर्न की सीमाओं को संबोधित करें।
MVC पैटर्न द्वारा पेश की जाने वाली समस्याओं में से एक और आज के लेख का विषय निम्नलिखित है:जटिल दृश्य तर्क को संभालने के लिए कौन जिम्मेदार है? दृश्य को केवल डेटा प्रस्तुत करने से संबंधित होना चाहिए, नियंत्रक केवल मॉडल से प्राप्त संदेश को रिले कर रहा है, और मॉडल को किसी भी दृश्य तर्क से संबंधित नहीं होना चाहिए।
इस सामान्य पहेली में मदद करने के लिए, सभी रेल अनुप्रयोगों को helpers
. के साथ प्रारंभ किया जाता है निर्देशिका। helper
निर्देशिका में ऐसे तरीकों वाले मॉड्यूल हो सकते हैं जो जटिल व्यू लॉजिक में सहायता करते हैं।
रेल एप्लिकेशन के भीतर एक सहायक का उदाहरण यहां दिया गया है:
app/helpers/application_helper.rb
module ApplicationHelper
def display_ad_type(advertisement)
type = advertisement.ad_type
case type
when 'foo'
content_tag(:span, class: "foo ad-#{type}") { type }
when 'bar'
content_tag(:p, 'bar advertisement')
else
content_tag(:span, class: "badge ads-badge badge-pill ad-#{type}") { type }
end
end
end
यह उदाहरण सरल है लेकिन इस तथ्य को प्रदर्शित करता है कि आप इस तरह के निर्णय लेने की प्रक्रिया को टेम्पलेट से ही निकालना चाहेंगे ताकि इसकी जटिलता को कम किया जा सके।
हेल्पर्स अच्छे हैं, लेकिन जटिल व्यू लॉजिक को संभालने के लिए एक और पैटर्न है जो वर्षों से स्वीकृत हो गया है, और वह है फेकाडे पैटर्न।
मुखौटा पैटर्न का परिचय
रूबी ऑन रेल्स एप्लिकेशन में, आमतौर पर अग्रभागों को app/facades
. के भीतर रखा जाता है निर्देशिका।
जबकि helpers
. के समान , facades
मॉड्यूल के भीतर विधियों का समूह नहीं है। एक मुखौटा एक पोरो (सादा पुरानी रूबी वस्तु) है जिसे नियंत्रक के भीतर तत्काल किया जाता है, लेकिन वह जो विस्तृत व्यापार तर्क देखें। इस प्रकार, यह निम्नलिखित लाभों की अनुमति देता है:
UsersHelper
के लिए एक मॉड्यूल होने के बजाय याArticlesHelper
याBooksHelper
, प्रत्येक नियंत्रक क्रिया का अपना मुखौटा हो सकता है:Users::IndexFacade
,Articles::ShowFacade
,Books::EditFacade
।- मॉड्यूल से कहीं अधिक, फ़ेडेड आपको एकल उत्तरदायित्व सिद्धांत को लागू करने के लिए सुनिश्चित करने के लिए फ़ेडेड को नेस्ट करने की अनुमति देकर अच्छे कोडिंग प्रथाओं को प्रोत्साहित करते हैं। हालांकि आप शायद ऐसे फ़ेसडे नहीं चाहते जो सैकड़ों स्तरों में गहरे नेस्टेड हों, बेहतर रख-रखाव और परीक्षण कवरेज के लिए नेस्टिंग की एक या दो परतें होना एक अच्छी बात हो सकती है।
यहां एक काल्पनिक उदाहरण दिया गया है:
module Books
class IndexFacade
attr_reader :books, :params, :user
def initialize(user:, params:)
@params = params
@user = user
@books = user.books
end
def filtered_books
@filtered_books ||= begin
scope = if query.present?
books.where('name ILIKE ?', "%#{query}%")
elsif isbn.present?
books.where(isbn: isbn)
else
books
end
scope.order(created_at: :desc).page(params[:page])
end
end
def recommended
# We have a nested facade here.
# The `Recommended Books` part of the view has a
# single responsibility so best to extract it
# to improve its encapsulation and testability.
@recommended ||= Books::RecommendedFacade.new(
books: books,
user: user
)
end
private
def query
@query ||= params[:query]
end
def isbn
@isbn ||= params[:isbn]
end
end
end
जब फेकाडे पैटर्न का उपयोग नहीं करना है
आइए एक पल के लिए यह भी सोचें कि कौन से पहलू नहीं हैं।
-
अग्रभाग को उन कक्षाओं में नहीं रखा जाना चाहिए जो रहते हैं, उदाहरण के लिए,
lib
. में कोड के लिए निर्देशिका जिसे दृश्य में प्रदर्शित करने की आवश्यकता है। मुखौटा का जीवनचक्र नियंत्रक क्रिया में उत्पन्न होना चाहिए और इसके संबद्ध दृश्य में उपयोग किया जाना चाहिए। -
सीआरयूडी क्रियाओं को करने के लिए व्यावसायिक तर्क के लिए फ़ेडेड का उपयोग नहीं किया जाता है (उसके लिए अन्य पैटर्न हैं, जैसे कि सेवाएँ या इंटरेक्टर्स-लेकिन यह एक और दिन के लिए एक विषय है।) दूसरे शब्दों में, फ़ेडेड को बनाने से संबंधित नहीं होना चाहिए, अद्यतन करना या हटाना। उनका उद्देश्य व्यू या कंट्रोलर से जटिल प्रस्तुति तर्क निकालना है और उस सारी जानकारी तक पहुँचने के लिए एकल इंटरफ़ेस की पेशकश करना है।
-
अंतिम लेकिन कम से कम, Facades चांदी की गोली नहीं हैं। वे आपको एमवीसी पैटर्न को बायपास करने की अनुमति नहीं देते हैं, बल्कि वे इसके साथ खेलते हैं। यदि किसी मॉडल में कोई परिवर्तन होता है, तो वह तुरंत दृश्य में दिखाई नहीं देगा। जैसा कि हमेशा एमवीसी के मामले में होता है, दृश्य पर परिवर्तन प्रदर्शित करने के लिए फेकाडे के लिए नियंत्रक कार्रवाई को फिर से प्रस्तुत करना होगा।
नियंत्रक लाभ
Facades के मुख्य, स्पष्ट लाभों में से एक यह है कि वे आपको नियंत्रक तर्क को नाटकीय रूप से कम करने की अनुमति देंगे।
आपका कंट्रोलर कोड कुछ इस तरह से कम कर दिया जाएगा:
class BooksController < ApplicationController
def index
@books = if params[:query].present?
current_user.books.where('name ILIKE ?', "%#{params[:query]}%")
elsif params[:isbn].present?
current_user.books.where(isbn: params[:isbn])
else
current_user.books
end
@books.order(created_at: :desc).page(params[:page])
@recommended = @books.where(some_complex_query: true)
end
end
इसके लिए:
class BooksController < ApplicationController
def index
@index_facade = Books::IndexFacade.new(user: current_user, params: params)
end
end
लाभ देखें
दृश्यों के लिए, Facades का उपयोग करते समय दो मुख्य लाभ होते हैं:
- सशर्त जांच, इनलाइन प्रश्नों और अन्य तर्कों को टेम्पलेट से ही बड़े करीने से निकाला जा सकता है जिससे कोड अधिक पठनीय हो जाता है। उदाहरण के लिए, आप इसे एक रूप में उपयोग कर सकते हैं:
<%= f.label :location %>
<%= f.select :location, options_for_select(User::LOCATION_TYPES.map { |type| [type.underscore.humanize, type] }.sort.prepend(['All', 'all'])), multiple: (current_user.active_ips.size > 1 && current_user.settings.use_multiple_locations?) %>
बस बन सकता है:
<%= f.label :location %>
<%= f.select :location, options_for_select(@form_facade.user_locations), multiple: @form_facade.multiple_locations? %>
- वेरिएबल जिन्हें कई बार कॉल किया जाता है उन्हें कैश किया जा सकता है। यह आपके ऐप में महत्वपूर्ण प्रदर्शन सुधार प्रदान कर सकता है और pesky N+1 प्रश्नों को दूर करने में मदद कर सकता है:
// Somewhere in the view, a query is performed.
<% current_user.books.where(isbn: params[:isbn]).each do |book| %>
// Do things
<% end %>
// Somewhere else in the view, the same query is performed again.
<% current_user.books.where(isbn: params[:isbn]).each do |book| %>
// Do things
<% end %>
बन जाएगा:
// Somewhere in the view, a query is performed.
<% @index_facade.filtered_books.each do |book| %>
// Do things
<% end %>
// Somewhere else in the view.
// Second query is not performed due to instance variable caching.
<% @index_facade.filtered_books.each do |book| %>
// Do things
<% end %>
परीक्षण के लाभ
Facades का एक प्रमुख लाभ यह है कि वे आपको एक संपूर्ण नियंत्रक परीक्षण लिखने के बिना, या इससे भी बदतर, व्यावसायिक तर्क के एकवचन बिट्स का परीक्षण करने की अनुमति देते हैं, एक एकीकरण परीक्षण लिखने के बिना जो एक प्रवाह के माध्यम से जाता है और यह सुनिश्चित करने के लिए एक पृष्ठ तक पहुंचता है। डेटा प्रस्तुति अपेक्षित है।
चूंकि आप एकल पोरो का परीक्षण कर रहे होंगे, इससे एक तेज़ परीक्षण सूट बनाए रखने में मदद मिलेगी।
प्रदर्शन के उद्देश्य से मिनिटेस्ट में लिखे गए परीक्षण का एक सरल उदाहरण यहां दिया गया है:
require 'test_helper'
module Books
class IndexFacadeTest < ActiveSupport::TestCase
attr_reader :user, :params
setup do
@user = User.create(first_name: 'Bob', last_name: 'Dylan')
@params = {}
end
test "#filtered_books returns all user's books when params are empty"
index_facade = Books::IndexFacade.new(user: user, params: params)
expectation = user.books.order(created_at: :desc).page(params[:page])
# Without writing an entire controller test or
# integration test, we can check whether using the facade with
# empty parameters will return the correct results
# to the user.
assert_equal expectation, index_facade.filtered_books
end
test "#filtered_books returns books matching a query"
@params = { query: 'Lord of the Rings' }
index_facade = Books::IndexFacade.new(user: user, params: params)
expectation = user
.books
.where('name ILIKE ?', "%#{params[:query]}%")
.order(created_at: :desc)
.page(params[:page])
assert_equal expectation, index_facade.filtered_books
end
end
end
यूनिट परीक्षण के पहलू परीक्षण सूट के प्रदर्शन में काफी सुधार करते हैं, और हर बड़ी कंपनी को अंततः धीमी परीक्षण सूट का सामना करना पड़ेगा, जब तक कि इस तरह की समस्याओं को कुछ स्तर की गंभीरता से संबोधित नहीं किया जाता है।
एक मुखौटा, दो पहलू, तीन पहलू, अधिक?
आप एक परिदृश्य का सामना कर सकते हैं जहां एक दृश्य आंशिक रूप से प्रस्तुत करता है जो कुछ डेटा आउटपुट करता है। उस स्थिति में, आपके पास या तो मूल मुखौटा का उपयोग करने या नेस्टेड मुखौटा का उपयोग करने का विकल्प होता है। यह काफी हद तक इस बात पर निर्भर करता है कि तर्क कितना शामिल है, क्या आप इसे अलग से परीक्षण करना चाहते हैं और क्या यह कार्यक्षमता निकालने के लिए समझ में आता है।
कितने अग्रभागों का उपयोग करना है या कितने अग्रभागों को एक-दूसरे के भीतर घोंसला बनाना है, इसके लिए कोई सुनहरा नियम नहीं है। यह डेवलपर के विवेक पर है। मैं आम तौर पर नियंत्रक कार्रवाई के लिए एक ही मुखौटा रखना पसंद करता हूं और कोड को पालन करने में आसान बनाने के लिए मैं घोंसले को एक स्तर तक सीमित करता हूं।
यहां कुछ सामान्य प्रश्न दिए गए हैं जो आप विकास के दौरान स्वयं से पूछ सकते हैं:
- क्या अग्रभाग उस तर्क को समाहित करता है जिसे मैं दृश्य पर प्रस्तुत करने का प्रयास कर रहा हूं?
- क्या इस संदर्भ में मुखौटा के भीतर का तरीका समझ में आता है?
- क्या अब कोड का अनुसरण करना आसान है, या अनुसरण करना कठिन है?
जब संदेह हो, तो हमेशा अपने कोड को यथासंभव आसान बनाने का प्रयास करें।
निष्कर्ष
अंत में, कोड रखरखाव, प्रदर्शन और परीक्षण क्षमता में सुधार करते हुए, आपके नियंत्रकों और विचारों को दुबला रखने के लिए मुखौटा एक शानदार पैटर्न है।
हालांकि, किसी भी प्रोग्रामिंग प्रतिमान की तरह, कोई चांदी की गोली नहीं है। यहां तक कि हाल के वर्षों (HMVC, MVVM, आदि) में उभरे कई पैटर्न भी सॉफ़्टवेयर विकास की जटिलताओं का संपूर्ण समाधान नहीं हैं।
ऊष्मप्रवैगिकी के दूसरे नियम के समान, जिसमें कहा गया है कि एक बंद प्रणाली में एन्ट्रापी की स्थिति हमेशा बढ़ेगी, इसलिए किसी भी सॉफ्टवेयर प्रोजेक्ट में भी समय के साथ जटिलता बढ़ती और विकसित होती है। लंबे समय में, लक्ष्य कोड लिखना है जो जितना संभव हो सके पढ़ने, परीक्षण, रखरखाव और पालन करने में आसान हो; अग्रभाग बिल्कुल यही पेशकश करते हैं।
पी.एस. यदि आप रूबी मैजिक की पोस्ट प्रेस से बाहर होते ही पढ़ना चाहते हैं, तो हमारे रूबी मैजिक न्यूजलेटर की सदस्यता लें और एक भी पोस्ट मिस न करें!