model-view-controller - एक्स्ट जेएस के एमवीसी एक विरोधी पैटर्न है?




extjs anti-patterns (6)

मैं 25 डेवलपर्स की एक टीम में काम करता हूं हम सेन्का के एक्स्टजेएस एमवीसी पैटर्न का उपयोग करते हैं लेकिन हमें विश्वास है कि एमवीसी की उनकी परिभाषा भ्रामक है शायद हम उनके एमवीसी को एक विरोधी-पैटर्न भी कहते हैं

एएमएआईके, एमवीसी नियंत्रक में केवल नाम या दृश्य के पथ को जानता है, और दृश्य के आंतरिक संरचना पर कोई ज्ञान नहीं है। उदाहरण के लिए, यह नियंत्रक की ज़िम्मेदारी नहीं है, चाहे ग्राहकों की सूची को एक सरल ड्रॉप डाउन, या एक स्वत: पूर्ण रूप से दिखाया जाए।

हालांकि, एक्सटेंशन जेएस के एमवीसी में, नियंत्रक को देखने के तत्वों के प्रतिपादन को पता होना चाहिए, क्योंकि नियंत्रक उन तत्वों में घुसता है, और उनकी घटनाओं को सुनता है। इसका मतलब यह है कि यदि दृश्य परिवर्तन का एक तत्व (उदाहरण के लिए एक बटन एक लिंक बन गया है), तो नियंत्रक में प्रासंगिक चयनकर्ता को भी बदलना चाहिए। दूसरे शब्दों में, नियंत्रक को देखने के आंतरिक ढांचे के लिए कसकर युग्मित किया जाता है।

क्या इस वजह से एंटी-पैटर्न के रूप में एक्स्ट जेएस के एमवीवी को निंदा करना स्वीकार्य है? क्या हम सही हैं कि नियंत्रक के विचारों के लिए युग्मित हैं?


Answers

मैं वर्तमान में सेन्का ट्रेनिंग से फास्ट ट्रैक के लिए ExtJS 4 से गुजर रहा हूं। मेरे पास एक्स्टजेएस (एक्स्टजेएस 2.0 के बाद से) में एक मजबूत पृष्ठभूमि है और यह देखने के लिए बहुत उत्सुक था कि कैसे एमवीसीएल एक्स्टजेएस 4 में लागू किया गया था।

अब, पहले, जिस तरह से मैं एक नियंत्रक की तरह अनुकरण करता हूं, वह मुख्य कंटेनर की जिम्मेदारी को सौंपना होगा। निम्नलिखित उदाहरण की कल्पना करें ExtJS 3:

Ext.ns('Test');

Test.MainPanel = Ext.extend(Ext.Container, {
    initComponent : function() {
        this.panel1 = new Test.Panel1({
            listeners: {
                firstButtonPressed: function(){
                    this.panel2.addSomething();
                },
                scope: this
            }
        });
        this.panel2 = new Test.Panel2();

        this.items = [this.panel1,this.panel2];

        Test.MainPanel.superclass.initComponent.call(this);
    }

});

Test.Panel1 = Ext.extend(Ext.Panel, {
    initComponent : function() {
        this.addEvents('firstButtonPressed');

        this.tbar = new Ext.Toolbar({
           items: [{
             text: 'First Button',
               handler: function(){
                   this.fireEvent('firstButtonPressed');
               }
           }] 
        });

        Text.Panel1.superclass.initComponent.call(this);
    }
});

Test.Panel2 = Ext.extend(Ext.Panel, {
    initComponent : function() {
        this.items = [new Ext.form.Label('test Label')]

        Test.Panel2.superclass.initComponent.call(this);
    },

    addSomething: function(){
        alert('add something reached')
    }
});

जैसा कि आप देख सकते हैं, मेरा मेन पैनेल (इस तथ्य के अलावा जो दोनों पैनलों को पकड़ रहा है) भी घटनाओं को नियुक्त करता है और इस प्रकार दो घटकों के बीच संचार बना रहा है, इस प्रकार नियंत्रक के समान अनुकरण

एक्स्टजेएस 4 में एमवीसी सीधे इसमें कार्यान्वित हुआ है। क्या वास्तव में मुझे हड़बड़ा था कि जिस तरह से नियंत्रक वास्तव में घटकों को प्राप्त करता है QuerySelector के माध्यम से है जो मेरी राय में बहुत त्रुटि के लिए प्रवण है चलो देखते हैं:

Ext.define('MyApp.controller.Earmarks', {
    extend:'Ext.app.Controller',
    views:['earmark.Chart'],


    init:function () {
        this.control({
             'earmarkchart > toolbar > button':{
                click:this.onChartSelect
            },
            'earmarkchart tool[type=gear]':{
                click:this.selectChart
             }
        });
    }
});

इसलिए जैसा कि हम यहां देख सकते हैं, जिस तरह से नियंत्रक earmarkchart बटन और उपकरण के बारे में पता है, चयनकर्ताओं के माध्यम से है आइए अब सोचें कि मैं अपने ख़ास ख़रीदना में लेआउट बदल रहा हूं और वास्तव में टूलबार के बाहर बटन को स्थानांतरित कर रहा हूं। अचानक मेरा आवेदन टूटा हुआ है, क्योंकि मुझे हमेशा यह जानना होगा कि लेआउट बदलने से उसके साथ जुड़े नियंत्रक पर असर पड़ सकता है।

कोई कह सकता है कि मैं इसके बजाय itemId का उपयोग कर सकता हूँ, लेकिन फिर मुझे पता होना चाहिए कि अगर मैं एक घटक को हटा देता हूं तो मुझे यह पता लगाना चाहिए कि क्या उस मद आईडी के लिए मेरे नियंत्रकों में कोई छिपा हुआ संदर्भ है, और यह तथ्य भी है कि मैं नहीं कर सकता प्रति माता पिता के घटक के अनुसार एक ही आइटम है, इसलिए यदि मेरे पास एक पैनल 1 और 'ग्रिड 1' में एक 'testId' नामक एक आइटम है तो मुझे अभी भी चुनना होगा अगर मैं चाहता हूं कि आइटम 1 पैनल से या ग्रिड 1 से।

मैं समझता हूं कि प्रश्न बहुत शक्तिशाली है क्योंकि यह आपको बहुत अधिक लचीलापन देता है, लेकिन यह लचीलापन मेरी राय में बहुत ही उच्च कीमत पर आता है, और अगर मेरे पास 5 लोगों की एक टीम है जो उपयोगकर्ता इंटरफेस विकसित कर रहा है और मुझे इस अवधारणा को समझाने की आवश्यकता है I मेरे हाथों को आग में लगाएंगे कि वे इससे पहले जितने बिंदु का उल्लेख करेंगे I

इस पर आपकी समग्र राय क्या है? क्या यह घटनाओं के साथ किसी तरह से संवाद करना आसान होगा? मेरा कंट्रोलर वास्तव में घटनाओं की अपेक्षाओं के बारे में क्या जानता है, इसका मतलब यह है कि कोई भी घटना की घटना को ठीक कर सकता है। नियंत्रक और संबंधित नियंत्रक इसे सभी प्रश्नों की समस्या के बजाय मिलेंगे।


बेशक नियंत्रक किसी तरह से विचार करने के लिए बाध्य हैं। आपको अपने विचारों में बिल्कुल तत्वों को लक्षित करने की आवश्यकता होती है जिन्हें आप सुनना चाहते हैं।

उदाहरण के लिए: उस बटन क्लिक या उस फॉर्म तत्व परिवर्तन को या उस कस्टम घटक / ईवेंट को सुनें

एमवीसी का लक्ष्य घटकों को घटाना और पुन: प्रयोज्यता और सेंचा एमवीसी के लिए कमाल है। @ बमौसाक्यू के अनुसार, आपको एमवीसी पैटर्न का पूरा फायदा उठाने के लिए दृश्य नियंत्रकों (दृश्य / विगेट्स खुद के लिए अंतर्निहित) और अनुप्रयोग नियंत्रक (शीर्ष स्तर के विचारों में जोड़-तोड़) के अलग होने में सावधान रहना होगा। और यह कुछ स्पष्ट नहीं है जब आप http://docs.sencha.com/ext-js/4-1/#!/guide/application_architecture पढ़ सकते हैं अपने एमवीसी दृष्टिकोण को रिफैक्टर करें, अलग-अलग नियंत्रक बनाएं, कस्टम घटक बनाएं, और इसका लाभ लेने के लिए पूर्ण एक्स्टजेएस एमवीसी आर्किटेक्चर को गले लगाएं।

सेन्चा के दृष्टिकोण आईएमएचओ में मामूली समस्या है, एमवीसी refs सिस्टम वास्तव में काम नहीं करता जब आपके पास एक आवेदन में एक ही विचार के कई उदाहरण हैं उदाहरण: यदि आपके पास एक ही घटक के कई उदाहरणों के साथ एक टैब पैनेल है, तो रिफ सिस्टम टूट गया है क्योंकि यह हमेशा डोम में पाया पहला तत्व लक्षित करेगा ... वहां काम करनेवाले और एक परियोजना को ठीक करने का प्रयास किया गया है लेकिन मुझे उम्मीद है कि यह जल्दी ही जल्दी से परिष्कृत हो जाओ


मुझे लगता है कि यदि आप सीनचा आर्किटेक्ट का उपयोग दृश्यों का निर्माण करने के लिए करते हैं तो उस दृश्य से अपना खुद का दृश्य बनाने के लिए इनहेरिट करें अब यह दृश्य किसी भी घटना को हुक करने और सार्थक घटनाओं को बढ़ाने के लिए जिम्मेदार हो सकता है

यह सिर्फ एक विचार है ...

//Designer Generated
Ext.define('MyApp.view.MainView', {
    extend: 'Ext.grid.GridPanel',
    alias: 'widget.mainview',    
    initComponent: function() {
    }
});


//Your View Decorator
Ext.define('MyApp.view.MainView', {
    extend: 'MyApp.view.MainViewEx',
    alias: 'widget.mainviewex',    
    initComponent: function() {
    this.mon(this, 'rowselect', function(){
        this.fireEvent('userselected', arguments);
    }, this);
    }
});

मैं एक्स्टजेएस 4 के एमवीसी रोज़ का इस्तेमाल करता हूं। स्पेगेटी कोड के बजाय, मेरे पास एक सुरुचिपूर्ण एमवीसी ऐप है जो कंसने से अलग कंसेंस के अलग से परिभाषित किया गया है और यह बनाए रखने और विस्तार करने के लिए हास्यास्पद रूप से सरल है। हो सकता है कि आपके कार्यान्वयन को थोड़ा-सा करने की आवश्यकता है ताकि एमवीसी दृष्टिकोण का पूरा लाभ उठाया जा सके।


अपडेट (मार्च 2015): एक्स्ट 5.0 में विजिटर नियंत्रकों को प्रस्तुत किया गया है, जो इस धागे में चर्चा की गई अधिकांश चिंताओं को संबोधित करना चाहिए। लाभ:

  • ViewController के अंदर घटक संदर्भ के आसपास बेहतर / लागू दायरा
  • एप्लिकेशन प्रवाह-नियंत्रण तर्क से दृश्य-विशिष्ट तर्क को अलग-अलग करने के लिए आसान है
  • ViewController जीवनचक्र उस रूपरेखा के साथ प्रबंधित दृश्य के साथ प्रबंधित है

Ext 5 फिर भी मौजूदा Ext.app.Controller वर्ग प्रदान करता है, ताकि चीजों को पिछली-संगत रख सकें, और आपके एप्लिकेशन को कैसे संरचित करें, इसके लिए अधिक लचीलापन प्रदान करें।

मूल उत्तर:

एक्स्ट जेएस के एमवीसी में, नियंत्रक को देखने के तत्वों के प्रतिपादन को पता होना चाहिए, क्योंकि नियंत्रक उन तत्वों में घुसता है, और उनकी घटनाओं को सुनता है। इसका मतलब यह है कि यदि दृश्य परिवर्तन का एक तत्व (उदाहरण के लिए एक बटन एक लिंक बन गया है), तो नियंत्रक में प्रासंगिक चयनकर्ता को भी बदलना चाहिए। दूसरे शब्दों में, नियंत्रक को देखने के आंतरिक ढांचे के लिए कसकर युग्मित किया जाता है।

मैं वास्तव में सहमत हूं कि ज्यादातर मामलों में यह आपके द्वारा उद्धृत सटीक कारणों के लिए सबसे अच्छा विकल्प नहीं है, और यह दुर्भाग्यपूर्ण है कि एक्स्ट और टच के साथ आने वाले अधिकांश उदाहरण रेफ्रेश और कंट्रोल फ़ंक्शंस का प्रदर्शन करते हैं जो अक्सर चयनकर्ताओं का उपयोग करते हुए परिभाषित होते हैं जो देखने के इनकैप्सुलेशन का उल्लंघन करते हैं। हालांकि, यह एमवीसी की एक आवश्यकता नहीं है - यह उदाहरण है कि कैसे उदाहरण लागू किए गए हैं, और यह आसानी से बचने के लिए है

बीटीडब्लू, मुझे लगता है कि यह निश्चित रूप से सच अनुप्रयोग नियंत्रकों (नियंत्रण एप प्रवाह और साझा व्यापार तर्क के बीच नियंत्रक शैलियों को अलग करने के लिए समझ सकता है, विचारों से पूरी तरह से अनूठे होना चाहिए - ये वे हैं जो आप की बात कर रहे हैं), और नियंत्रकों को देखें (नियंत्रण / एक दृश्य के लिए विशेष रूप से इवेंट लॉजिक, डिजाइन द्वारा कसकर-युग्मित)। उत्तरार्द्ध का उदाहरण दृश्य के भीतर विगेट्स के बीच समन्वयित करने के लिए तर्क होगा, उस दृश्य के बिल्कुल आंतरिक रूप से। यह एक आम उपयोग का मामला है, और दृश्य-नियंत्रक को उसके दृश्य में जोड़ना कोई समस्या नहीं है - यह दृश्य कोड को यथासंभव गूंगा रखना एक कोड प्रबंधन रणनीति है।

समस्या यह है कि अधिकतर प्रलेखित उदाहरणों में, हर नियंत्रक केवल जो कुछ करना चाहते हैं, उसका संदर्भ देता है, और यह एक शानदार पैटर्न नहीं है हालांकि, यह एक्स्ट के एमवीसी कार्यान्वयन की आवश्यकता नहीं है - यह केवल उनके (उदाहरण के लिए आलसी) कन्वेंशन है। यह काफी सरल है (और मैं तर्क देता हूं कि सलाह दी जाती है) के बजाय अपने दृश्य वर्ग अपने स्वयं के कस्टम प्राप्तकर्ताओं और किसी भी चीज के लिए घटनाओं को परिभाषित करते हैं जिन्हें एप्लिकेशन नियंत्रकों के सामने जाना चाहिए refs कॉन्फ़िग सिर्फ एक लयबद्धता है - आप हमेशा myView.getSomeReference() तरह कुछ कॉल कर सकते हैं, और देखने के लिए अनुमति देता है जो वापस हो जाता है इस this.control('some > view > widget') बजाय this.control('some > view > widget') सिर्फ एक कस्टम इवेंट को दृश्य पर परिभाषित करता है और ऐसा करता है। this.control('myevent') जब वह विजेट कुछ ऐसा करता है जिसे नियंत्रक को इसके बारे में पता होना चाहिए। उस के रूप में आसान

दोष यह है कि इस दृष्टिकोण के लिए थोड़ी अधिक कोड की आवश्यकता होती है, और साधारण मामलों (जैसे उदाहरण) के लिए यह ओवरकिल हो सकता है लेकिन मैं मानता हूं कि वास्तविक अनुप्रयोगों, या किसी साझा विकास के लिए, यह एक बेहतर दृष्टिकोण है

तो हां, एप-स्तरीय नियंत्रकों को आंतरिक दृश्य नियंत्रण में बाध्यकारी है, स्वयं में, एक विरोधी-पैटर्न। लेकिन एक्सटेंशन के एमवीसी को इसकी आवश्यकता नहीं है, और यह स्वयं करना से बचने के लिए बहुत सरल है


  • 'पॉपअप' निर्देश बनाएं और इसे पॉपअप सामग्री के कंटेनर पर लागू करें
  • निर्देश में, सामग्री को एक पूर्ण स्थिति div में इसके नीचे मुखौटा div के साथ लपेटें।
  • निर्देश के भीतर से आवश्यक डीओएम पेड़ में 2 divs को स्थानांतरित करना ठीक है। स्क्रीन के केंद्र में पॉपअप को स्थिति देने के लिए कोड सहित किसी भी यूआई कोड निर्देशों में ठीक है।
  • नियंत्रक को एक बुलियन ध्वज बनाएं और बांधें। यह ध्वज दृश्यता को नियंत्रित करेगा।
  • स्कोप वैरिएबल बनाएं जो ठीक / रद्द फ़ंक्शन इत्यादि के लिए बॉन्ड करें।

एक उच्च स्तरीय उदाहरण जोड़ने के लिए संपादन (गैर कार्यात्मक)

<div id='popup1-content' popup='showPopup1'>
  ....
  ....
</div>


<div id='popup2-content' popup='showPopup2'>
  ....
  ....
</div>



.directive('popup', function() {
  var p = {
      link : function(scope, iElement, iAttrs){
           //code to wrap the div (iElement) with a abs pos div (parentDiv)
          // code to add a mask layer div behind 
          // if the parent is already there, then skip adding it again.
         //use jquery ui to make it dragable etc.
          scope.watch(showPopup, function(newVal, oldVal){
               if(newVal === true){
                   $(parentDiv).show();
                 } 
              else{
                 $(parentDiv).hide();
                }
          });
      }


   }
  return p;
});