Ruby Language
रूबी में डिजाइन पैटर्न और मुहावरे
खोज…
एकाकी वस्तु
रूबी स्टैंडर्ड लाइब्रेरी में एक सिंगलटन मॉड्यूल है जो सिंगलटन पैटर्न को लागू करता है। एक सिंगलटन क्लास बनाने के लिए पहला कदम एक क्लास में Singleton
मॉड्यूल की आवश्यकता और उसे शामिल करना है:
require 'singleton'
class Logger
include Singleton
end
यदि आप सामान्य रूप से एक नियमित वर्ग के रूप में इस वर्ग को तत्काल करने की कोशिश करते हैं, तो NoMethodError
अपवाद उठाया जाता है। अन्य उदाहरणों को गलती से बनाए जाने से रोकने के लिए निर्माणकर्ता को निजी बनाया जाता है:
Logger.new
#=> NoMethodError: private method `new' called for AppConfig:Class
इस वर्ग के उदाहरण तक पहुँचने के लिए, हमें instance()
का उपयोग करने की आवश्यकता है:
first, second = Logger.instance, Logger.instance
first == second
#=> true
लकड़हारा उदाहरण
require 'singleton'
class Logger
include Singleton
def initialize
@log = File.open("log.txt", "a")
end
def log(msg)
@log.puts(msg)
end
end
Logger
वस्तु का उपयोग करने के लिए:
Logger.instance.log('message 2')
सिंगलटन के बिना शामिल हैं
उपरोक्त सिंगलटन कार्यान्वयन सिंग्लटन मॉड्यूल के समावेश के बिना भी किया जा सकता है। यह निम्नलिखित के साथ प्राप्त किया जा सकता है:
class Logger
def self.instance
@instance ||= new
end
end
जो निम्नलिखित के लिए एक आशुलिपि अंकन है:
class Logger
def self.instance
@instance = @instance || Logger.new
end
end
हालांकि, ध्यान रखें कि सिंगलटन मॉड्यूल का परीक्षण और अनुकूलन किया जाता है, इसलिए आपके सिंगलटन को लागू करने के लिए बेहतर विकल्प है।
देखने वाला
ऑब्जर्वर पैटर्न एक सॉफ्टवेयर डिज़ाइन पैटर्न है, जिसमें एक ऑब्जेक्ट (जिसे subject
कहा जाता subject
) अपने आश्रितों ( observers
बुलाया जाता subject
) की एक सूची रखता है, और उन्हें किसी भी राज्य के परिवर्तनों के बारे में स्वचालित रूप से सूचित करता है, आमतौर पर उनके तरीकों में से एक को कॉल करके।
रूबी ऑब्जर्वर डिजाइन पैटर्न को लागू करने के लिए एक सरल तंत्र प्रदान करता है। मॉड्यूल Observable
ऑब्जर्वेबल ऑब्जेक्ट में किसी भी परिवर्तन के ग्राहक को सूचित करने के लिए तर्क प्रदान करता है।
काम करने के लिए, अवलोकन करने वाले को यह बदलना पड़ता है कि वे पर्यवेक्षकों को बदल दें और सूचित करें।
अवलोकन करने वाली वस्तुओं को एक update()
पद्धति लागू करनी होगी, जो ऑब्जर्वर के लिए कॉलबैक होगी।
आइए एक छोटी सी चैट लागू करें, जहां उपयोगकर्ता उपयोगकर्ताओं की सदस्यता ले सकते हैं और जब उनमें से कोई कुछ लिखता है, तो ग्राहक अधिसूचित हो जाते हैं।
require "observer"
class Moderator
include Observable
def initialize(name)
@name = name
end
def write
message = "Computer says: No"
changed
notify_observers(message)
end
end
class Warner
def initialize(moderator, limit)
@limit = limit
moderator.add_observer(self)
end
end
class Subscriber < Warner
def update(message)
puts "#{message}"
end
end
moderator = Moderator.new("Rupert")
Subscriber.new(moderator, 1)
moderator.write
moderator.write
निम्नलिखित उत्पादन का उत्पादन:
# Computer says: No
# Computer says: No
हमने मॉडरेट वर्ग में write
की विधि को दो बार ट्रिगर किया है, अपने ग्राहकों को सूचित करते हुए, इस मामले में सिर्फ एक।
हम जितने अधिक सब्सक्राइबर जोड़ेंगे, उतने अधिक परिवर्तन होंगे।
डेकोरेटर पैटर्न
डेकोरेटर पैटर्न वस्तुओं को एक ही वर्ग की अन्य वस्तुओं को प्रभावित किए बिना व्यवहार जोड़ता है। डेकोरेटर पैटर्न उप-कक्षाएं बनाने के लिए एक उपयोगी विकल्प है।
प्रत्येक डेकोरेटर के लिए एक मॉड्यूल बनाएं। यह दृष्टिकोण वंशानुक्रम की तुलना में अधिक लचीला है क्योंकि आप अधिक संयोजनों में जिम्मेदारियों को मिला सकते हैं और मेल कर सकते हैं। इसके अतिरिक्त, क्योंकि पारदर्शिता सज्जाकारों को पुनरावृत्ति होने की अनुमति देती है, यह असीमित संख्या में जिम्मेदारियों के लिए अनुमति देता है।
मान लें कि पिज्जा क्लास में एक लागत विधि है जो 300 रिटर्न देती है:
class Pizza
def cost
300
end
end
पिज्जा को पनीर की एक अतिरिक्त परत के साथ PizzaWithCheese
और लागत 50 से अधिक हो जाती है। सबसे आसान तरीका यह है कि PizzaWithCheese
उप-वर्ग बनाएं जो लागत विधि में 350 लौटाता है।
class PizzaWithCheese < Pizza
def cost
350
end
end
अगला, हमें एक बड़े पिज्जा का प्रतिनिधित्व करने की आवश्यकता है जो एक सामान्य पिज्जा की लागत में 100 जोड़ता है। हम पिज्जा के लार्जपाइरा उपवर्ग का उपयोग करके इसका प्रतिनिधित्व कर सकते हैं।
class LargePizza < Pizza
def cost
400
end
end
हमारे पास एक एक्स्ट्रालार्जपाइन्जर भी हो सकता है जो हमारे लार्जपिंजरा को 15 से आगे की लागत देता है। अगर हम विचार करें कि इन पिज्जा प्रकारों को पनीर के साथ परोसा जा सकता है, तो हमें लार्जपाइजरविथचैज और एक्स्ट्रालार्जेज पिज्ज़ाविथेक उपवर्गों को जोड़ना होगा। यह कुल 6 वर्गों के साथ समाप्त होगा।
दृष्टिकोण को सरल बनाने के लिए, पिज्जा क्लास में व्यवहार को गतिशील रूप से जोड़ने के लिए मॉड्यूल का उपयोग करें:
मॉड्यूल + विस्तार + सुपर डेकोरेटर: ->
class Pizza
def cost
300
end
end
module CheesePizza
def cost
super + 50
end
end
module LargePizza
def cost
super + 100
end
end
pizza = Pizza.new #=> cost = 300
pizza.extend(CheesePizza) #=> cost = 350
pizza.extend(LargePizza) #=> cost = 450
pizza.cost #=> cost = 450
प्रतिनिधि
प्रॉक्सी ऑब्जेक्ट का उपयोग अक्सर किसी अन्य ऑब्जेक्ट तक पहरा देने को सुनिश्चित करने के लिए किया जाता है, जो आंतरिक व्यावसायिक तर्क हम सुरक्षा आवश्यकताओं के साथ प्रदूषित नहीं करना चाहते हैं।
मान लीजिए कि हम गारंटी देना चाहते हैं कि विशिष्ट अनुमतियों का केवल उपयोगकर्ता ही संसाधन तक पहुँच सकता है।
प्रॉक्सी परिभाषा: (यह सुनिश्चित करता है कि केवल वे उपयोगकर्ता जो वास्तव में आरक्षण देख सकते हैं उपभोक्ता आरक्षण_ सेवा में सक्षम होंगे)
class Proxy
def initialize(current_user, reservation_service)
@current_user = current_user
@reservation_service = reservation_service
end
def highest_total_price_reservations(date_from, date_to, reservations_count)
if @current_user.can_see_reservations?
@reservation_service.highest_total_price_reservations(
date_from,
date_to,
reservations_count
)
else
[]
end
end
end
मॉडल और आरक्षण सेवा:
class Reservation
attr_reader :total_price, :date
def initialize(date, total_price)
@date = date
@total_price = total_price
end
end
class ReservationService
def highest_total_price_reservations(date_from, date_to, reservations_count)
# normally it would be read from database/external service
reservations = [
Reservation.new(Date.new(2014, 5, 15), 100),
Reservation.new(Date.new(2017, 5, 15), 10),
Reservation.new(Date.new(2017, 1, 15), 50)
]
filtered_reservations = reservations.select do |reservation|
reservation.date.between?(date_from, date_to)
end
filtered_reservations.take(reservations_count)
end
end
class User
attr_reader :name
def initialize(can_see_reservations, name)
@can_see_reservations = can_see_reservations
@name = name
end
def can_see_reservations?
@can_see_reservations
end
end
ग्राहक सेवा:
class StatsService
def initialize(reservation_service)
@reservation_service = reservation_service
end
def year_top_100_reservations_average_total_price(year)
reservations = @reservation_service.highest_total_price_reservations(
Date.new(year, 1, 1),
Date.new(year, 12, 31),
100
)
if reservations.length > 0
sum = reservations.reduce(0) do |memo, reservation|
memo + reservation.total_price
end
sum / reservations.length
else
0
end
end
end
परीक्षा:
def test(user, year)
reservations_service = Proxy.new(user, ReservationService.new)
stats_service = StatsService.new(reservations_service)
average_price = stats_service.year_top_100_reservations_average_total_price(year)
puts "#{user.name} will see: #{average_price}"
end
test(User.new(true, "John the Admin"), 2017)
test(User.new(false, "Guest"), 2017)
लाभ
- जब एक्सेस प्रतिबंध बदले जाते हैं, तो हम
ReservationService
में किसी भी बदलाव से बचते हैं। - हम व्यापार से संबंधित डेटा (मिश्रण नहीं कर रहे हैं
date_from
,date_to
,reservations_count
सेवा में डोमेन असंबंधित अवधारणाओं (उपयोगकर्ता अनुमतियों) के साथ)। - उपभोक्ता (
StatsService
) अनुमति संबंधी तर्क से भी मुक्त है
चेतावनियां
- प्रॉक्सी इंटरफ़ेस हमेशा वैसा ही होता है जैसा कि वह ऑब्जेक्ट छुपाता है, ताकि प्रॉक्सी द्वारा लिपटे हुए सेवा का उपभोग करने वाले उपयोगकर्ता को प्रॉक्सी की उपस्थिति के बारे में पता न चले।