MVVM खराब डिज़ाइन की गई डेटा बाइंडिंग परतों के लिए एक बैंड-सहायता है। विशेष रूप से, WPF / XAML में डेटा बाइंडिंग में सीमाओं के कारण WPF / सिल्वरलाइट / WP7 दुनिया में इसका बहुत उपयोग देखा गया है।
अब से, हम यह मानने जा रहे हैं कि हम WPF / XAML के बारे में बात कर रहे हैं क्योंकि इससे चीजें अधिक स्पष्ट होंगी। एमवीवीएम को WPF / XAML में हल करने के लिए कुछ कमियों को देखते हैं।
यूआई आकार बनाम डेटा आकार
MVVM में 'VM', C # में परिभाषित वस्तुओं का एक समूह बनाता है, जो XAML में परिभाषित प्रस्तुति वस्तुओं के एक सेट पर मैप करता है। ये C # ऑब्जेक्ट आमतौर पर प्रस्तुति ऑब्जेक्ट्स पर DataContext गुणों के माध्यम से XAML से जुड़े होते हैं।
परिणामस्वरूप, viewmodel ऑब्जेक्ट ग्राफ़ को आपके एप्लिकेशन की प्रस्तुति ऑब्जेक्ट ग्राफ़ पर मैप करने की आवश्यकता होती है। यह कहने के लिए नहीं है कि मैपिंग को एक-से-एक होने की आवश्यकता है, लेकिन यदि एक सूची नियंत्रण एक विंडो नियंत्रण द्वारा निहित है, तो उस सूची के डेटा का वर्णन करने वाले ऑब्जेक्ट के लिए विंडो के DataContext ऑब्जेक्ट से प्राप्त करने का एक तरीका होना चाहिए।
व्यूमॉडल ऑब्जेक्ट ग्राफ यूआई ऑब्जेक्ट ग्राफ से मॉडल ऑब्जेक्ट ग्राफ को सफलतापूर्वक डिकोड करता है, लेकिन एक अतिरिक्त दृश्यमॉडल परत की कीमत पर जिसे बनाया और बनाए रखा जाना चाहिए।
अगर मुझे स्क्रीन ए से स्क्रीन बी के कुछ डेटा को स्थानांतरित करना है, तो मुझे व्यूमॉडल के साथ गड़बड़ करने की आवश्यकता है। एक व्यापार आदमी के दिमाग में, यह एक यूआई परिवर्तन है। यह विशुद्ध रूप से एक्सएएमएल की दुनिया में होना चाहिए । अफसोस की बात है, यह शायद ही कभी हो सकता है। इससे भी बदतर, इस बात पर निर्भर करता है कि दृश्यमॉडल कैसे संरचित हैं और कैसे सक्रिय रूप से डेटा बदलता है, इस परिवर्तन को पूरा करने के लिए काफी डेटा री-रूटिंग की आवश्यकता हो सकती है।
आसन्न डेटा बाइंडिंग के आसपास काम करना
WPF / XAML बाइंडिंग अपर्याप्त रूप से अभिव्यंजक हैं। आप मूल रूप से एक ऑब्जेक्ट प्राप्त करने का एक तरीका प्रदान करते हैं, ट्रैवर्स के लिए एक संपत्ति पथ, और प्रस्तुति ऑब्जेक्ट के लिए डेटा प्रॉपर्टी के मूल्य को अनुकूलित करने के लिए कन्वर्टर्स को बाइंड करते हैं।
यदि आपको C # से अधिक जटिल किसी संपत्ति को बांधने की आवश्यकता है, तो आप मूल रूप से भाग्य से बाहर हैं। मैंने कभी भी एक बाध्यकारी कनवर्टर के बिना WPF ऐप नहीं देखा है जो विज़िबल / Collapsed में सच / गलत निकला है। कई WPF एप्स में NegatingVisibilityConverter या इसी तरह की कुछ चीजें होती हैं जो ध्रुवता को प्रवाहित करती हैं। यह अलार्म घंटियाँ सेट किया जाना चाहिए।
एमवीवीएम आपको अपने सी # कोड को संरचित करने के लिए दिशानिर्देश देता है जिसका उपयोग इस सीमा पर सुचारू करने के लिए किया जा सकता है। आप SomeButtonVisibility नामक अपने व्यूमॉडल पर एक संपत्ति का पर्दाफाश कर सकते हैं और इसे केवल उस बटन की दृश्यता के लिए बाध्य कर सकते हैं। आपका XAML अब अच्छा और सुंदर है ... लेकिन आपने खुद को एक क्लर्क में बना लिया है - अब आपको अपने यूआई के विकसित होने पर दो स्थानों (यूआई और सी # में कोड) में बाइंडिंग को अपडेट करना होगा। यदि आपको किसी अन्य स्क्रीन पर होने के लिए समान बटन की आवश्यकता है, तो आपको एक समान संपत्ति को एक व्यूमोडल पर उजागर करना होगा जो उस स्क्रीन तक पहुंच सकता है। इससे भी बदतर, मैं अभी XAML को नहीं देख सकता और देखूंगा कि बटन कब दिखाई देगा। जैसे ही बाइंडिंग थोड़ी नॉनवेज हो जाती है, मुझे C # कोड में जासूसी का काम करना पड़ता है।
डेटा तक पहुंच आक्रामक रूप से बंद है
चूंकि डेटा आमतौर पर DataContext गुणों के माध्यम से UI में प्रवेश करता है, इसलिए आपके पूरे ऐप में लगातार वैश्विक या सत्र डेटा का प्रतिनिधित्व करना कठिन है।
"वर्तमान में लॉग इन उपयोगकर्ता" का विचार एक महान उदाहरण है - यह अक्सर आपके ऐप के उदाहरण के भीतर वास्तव में एक वैश्विक चीज है। WPF / XAML में लगातार तरीके से वर्तमान उपयोगकर्ता तक वैश्विक पहुंच सुनिश्चित करना बहुत मुश्किल है।
मैं जो करना चाहता हूं वह डेटा बाइंडिंग में "करंटयूजर" शब्द का उपयोग करने के लिए स्वतंत्र रूप से वर्तमान में लॉग इन उपयोगकर्ता को संदर्भित करने के लिए है। इसके बजाय, मुझे यह सुनिश्चित करना होगा कि प्रत्येक DataContext मुझे वर्तमान उपयोगकर्ता ऑब्जेक्ट को प्राप्त करने का एक तरीका देता है। एमवीवीएम इस पर विचार कर सकता है, लेकिन व्यूमाडल्स गड़बड़ाने वाले हैं क्योंकि इन सभी को इस वैश्विक डेटा तक पहुंच प्रदान करनी है।
एक उदाहरण जहां MVVM गिरता है
कहें कि हमारे पास उपयोगकर्ताओं की एक सूची है। प्रत्येक उपयोगकर्ता के बगल में, हम "उपयोगकर्ता हटाएं" बटन प्रदर्शित करना चाहते हैं, लेकिन केवल अगर वर्तमान में लॉग इन उपयोगकर्ता एक व्यवस्थापक है। साथ ही, उपयोगकर्ताओं को स्वयं को हटाने की अनुमति नहीं है।
आपकी मॉडल ऑब्जेक्ट को वर्तमान में लॉग इन किए गए उपयोगकर्ता के बारे में नहीं पता होना चाहिए - वे आपके डेटाबेस में उपयोगकर्ता रिकॉर्ड का प्रतिनिधित्व करेंगे, लेकिन किसी भी तरह वर्तमान में लॉग इन उपयोगकर्ता को आपकी सूची पंक्तियों के भीतर डेटा बाइंडिंग के संपर्क में होना चाहिए। MVVM तय करता है कि हमें प्रत्येक सूची पंक्ति के लिए एक व्यूअमॉडल ऑब्जेक्ट बनाना चाहिए जो वर्तमान में उस सूची पंक्ति द्वारा प्रतिनिधित्व किए गए उपयोगकर्ता के साथ लॉग इन करता है, फिर उस व्यूमॉडल ऑब्जेक्ट पर "DeleteButtonVisibility" या "CanDelete" नामक एक संपत्ति को उजागर करें (आपकी भावनाओं के आधार पर) बाध्यकारी कन्वर्टर्स के बारे में)।
यह ऑब्जेक्ट अधिकांश अन्य तरीकों से उपयोगकर्ता ऑब्जेक्ट की तरह एक बहुत ही भयानक दिखने वाला है - इसे बदलने के साथ उपयोगकर्ता मॉडल ऑब्जेक्ट के सभी गुणों और उस डेटा के आगे के अपडेट को प्रतिबिंबित करने की आवश्यकता हो सकती है। यह वास्तव में icky लगता है - फिर से, MVVM आपको इस उपयोगकर्ता-कार्य-संबंधी ऑब्जेक्ट को बनाए रखने के लिए मजबूर करके एक क्लर्क में बनाता है।
विचार करें - आपको संभवतः एक डेटाबेस, मॉडल और दृश्य में अपने उपयोगकर्ता के गुणों का भी प्रतिनिधित्व करना होगा। यदि आपके और आपके डेटाबेस के बीच एक एपीआई है, तो यह बदतर है - वे डेटाबेस, एपीआई सर्वर, एपीआई क्लाइंट, मॉडल और दृश्य में प्रतिनिधित्व करते हैं। मैं एक डिज़ाइन पैटर्न को अपनाने में वास्तव में संकोच कर रहा हूँ जो एक और परत को जोड़ा जाता है जिसे हर बार एक संपत्ति को जोड़ने या बदलने की आवश्यकता होती है।
इससे भी बदतर, यह परत आपके यूआई की जटिलता के साथ मापता है, आपके डेटा मॉडल की जटिलता के साथ नहीं। अक्सर एक ही डेटा को कई स्थानों पर और आपके UI में दर्शाया जाता है - यह केवल एक परत नहीं जोड़ता है, यह बहुत अधिक सतह क्षेत्र के साथ परत जोड़ता है!
चीजें कैसे हो सकती थीं
ऊपर वर्णित मामले में, मैं कहना चाहूंगा:
<Button Visibility="{CurrentUser.IsAdmin && CurrentUser.Id != Id}" ... />
करेंटयूसर को मेरे ऐप में सभी एक्सएएमएल को विश्व स्तर पर उजागर किया जाएगा। आईडी मेरी सूची पंक्ति के लिए DataContext पर एक संपत्ति का उल्लेख करेगी। दृश्यता स्वचालित रूप से बूलियन से परिवर्तित होगी। Id, CurrentUser.IsAdmin, CurrentUser, या CurrentUser.Id में कोई भी अपडेट इस बटन की दृश्यता के लिए एक अद्यतन ट्रिगर करेगा। बहुत आसान।
इसके बजाय, WPF / XAML अपने उपयोगकर्ताओं को एक पूर्ण गड़बड़ बनाने के लिए मजबूर करता है। जहाँ तक मैं बता सकता हूँ, कुछ रचनात्मक ब्लॉगर्स ने उस गड़बड़ पर एक नाम दिया और वह नाम था MVVM। मूर्ख मत बनो - यह गोफ़ डिज़ाइन पैटर्न के समान वर्ग में नहीं है। यह एक बदसूरत डेटा बाध्यकारी प्रणाली के आसपास काम करने के लिए एक बदसूरत हैक है।
(इस दृष्टिकोण को कभी-कभी "कार्यात्मक प्रतिक्रियाशील प्रोग्रामिंग" के रूप में संदर्भित किया जाता है, यदि आप आगे पढ़ने के लिए देख रहे हैं)।
निष्कर्ष के तौर पर
यदि आपको WPF / XAML में काम करना चाहिए, तो भी मैं MVVM की सिफारिश नहीं करता।
आप चाहते हैं कि आपके कोड को "कैसे चीजें हो सकती हैं" की तरह संरचित किया गया है, उदाहरण के लिए यह होगा - मॉडल सीधे देखने के लिए उजागर होता है, जटिल डेटा बाइंडिंग अभिव्यक्तियों + लचीले मूल्य के साथ। यह बेहतर तरीका है - अधिक पठनीय, अधिक लिखने योग्य, और अधिक बनाए रखने योग्य।
एमवीवीएम आपको अपने कोड को अधिक क्रियात्मक, कम रखरखाव योग्य तरीके से संरचना करने के लिए कहता है।
MVVM के बजाय, आपको अच्छे अनुभव के बारे में जानने में मदद करने के लिए कुछ सामानों का निर्माण करें: अपने UI पर लगातार वैश्विक स्थिति को उजागर करने के लिए एक सम्मेलन का विकास करें। अपने आप को बाइंडिंग कन्वर्टर्स, मल्टीबाइंडिंग आदि से कुछ टूलींग बनाएँ, जिससे आप अधिक जटिल बाइंडिंग एक्सप्रेशन व्यक्त कर सकते हैं। अपने आप को बाध्यकारी कन्वर्टर्स की एक लाइब्रेरी बनाएँ, जो सामान्य ज़बरदस्ती के मामलों को कम दर्दनाक बनाने में मदद करे।
इससे भी बेहतर - XAML को कुछ अधिक अभिव्यंजक के साथ बदलें। XAML C # ऑब्जेक्ट्स को इंस्टेंट करने के लिए एक बहुत ही सरल XML फॉर्मेट है - अधिक अभिव्यंजक संस्करण के साथ आना मुश्किल नहीं होगा।
मेरी अन्य सिफारिश: टूलकिट का उपयोग न करें जो इस प्रकार के समझौतों को मजबूर करते हैं। वे आपके समस्या डोमेन पर ध्यान केंद्रित करने के बजाय MVVM जैसी बकवास की ओर धकेलकर आपके अंतिम उत्पाद की गुणवत्ता को चोट पहुंचाएंगे।