इस लेख में, आप कुछ संवर्द्धन के साथ .NET C# में ऑब्जर्वर डिज़ाइन पैटर्न के बारे में जानेंगे।
पर्यवेक्षक डिजाइन पैटर्न परिभाषा
ऑब्जर्वर डिज़ाइन पैटर्न सबसे महत्वपूर्ण और आमतौर पर इस्तेमाल किए जाने वाले डिज़ाइन पैटर्न में से एक है।
सबसे पहले, ऑब्जर्वर डिज़ाइन पैटर्न की औपचारिक परिभाषा की जाँच करें।
के अनुसार
प्रेक्षक डिजाइन पैटर्न एक ग्राहक को एक प्रदाता के साथ पंजीकरण करने और सूचनाएं प्राप्त करने में सक्षम बनाता है। यह किसी भी परिदृश्य के लिए उपयुक्त है जिसके लिए पुश-आधारित अधिसूचना की आवश्यकता होती है। पैटर्न एक प्रदाता (एक विषय या एक अवलोकन योग्य के रूप में भी जाना जाता है) और शून्य, एक या अधिक पर्यवेक्षकों को परिभाषित करता है। पर्यवेक्षक प्रदाता के साथ पंजीकरण करते हैं, और जब भी कोई पूर्वनिर्धारित स्थिति, घटना या राज्य परिवर्तन होता है, तो प्रदाता स्वचालित रूप से सभी पर्यवेक्षकों को उनके तरीकों में से एक को कॉल करके सूचित करता है। इस पद्धति कॉल में, प्रदाता पर्यवेक्षकों को वर्तमान स्थिति की जानकारी भी प्रदान कर सकता है। .NET में, सामान्य System.IObservable<T> और System.IObserver<T> इंटरफेस को लागू करके पर्यवेक्षक डिजाइन पैटर्न लागू किया जाता है। सामान्य प्रकार का पैरामीटर उस प्रकार का प्रतिनिधित्व करता है जो अधिसूचना जानकारी प्रदान करता है।
तो, ऊपर दी गई परिभाषा से, हम निम्नलिखित को समझ सकते हैं:
- हमारे पास दो दल या मॉड्यूल हैं।
- मॉड्यूल जिसमें प्रदान करने के लिए जानकारी की कुछ धारा है। इस मॉड्यूल को प्रदाता कहा जाता है (क्योंकि यह जानकारी प्रदान करता है), या विषय (क्योंकि यह बाहरी दुनिया को जानकारी देता है), या ऑब्जर्वेबल (जैसा कि यह बाहरी दुनिया द्वारा देखा जा सकता है)।
- मॉड्यूल जो कहीं और से आने वाली जानकारी की धारा में रूचि रखता है। इस मॉड्यूल को ऑब्जर्वर कहा जाता है (क्योंकि यह सूचना का अवलोकन करता है)।
पर्यवेक्षक डिजाइन पैटर्न के लाभ
जैसा कि हम अब जानते हैं, ऑब्जर्वर डिज़ाइन पैटर्न ऑब्जर्वेबल और ऑब्जर्वर मॉड्यूल के बीच संबंध बनाता है। ऑब्जर्वर डिज़ाइन पैटर्न अद्वितीय बनाता है कि इसका उपयोग करके आप इसे कसकर युग्मित संबंध के बिना प्राप्त कर सकते हैं।
पैटर्न के काम करने के तरीके का विश्लेषण करने पर, आप निम्नलिखित पाएंगे:
- ऑब्जर्वेबल ऑब्जर्वर के बारे में आवश्यक न्यूनतम जानकारी जानता है।
- ऑब्जर्वर ऑब्जर्वेबल के बारे में आवश्यक न्यूनतम जानकारी जानता है।
- यहां तक कि आपसी ज्ञान भी अमूर्त के माध्यम से हासिल किया जाता है, ठोस कार्यान्वयन नहीं।
- अंत में, दोनों मॉड्यूल अपना काम कर सकते हैं, और केवल अपना काम कर सकते हैं।
सार प्रयुक्त
ये .NET C# में ऑब्जर्वर डिज़ाइन पैटर्न को लागू करने के लिए उपयोग किए जाने वाले सार हैं।
आईओ ऑब्जर्वेबल <आउट टी>
यह एक Covariant इंटरफ़ेस है जो किसी भी ऑब्जर्वेबल का प्रतिनिधित्व करता है। यदि आप .NET में Variance के बारे में अधिक जानना चाहते हैं, तो आप इस लेख को देख सकते हैं
इस इंटरफ़ेस में परिभाषित सदस्य हैं:
public IDisposable Subscribe (IObserver<out T> observer);
Subscribe
विधि को ऑब्जर्वेबल को सूचित करने के लिए कहा जाना चाहिए कि कुछ ऑब्जर्वर इसकी जानकारी की धारा में रुचि रखते हैं।
Subscribe
विधि एक वस्तु लौटाती है जो IDisposable
इंटरफ़ेस को लागू करती है। ऑब्जर्वर द्वारा इस वस्तु का उपयोग ऑब्जर्वेबल द्वारा प्रदान की गई जानकारी की धारा से सदस्यता समाप्त करने के लिए किया जा सकता है। एक बार यह हो जाने के बाद, पर्यवेक्षक को सूचना की धारा में किसी भी अद्यतन के बारे में सूचित नहीं किया जाएगा।
IOऑब्जर्वर <टी में>
यह किसी भी ऑब्जर्वर का प्रतिनिधित्व करने वाला एक कॉन्ट्रावैरिएंट इंटरफ़ेस है। यदि आप .NET में Variance के बारे में अधिक जानना चाहते हैं, तो आप इस लेख को देख सकते हैं
इस इंटरफ़ेस में परिभाषित सदस्य हैं:
public void OnCompleted (); public void OnError (Exception error); public void OnNext (T value);
ऑब्जर्वर को सूचित करने के लिए कि सूचना की धारा पूरी हो गई है और ऑब्जर्वर को और अधिक जानकारी की उम्मीद नहीं करनी चाहिए, OnCompleted
विधि को ऑब्जर्वेबल द्वारा बुलाया जाना चाहिए।
पर्यवेक्षक को यह सूचित करने के लिए कि एक त्रुटि हुई है, OnError
विधि को ऑब्जर्वेबल द्वारा बुलाया जाना चाहिए।
ऑब्जर्वर को सूचित करने के लिए ऑब्जर्वेबल द्वारा OnNext
विधि को कॉल किया जाना चाहिए कि जानकारी का एक नया टुकड़ा तैयार है और स्ट्रीम में जोड़ा जा रहा है।
माइक्रोसॉफ्ट का कार्यान्वयन
अब, देखते हैं कि कैसे Microsoft C# में ऑब्जर्वर डिज़ाइन पैटर्न को लागू करने की अनुशंसा करता है। बाद में, मैं आपको कुछ छोटे एन्हांसमेंट दिखाऊंगा जिन्हें मैंने स्वयं लागू किया था।
हम एक साधारण मौसम पूर्वानुमान कंसोल एप्लिकेशन बनाएंगे। इस एप्लिकेशन में, हमारे पास वेदरफोरकास्ट मॉड्यूल (ऑब्जर्वेबल, प्रोवाइडर, सब्जेक्ट) और वेदरफोरकास्ट ऑब्जर्वर मॉड्यूल (ऑब्जर्वर) होगा।
तो, आइए कार्यान्वयन पर गौर करना शुरू करें।
मौसम की जानकारी
namespace Observable { public class WeatherInfo { internal WeatherInfo(double temperature) { Temperature = temperature; } public double Temperature { get; } } }
यह सूचना धारा में प्रवाहित होने वाली सूचना के टुकड़े का प्रतिनिधित्व करने वाली इकाई है।
मौसम पूर्वानुमान
using System; using System.Collections.Generic; namespace Observable { public class WeatherForecast : IObservable<WeatherInfo> { private readonly List<IObserver<WeatherInfo>> m_Observers; private readonly List<WeatherInfo> m_WeatherInfoList; public WeatherForecast() { m_Observers = new List<IObserver<WeatherInfo>>(); m_WeatherInfoList = new List<WeatherInfo>(); } public IDisposable Subscribe(IObserver<WeatherInfo> observer) { if (!m_Observers.Contains(observer)) { m_Observers.Add(observer); foreach (var item in m_WeatherInfoList) { observer.OnNext(item); } } return new WeatherForecastUnsubscriber(m_Observers, observer); } public void RegisterWeatherInfo(WeatherInfo weatherInfo) { m_WeatherInfoList.Add(weatherInfo); foreach (var observer in m_Observers) { observer.OnNext(weatherInfo); } } public void ClearWeatherInfo() { m_WeatherInfoList.Clear(); } } }
हम यहाँ क्या देख सकते हैं:
-
WeatherForecast
क्लासIObservable<WeatherInfo>
लागू कर रहा है। -
Subscribe
विधि के कार्यान्वयन में, हम जांचते हैं कि पास ऑब्जर्वर पहले से पंजीकृत था या नहीं। यदि नहीं, तो हम इसे स्थानीयm_Observers
पर्यवेक्षकों की सूची में जोड़ते हैं। फिर, हम स्थानीयm_WeatherInfoList
सूची में सभीWeatherInfo
प्रविष्टियों पर एक-एक करके लूप करते हैं और ऑब्जर्वर केOnNext
मेथड को कॉल करके ऑब्जर्वर को इसके बारे में सूचित करते हैं। - अंत में, हम सूचना स्ट्रीम से सदस्यता समाप्त करने के लिए पर्यवेक्षक द्वारा उपयोग किए जाने वाले
WeatherForecastUnsubscriber
वर्ग का एक नया उदाहरण लौटाते हैं। -
RegisterWeatherInfo
विधि को परिभाषित किया गया है ताकि मुख्य मॉड्यूल नएWeatherInfo
पंजीकृत कर सके। वास्तविक दुनिया में, इसे एक आंतरिक अनुसूचित एपीआई कॉल या एक सिग्नलआर हब के श्रोता या कुछ और द्वारा प्रतिस्थापित किया जा सकता है जो सूचना के स्रोत के रूप में कार्य करेगा।
अनसब्सक्राइबर <टी>
using System; using System.Collections.Generic; namespace Observable { public class Unsubscriber<T> : IDisposable { private readonly List<IObserver<T>> m_Observers; private readonly IObserver<T> m_Observer; private bool m_IsDisposed; public Unsubscriber(List<IObserver<T>> observers, IObserver<T> observer) { m_Observers = observers; m_Observer = observer; } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (m_IsDisposed) return; if (disposing && m_Observers.Contains(m_Observer)) { m_Observers.Remove(m_Observer); } m_IsDisposed = true; } ~Unsubscriber() { Dispose(false); } } }
हम यहाँ क्या देख सकते हैं:
- यह किसी भी अन-सब्सक्राइबर के लिए बेस क्लास है।
- यह डिस्पोजेबल डिजाइन पैटर्न को लागू करके
IDisposable
लागू करता है। - कंस्ट्रक्टर के माध्यम से, यह ऑब्जर्वर की पूरी सूची और इसके लिए बनाए गए ऑब्जर्वर को लेता है।
- निपटान करते समय, यह जाँचता है कि क्या पर्यवेक्षक पहले से ही पर्यवेक्षकों की पूरी सूची में मौजूद है। यदि हां, तो इसे सूची से हटा दें।
मौसम का पूर्वानुमान अनसब्सक्राइबर
using System; using System.Collections.Generic; namespace Observable { public class WeatherForecastUnsubscriber : Unsubscriber<WeatherInfo> { public WeatherForecastUnsubscriber( List<IObserver<WeatherInfo>> observers, IObserver<WeatherInfo> observer) : base(observers, observer) { } } }
हम यहाँ क्या देख सकते हैं:
- यह
Unsubscriber<T>
क्लास से इनहेरिट कर रहा है। - कोई खास प्रबंध नहीं हो रहा है।
मौसम का पूर्वानुमान ऑब्जर्वर
using System; namespace Observable { public class WeatherForecastObserver : IObserver<WeatherInfo> { private IDisposable m_Unsubscriber; public virtual void Subscribe(WeatherForecast provider) { m_Unsubscriber = provider.Subscribe(this); } public virtual void Unsubscribe() { m_Unsubscriber.Dispose(); } public void OnCompleted() { Console.WriteLine("Completed"); } public void OnError(Exception error) { Console.WriteLine("Error"); } public void OnNext(WeatherInfo value) { Console.WriteLine($"Temperature: {value.Temperature}"); } } }
हम यहाँ क्या देख सकते हैं:
-
WeatherForecastObserver
वर्गIObserver<WeatherInfo>
लागू कर रहा है। -
OnNext
विधि पर, हम तापमान को कंसोल पर लिख रहे हैं। -
OnCompleted
मेथड पर, हम कंसोल पर "पूर्ण" लिख रहे हैं। -
OnError
विधि पर, हम कंसोल पर "त्रुटि" लिख रहे हैं। - हमने मुख्य मॉड्यूल को पंजीकरण प्रक्रिया को ट्रिगर करने की अनुमति देने के लिए
void Subscribe(WeatherForecast provider)
विधि को परिभाषित किया। अन-सब्सक्राइबर ऑब्जेक्ट लौटाया जाता है जो अनसब्सक्राइब करने की स्थिति में उपयोग करने के लिए आंतरिक रूप से सहेजा जाता है। - उसी अवधारणा का उपयोग करते हुए,
void Unsubscribe()
विधि को परिभाषित किया गया है और यह आंतरिक रूप से सहेजे गए अन-सब्सक्राइबर ऑब्जेक्ट का उपयोग करता है।
कार्यक्रम
using System; namespace Observable { class Program { static void Main(string[] args) { var provider = new WeatherForecast(); provider.RegisterWeatherInfo(new WeatherInfo(1)); provider.RegisterWeatherInfo(new WeatherInfo(2)); provider.RegisterWeatherInfo(new WeatherInfo(3)); var observer = new WeatherForecastObserver(); observer.Subscribe(provider); provider.RegisterWeatherInfo(new WeatherInfo(4)); provider.RegisterWeatherInfo(new WeatherInfo(5)); observer.Unsubscribe(); provider.RegisterWeatherInfo(new WeatherInfo(6)); observer.Subscribe(provider); provider.RegisterWeatherInfo(new WeatherInfo(7)); Console.ReadLine(); } } }
हम यहाँ क्या देख सकते हैं:
- हमने प्रदाता का एक उदाहरण बनाया है।
- फिर जानकारी के 3 टुकड़े दर्ज किए।
- इस क्षण तक, कंसोल में कुछ भी लॉग नहीं होना चाहिए क्योंकि कोई पर्यवेक्षक परिभाषित नहीं है।
- फिर पर्यवेक्षक का एक उदाहरण बनाया।
- फिर ऑब्जर्वर को स्ट्रीम में सब्सक्राइब करें।
- इस समय, हमें कंसोल में 3 लॉग किए गए तापमान का पता लगाना चाहिए। ऐसा इसलिए है क्योंकि जब पर्यवेक्षक सदस्यता लेता है, तो उसे पहले से मौजूद जानकारी के बारे में सूचित किया जाता है और हमारे मामले में, वे सूचना के 3 भाग होते हैं।
- फिर हम जानकारी के 2 टुकड़े दर्ज करते हैं।
- इसलिए, हमें कंसोल में लॉग इन किए गए 2 और संदेश मिलते हैं।
- फिर हम सदस्यता समाप्त करते हैं।
- फिर हम 1 जानकारी दर्ज करते हैं।
- हालाँकि, जानकारी का यह भाग कंसोल में लॉग नहीं होगा क्योंकि प्रेक्षक ने पहले ही सदस्यता समाप्त कर दी थी।
- फिर पर्यवेक्षक फिर से सदस्यता लेता है।
- फिर हम 1 जानकारी दर्ज करते हैं।
- तो, जानकारी का यह टुकड़ा कंसोल पर लॉग किया गया है।
अंत में, इसे चलाने से इस परिणाम के साथ समाप्त होना चाहिए:
मेरा विस्तारित कार्यान्वयन
जब मैंने Microsoft के कार्यान्वयन की जाँच की, तो मुझे कुछ चिंताएँ मिलीं। इसलिए मैंने कुछ मामूली बदलाव करने का फैसला किया।
IExtendedObservable <आउट टी>
using System; using System.Collections.Generic; namespace ExtendedObservable { public interface IExtendedObservable<out T> : IObservable<T> { IReadOnlyCollection<T> Snapshot { get; } IDisposable Subscribe(IObserver<T> observer, bool withHistory); } }
हम यहाँ क्या देख सकते हैं:
-
IExtendedObservable<out T>
इंटरफ़ेसIObservable<T>
इंटरफ़ेस का विस्तार करता है। - यह सहपरिवर्ती है। यदि आप इसके बारे में अधिक जानना चाहते हैं, तो आप लेख को देख सकते हैं
.NET C# में सहप्रसरण और प्रतिप्रसरण . - हमने
IReadOnlyCollection<T> Snapshot
प्रॉपर्टी को परिभाषित किया है ताकि अन्य मॉड्यूल को सब्सक्राइब किए बिना पहले से मौजूद जानकारी प्रविष्टियों की तत्काल सूची प्राप्त करने की अनुमति मिल सके। - हमने
IDisposable Subscribe(IObserver<T> observer, bool withHistory)
मेथड को एक अतिरिक्तbool withHistory
पैरामीटर के साथ परिभाषित किया है ताकि ऑब्जर्वर यह तय कर सके कि वह पहले से मौजूद जानकारी प्रविष्टियों के बारे में सूचित करना चाहता है या नहीं।
सदस्यता समाप्त
using System; namespace ExtendedObservable { public class Unsubscriber : IDisposable { private readonly Action m_UnsubscribeAction; private bool m_IsDisposed; public Unsubscriber(Action unsubscribeAction) { m_UnsubscribeAction = unsubscribeAction; } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (m_IsDisposed) return; if (disposing) { m_UnsubscribeAction(); } m_IsDisposed = true; } ~Unsubscriber() { Dispose(false); } } }
हम यहाँ क्या देख सकते हैं:
- अब,
Unsubscriber
वर्ग सामान्य नहीं है। - ऐसा इसलिए है क्योंकि इसे सूचना इकाई के प्रकार को जानने के लिए और अधिक की आवश्यकता नहीं है।
- ऑब्जर्वर की पूरी सूची और इसके लिए बनाए गए ऑब्जर्वर की पूरी सूची तक पहुंचने के बजाय, यह सिर्फ ऑब्जर्वेबल को डिस्पोज़ किए जाने पर सूचित करता है और ऑब्जर्वेबल अपने आप में डीरजिस्ट्रेशन प्रक्रिया को हैंडल करता है।
- इस तरह वह पहले से कम काम कर रहा है और सिर्फ अपना काम कर रहा है।
मौसम का पूर्वानुमान अनसब्सक्राइबर
using System; using System.Collections.Generic; namespace ExtendedObservable { public class WeatherForecastUnsubscriber : Unsubscriber { public WeatherForecastUnsubscriber( Action unsubscribeAction) : base(unsubscribeAction) { } } }
हम यहाँ क्या देख सकते हैं:
- हमने
Unsubscriber<T>
से<T>
भाग को हटा दिया। - और अब कन्स्ट्रक्टर निपटान के मामले में बुलाए जाने वाले
Action
में लेता है।
मौसम पूर्वानुमान
using System; using System.Collections.Generic; namespace ExtendedObservable { public class WeatherForecast : IExtendedObservable<WeatherInfo> { private readonly List<IObserver<WeatherInfo>> m_Observers; private readonly List<WeatherInfo> m_WeatherInfoList; public WeatherForecast() { m_Observers = new List<IObserver<WeatherInfo>>(); m_WeatherInfoList = new List<WeatherInfo>(); } public IReadOnlyCollection<WeatherInfo> Snapshot => m_WeatherInfoList; public IDisposable Subscribe(IObserver<WeatherInfo> observer) { return Subscribe(observer, false); } public IDisposable Subscribe(IObserver<WeatherInfo> observer, bool withHistory) { if (!m_Observers.Contains(observer)) { m_Observers.Add(observer); if (withHistory) { foreach (var item in m_WeatherInfoList) { observer.OnNext(item); } } } return new WeatherForecastUnsubscriber( () => { if (m_Observers.Contains(observer)) { m_Observers.Remove(observer); } }); } public void RegisterWeatherInfo(WeatherInfo weatherInfo) { m_WeatherInfoList.Add(weatherInfo); foreach (var observer in m_Observers) { observer.OnNext(weatherInfo); } } public void ClearWeatherInfo() { m_WeatherInfoList.Clear(); } } }
हम यहाँ क्या देख सकते हैं:
- यह
IReadOnlyCollection<WeatherInfo> Snapshot
संपत्ति को छोड़कर लगभग समान है जो आंतरिकm_WeatherInfoList
सूची देता है लेकिनIReadOnlyCollection
के रूप में। - और
IDisposable Subscribe(IObserver<WeatherInfo> observer, bool withHistory)
विधि जोwithHistory
पैरामीटर का उपयोग करती है।
मौसम का पूर्वानुमान ऑब्जर्वर
using System; namespace ExtendedObservable { public class WeatherForecastObserver : IObserver<WeatherInfo> { private IDisposable m_Unsubscriber; public virtual void Subscribe(WeatherForecast provider) { m_Unsubscriber = provider.Subscribe(this, true); } public virtual void Unsubscribe() { m_Unsubscriber.Dispose(); } public void OnCompleted() { Console.WriteLine("Completed"); } public void OnError(Exception error) { Console.WriteLine("Error"); } public void OnNext(WeatherInfo value) { Console.WriteLine($"Temperature: {value.Temperature}"); } } }
हम यहां जो नोटिस कर सकते हैं वह यह है कि Subscribe(WeatherForecast provider)
को छोड़कर यह लगभग समान है जो अब यह तय करता है कि इसे हिस्ट्री के साथ Subscribe
करना चाहिए या नहीं।
कार्यक्रम
using System; namespace ExtendedObservable { class Program { static void Main(string[] args) { var provider = new WeatherForecast(); provider.RegisterWeatherInfo(new WeatherInfo(1)); provider.RegisterWeatherInfo(new WeatherInfo(2)); provider.RegisterWeatherInfo(new WeatherInfo(3)); var observer = new WeatherForecastObserver(); observer.Subscribe(provider); provider.RegisterWeatherInfo(new WeatherInfo(4)); provider.RegisterWeatherInfo(new WeatherInfo(5)); observer.Unsubscribe(); provider.RegisterWeatherInfo(new WeatherInfo(6)); observer.Subscribe(provider); provider.RegisterWeatherInfo(new WeatherInfo(7)); Console.ReadLine(); } } }
यह पहले जैसा ही है।
अंत में, इसे चलाना पहले के समान परिणाम के साथ समाप्त होना चाहिए:
आगे क्या होगा
अब, आप .NET C# में ऑब्जर्वर डिज़ाइन पैटर्न की मूल बातें जानते हैं। हालाँकि, यह कहानी का अंत नहीं है।
IObservable<T>
और IObserver<T>
इंटरफेस के शीर्ष पर निर्मित पुस्तकालय हैं जो आपको अधिक उपयोगी सुविधाएं और क्षमताएं प्रदान करते हैं जो आपको उपयोगी लग सकते हैं।
इन पुस्तकालयों में से है
इसलिए, मैं आपको प्रोत्साहित करता हूं कि आप इन पुस्तकालयों का पता लगाएं और उन्हें आजमाएं। मुझे यकीन है कि आप उनमें से कुछ को पसंद करेंगे।