MVC पैटर्न और स्विंग


80

डिज़ाइन पैटर्न में से एक जो मुझे "वास्तविक स्विंग जीवन" का एक वास्तविक समझ प्राप्त करने में सबसे मुश्किल लगता है वह है एमवीसी पैटर्न। मैं इस साइट पर बहुत सी पोस्टों के माध्यम से गया हूं जो पैटर्न पर चर्चा करते हैं, लेकिन मुझे अभी भी यह नहीं लगता है कि मुझे अपने जावा स्विंग एप्लिकेशन में पैटर्न का लाभ लेने की स्पष्ट समझ है।

मान लीजिए कि मेरे पास एक JFrame है जिसमें एक तालिका, कुछ पाठ फ़ील्ड और कुछ बटन हैं। मैं शायद एक अंतर्निहित डेटा मॉडल के साथ JTable को "ब्रिज" करने के लिए एक TableModel का उपयोग करूंगा। हालाँकि, फ़ील्ड को साफ़ करने, फ़ील्ड को मान्य करने, बटन क्रियाओं के साथ लॉकिंग फ़ील्ड के लिए ज़िम्मेदार सभी फ़ंक्शंस आमतौर पर सीधे JFrame में जाते हैं। हालांकि, क्या यह पैटर्न के नियंत्रक और दृश्य को नहीं मिलाता है?

जहां तक ​​मैं देख सकता हूं, मैं JTable (और मॉडल) को देखते हुए एमवीसी पैटर्न "सही ढंग से" लागू करने का प्रबंधन करता हूं, लेकिन जब मैं संपूर्ण JFrame को एक पूरे के रूप में देखता हूं तो चीजें मैला हो जाती हैं।

मैं वास्तव में यह सुनना चाहता हूं कि अन्य लोग इस संबंध में कैसे जाते हैं। जब आप MVC पैटर्न का उपयोग कर एक उपयोगकर्ता को एक तालिका, कुछ फ़ील्ड और कुछ बटन प्रदर्शित करने की आवश्यकता होती है, तो आप कैसे जाते हैं?


2
यहाँ एक संबंधित उदाहरण है
ट्रैशगॉड

इस पार्टी में आने वाले किसी और के लिए - स्विंग एक शुद्ध MVC नहीं है - यह अवधारणा से भारी उधार लेता है, लेकिन "दृश्य और नियंत्रक" को एक साथ "ढह जाता है"
MadProgrammer

जवाबों:


106

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

संक्षिप्त सारांश

  1. आप उपयोगकर्ता हैं - आप दृश्य के साथ सहभागिता करते हैं। दृश्य मॉडल के लिए आपकी विंडो है। जब आप दृश्य के लिए कुछ करते हैं (जैसे कि प्ले बटन पर क्लिक करें) तब दृश्य नियंत्रक को बताता है कि आपने क्या किया। इसे संभालना नियंत्रक का काम है।

  2. नियंत्रक मॉडल को अपनी स्थिति बदलने के लिए कहता है। नियंत्रक आपके कार्यों को लेता है और उनकी व्याख्या करता है। यदि आप एक बटन पर क्लिक करते हैं, तो नियंत्रक का काम यह पता लगाना है कि उस क्रिया के आधार पर मॉडल का क्या मतलब है और कैसे हेरफेर किया जाना चाहिए।

  3. नियंत्रक दृश्य को बदलने के लिए भी कह सकता है। जब नियंत्रक को दृश्य से कोई कार्रवाई मिलती है, तो परिणामस्वरूप देखने के लिए दृश्य को बताने की आवश्यकता हो सकती है। उदाहरण के लिए, नियंत्रक इंटरफ़ेस में कुछ बटन या मेनू आइटम को सक्षम या अक्षम कर सकता है।

  4. जब राज्य बदल गया है तो मॉडल उस दृश्य को सूचित करता है। जब मॉडल में कुछ परिवर्तन होता है, तो आपके द्वारा की गई कुछ क्रियाओं के आधार पर (जैसे कि एक बटन पर क्लिक करना) या कुछ अन्य आंतरिक परिवर्तन (जैसे कि प्लेलिस्ट में अगला गीत शुरू हो गया है), मॉडल उस दृश्य को सूचित करता है कि उसका राज्य बदल गया है।

  5. दृश्य राज्य के लिए मॉडल पूछता है। दृश्य उस स्थिति को प्राप्त करता है जिसे वह मॉडल से सीधे प्रदर्शित करता है। उदाहरण के लिए, जब मॉडल इस दृश्य को सूचित करता है कि एक नया गाना बजना शुरू हो गया है, तो दृश्य मॉडल से गीत के नाम का अनुरोध करता है और उसे प्रदर्शित करता है। दृश्य राज्य के लिए मॉडल भी पूछ सकता है क्योंकि नियंत्रक दृश्य में कुछ परिवर्तन का अनुरोध करता है।

यहाँ छवि विवरण दर्ज करें स्रोत (यदि आप सोच रहे हैं कि एक "मलाईदार नियंत्रक" क्या है, तो एक Oreo कुकी के बारे में सोचें, जिसमें नियंत्रक मलाईदार केंद्र है, दृश्य शीर्ष बिस्किट है और मॉडल नीचे बिस्किट है।)

उम, यदि आप रुचि रखते हैं, तो आप यहाँ से एमवीसी पैटर्न के बारे में काफी मनोरंजक गीत डाउनलोड कर सकते हैं !

एक समस्या जिसे आप स्विंग प्रोग्रामिंग के साथ सामना कर सकते हैं, जिसमें एमवीसी पैटर्न के साथ स्विंगवॉकर और ईवेंटडिस्पैच थ्रेड को समाहित करना शामिल है। आपके कार्यक्रम के आधार पर, आपके विचार या नियंत्रक को SwingWorker को विस्तारित करना और उस doInBackground()विधि को ओवरराइड करना पड़ सकता है जहां संसाधन गहन तर्क रखा गया है। यह आसानी से विशिष्ट MVC पैटर्न के साथ फ्यूज किया जा सकता है, और स्विंग अनुप्रयोगों के विशिष्ट है।

EDIT # 1 :

इसके अतिरिक्त, एमवीसी को विभिन्न पैटर्नों के समग्र के रूप में मानना ​​महत्वपूर्ण है। उदाहरण के लिए, आपका मॉडल ऑब्जर्वर पैटर्न (मॉडल के लिए एक पर्यवेक्षक के रूप में पंजीकृत होने की आवश्यकता को देखते हुए) को लागू किया जा सकता है, जबकि आपका नियंत्रक रणनीति पैटर्न का उपयोग कर सकता है।

EDIT # 2 :

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


मैंने वास्तविक रूप से पुस्तक पढ़ी है, लेकिन मैंने SWING के पैटर्न को लागू करना कठिन पाया है। मैंने कुछ स्थानों पर पढ़ा है कि एक JFrame भी एक दृश्य और एक नियंत्रक का प्रतिनिधित्व करते हुए देखा जा सकता है।
सब्रतला

... JFrame एक घटक है, और एक पत्ती नहीं। आमतौर पर, नियंत्रक द्वारा किए गए अपडेट JFrame को भेजे जाते हैं, जो बाकी का ख्याल रखता है, इसलिए, यह इसे एक नियंत्रक होने का भ्रम दे सकता है, लेकिन वास्तव में, यह मामला नहीं है क्योंकि यह मॉडल नहीं बदला है, केवल दृश्य। यदि आपके JFrame ने किसी तरह सीधे मॉडल को बदल दिया है - तो आप इसे गलत कर रहे हैं।
ध्रुव गरोला

... फिर, यहाँ कीवर्ड "सीधे" है। आपके मामले में, आप मेज पर mececlicks को सुन सकते हैं, और तर्क में विधियों को तर्क भेज सकते हैं जो तालिका मॉडल को संशोधित करते हैं।
ध्रुव गरोला

2
@ ध्रुवगैरोला दूसरा बिंदु विवरण तीसरे बिंदु के लिए है, तीसरा और अंकों के लिए समान डुप्लिकेट विवरण है। क्या आप उन्हें सही कर सकते हैं
नारुतो बीजू मोड

वह गीत एक क्लासिक है! =D
अहिजाज़ा

36

मुझे यह देखने का विचार पसंद नहीं है कि मॉडल द्वारा अधिसूचित होने पर इसका डेटा बदल जाता है। मैं उस कार्यक्षमता को नियंत्रक को सौंपूंगा। उस स्थिति में, यदि आप एप्लिकेशन लॉजिक बदलते हैं, तो आपको व्यू कोड में हस्तक्षेप करने की आवश्यकता नहीं है। दृश्य का कार्य केवल अनुप्रयोगों के घटकों के लिए है + लेआउट कुछ ज्यादा नहीं कुछ कम नहीं। स्विंग में लेआउट करना पहले से ही एक क्रियात्मक कार्य है, इसे एप्लिकेशन लॉजिक में हस्तक्षेप क्यों करने दें?

एमवीसी का मेरा विचार (जो मैं वर्तमान में काम कर रहा हूं, अब तक बहुत अच्छा है):

  1. नजारा तीनों का सबसे बिंदास है। यह नियंत्रक और मॉडल के बारे में कुछ भी नहीं जानता है। इसकी चिंता केवल स्विंग घटकों के प्रोस्थेटिक्स और लेआउट है।
  2. मॉडल भी गूंगा है, लेकिन देखने में उतना गूंगा नहीं है। यह निम्नलिखित कार्य करता है।
    • ए। जब इसके सेटर को नियंत्रक द्वारा बुलाया जाता है, तो यह अपने श्रोताओं / पर्यवेक्षकों को सूचना देगा (जैसे मैंने कहा था, मैं इस भूमिका को नियंत्रक के पास भेजूंगा)। मैं SwingPropertyChangeSupport पसंद करता हूं इसे प्राप्त करने के लिए को क्योंकि यह इस उद्देश्य के लिए पहले से ही अनुकूलित है।
    • बी डेटाबेस इंटरैक्शन कार्यक्षमता।
  3. एक बहुत ही स्मार्ट नियंत्रक। दृश्य और मॉडल को बहुत अच्छी तरह से जानता है। नियंत्रक के दो कार्य हैं:
    • ए। यह उस क्रिया को परिभाषित करता है जो उपयोगकर्ता द्वारा इंटरैक्ट करने पर दृश्य निष्पादित होगा।
    • बी यह मॉडल को सुनता है। जैसा मैंने कहा है, जब मॉडल के सेटर को बुलाया जाता है, तो मॉडल नियंत्रक को सूचना देगा। इस अधिसूचना की व्याख्या करना नियंत्रक का काम है। यह दृश्य में परिवर्तन को प्रतिबिंबित करने की आवश्यकता हो सकती है।

कोड नमूना

देखें:

जैसा मैंने कहा कि दृश्य बनाना पहले से ही क्रियात्मक है इसलिए केवल अपना स्वयं का कार्यान्वयन बनाएं :)

interface View{
    JTextField getTxtFirstName();
    JTextField getTxtLastName();
    JTextField getTxtAddress();
}

परीक्षण योग्य उद्देश्यों के लिए तीनों को इंटरफ़ेस करना आदर्श है। मैंने केवल मॉडल और नियंत्रक के अपने कार्यान्वयन को प्रदान किया।

आदर्श :

public class MyImplementationOfModel implements Model{
    ...
    private SwingPropertyChangeSupport propChangeFirer;
    private String address;
    private String firstName;
    private String lastName;

    public MyImplementationOfModel() {
        propChangeFirer = new SwingPropertyChangeSupport(this);
    }
    public void addListener(PropertyChangeListener prop) {
        propChangeFirer.addPropertyChangeListener(prop);
    }
    public void setAddress(String address){
        String oldVal = this.address;
        this.address = address;

        //after executing this, the controller will be notified that the new address has been set. Its then the controller's
        //task to decide what to do when the address in the model has changed. Ideally, the controller will update the view about this
        propChangeFirer.firePropertyChange("address", oldVal, address);
    }
    ...
    //some other setters for other properties & code for database interaction
    ...
}

नियंत्रक :

public class MyImplementationOfController implements PropertyChangeListener, Controller{

    private View view;
    private Model model;

    public MyImplementationOfController(View view, Model model){
        this.view = view;
        this.model = model;

        //register the controller as the listener of the model
        this.model.addListener(this);

        setUpViewEvents();
    }

    //code for setting the actions to be performed when the user interacts to the view.
    private void setUpViewEvents(){
        view.getBtnClear().setAction(new AbstractAction("Clear") { 
            @Override
            public void actionPerformed(ActionEvent arg0) {
                model.setFirstName("");
                model.setLastName("");
                model.setAddress("");
            }
        });

        view.getBtnSave().setAction(new AbstractAction("Save") { 
            @Override
            public void actionPerformed(ActionEvent arg0) {
                ...
                //validate etc.
                ...
                model.setFirstName(view.getTxtFName().getText());
                model.setLastName(view.getTxtLName().getText());
                model.setAddress(view.getTxtAddress().getText());
                model.save();
            }
        });
    }

    public void propertyChange(PropertyChangeEvent evt){
        String propName = evt.getPropertyName();
        Object newVal = evt.getNewValue();

        if("address".equalsIgnoreCase(propName)){
            view.getTxtAddress().setText((String)newVal);
        }
        //else  if property (name) that fired the change event is first name property
        //else  if property (name) that fired the change event is last name property
    }
}

मुख्य, जहां MVC सेटअप है:

public class Main{
    public static void main(String[] args){
        View view = new YourImplementationOfView();
        Model model = new MyImplementationOfModel();

        ...
        //create jframe
        //frame.add(view.getUI());
        ...

        //make sure the view and model is fully initialized before letting the controller control them.
        Controller controller = new MyImplementationOfController(view, model);

        ...
        //frame.setVisible(true);
        ...
    }
}

4
दिलचस्प है लेकिन यह कम कुशल है जब एक एकल इकाई मॉडल को कई दृश्यों में प्रदर्शित किया जाता है ... तब आपका डिज़ाइन एकल मॉडल को संभालते हुए "बड़े नियंत्रक" को जन्म दे सकता है लेकिन सभी संबंधित विचारों को प्रबंधित कर सकता है। और यह और भी पेचीदा हो जाता है यदि आप "छोटे मॉडल" के एक सेट का पुन: उपयोग करने का प्रयास करते हैं, तो "बड़े मॉडल" में एकत्रीकरण के लिए धन्यवाद क्योंकि एक दृश्य कई "छोटे मॉडल" संस्थाओं में प्रेषित जानकारी प्रदर्शित करता है।
य्वेस मार्टिन

1
@onepotato मैंने अभी आपके कोड आज़माए हैं। जब मैं एक बटन दबाता हूं, तो मुझे आग लगाने के लिए setUpViewEvents () में कोड मिल सकते हैं। हालाँकि, जब मैं एक model.setSomething (123) करता हूं, तो प्रॉपर्टीचेंज में कोड फेयर नहीं होते हैं। मैंने सीधे ऑब्जेक्ट newVal = evt.getNewValue () के तहत एक प्रिंटल भी डाला; और यह प्रिंट नहीं करता है।
एमुलेटेक्सहार्ट

10
यह एमवीसी वास्तुशिल्प पैटर्न नहीं है, लेकिन निकटता से संबंधित एमवीपी (मॉडल-व्यू-प्रस्तोता) पैटर्न है। एक सामान्य एमवीसी में, जब यह बदल गया है , तो दृश्य को सूचित करने के लिए मॉडल का काम ठीक है कि आप क्या पसंद नहीं करते हैं। पर देखो इस चित्र एक ठेठ MVC काम में कैसे बातचीत को देखने के लिए।
मैक्सएक्सहैक्स

25

MVC पैटर्न एक मॉडल है कि उपयोगकर्ता इंटरफ़ेस को कैसे संरचित किया जा सकता है। इसलिए यह 3 तत्वों को परिभाषित करता है मॉडल, दृश्य, नियंत्रक:

  • मॉडल A मॉडल उपयोगकर्ता को प्रस्तुत की जाने वाली चीज़ का एक अमूर्त हिस्सा है। स्विंग में आपके पास गुई मॉडल और डेटा मॉडल का अंतर होता है। GUI मॉडल एक ऐसी ui घटक की स्थिति को अमूर्त करते हैं जैसे ButtonModel । डेटा मॉडल ने संरचित डेटा को संरचित किया जो यूआई उपयोगकर्ता को TableModel की तरह प्रस्तुत करता है ।
  • दृश्य देखें एक यूआई घटक है जो उपयोगकर्ता को डेटा प्रस्तुत करने के लिए जिम्मेदार है। इस प्रकार यह लेआउट की तरह सभी ui निर्भर मुद्दों के लिए जिम्मेदार है, ड्राइंग, आदि जैसे JTable
  • नियंत्रक एक उपयोगकर्ता इंटरैक्शन (माउस गति, माउस क्लिक, कुंजी प्रेस, आदि) के क्रम में निष्पादित एप्लिकेशन कोड को एन्क्रिप्ट करता है। नियंत्रकों को उनके निष्पादन के लिए इनपुट की आवश्यकता हो सकती है और वे आउटपुट का उत्पादन करते हैं। वे निष्पादन के परिणामस्वरूप मॉडल और अपडेट मॉडल से अपने इनपुट को पढ़ते हैं। वे यूआई का पुनर्गठन भी कर सकते हैं (उदाहरण के लिए यूआई घटकों को बदलें या एक नया दृश्य दिखाएं)। हालाँकि, उन्हें ui कंपोनेंट्स के बारे में नहीं पता होना चाहिए, क्योंकि आप एक अलग इंटरफ़ेस में रिस्ट्रक्चरिंग को इनकैप्सुलेट कर सकते हैं जिसे कंट्रोलर केवल इनवाइट करता है। स्विंग में एक नियंत्रक आमतौर पर एक्शनलिस्ट या एक्शन द्वारा लागू किया जाता है ।

उदाहरण

  • लाल = मॉडल
  • हरा = दृश्य
  • नीला = नियंत्रक

यहाँ छवि विवरण दर्ज करें

जब Buttonक्लिक किया जाता है तो यह इनवॉइस करता है ActionListenerActionListenerकेवल अन्य मॉडलों पर निर्भर करता है। यह कुछ मॉडल का उपयोग करता है क्योंकि यह इनपुट और अन्य है क्योंकि यह परिणाम या आउटपुट है यह विधि तर्कों और वापसी मूल्यों की तरह है। मॉडल अपडेट होने पर यूआई को सूचित करते हैं। इसलिए यूआई घटक को जानने के लिए नियंत्रक तर्क की आवश्यकता नहीं है। मॉडल ऑब्जेक्ट्स ui को नहीं जानते हैं। अधिसूचना एक पर्यवेक्षक पैटर्न द्वारा की जाती है। इस प्रकार मॉडल ऑब्जेक्ट केवल यह जानते हैं कि कोई है जो मॉडल को बदलने के लिए अधिसूचित करना चाहता है।

जावा स्विंग में कुछ घटक होते हैं जो एक मॉडल और नियंत्रक को भी लागू करते हैं। जैसे javax.swing.Action । यह एक यूआई मॉडल (गुण: सक्षमता, छोटा आइकन, नाम, आदि) को लागू करता है और एक नियंत्रक है क्योंकि यह एक्टिविस्टनर का विस्तार करता है ।

एक विस्तृत विवरण, उदाहरण अनुप्रयोग और स्रोत कोड : https://www.link-intersystems.com/blog/2013/07/20/the-mvc-pattern-implemented-with-java-swing/

260 से कम लाइनों में एमवीसी मूल बातें:

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.List;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.DefaultListModel;
import javax.swing.DefaultListSelectionModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.WindowConstants;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.PlainDocument;

public class Main {

    public static void main(String[] args) {
        JFrame mainFrame = new JFrame("MVC example");
        mainFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        mainFrame.setSize(640, 300);
        mainFrame.setLocationRelativeTo(null);

        PersonService personService = new PersonServiceMock();

        DefaultListModel searchResultListModel = new DefaultListModel();
        DefaultListSelectionModel searchResultSelectionModel = new DefaultListSelectionModel();
        searchResultSelectionModel
                .setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        Document searchInput = new PlainDocument();

        PersonDetailsAction personDetailsAction = new PersonDetailsAction(
                searchResultSelectionModel, searchResultListModel);
        personDetailsAction.putValue(Action.NAME, "Person Details");

        Action searchPersonAction = new SearchPersonAction(searchInput,
                searchResultListModel, personService);
        searchPersonAction.putValue(Action.NAME, "Search");

        Container contentPane = mainFrame.getContentPane();

        JPanel searchInputPanel = new JPanel();
        searchInputPanel.setLayout(new BorderLayout());

        JTextField searchField = new JTextField(searchInput, null, 0);
        searchInputPanel.add(searchField, BorderLayout.CENTER);
        searchField.addActionListener(searchPersonAction);

        JButton searchButton = new JButton(searchPersonAction);
        searchInputPanel.add(searchButton, BorderLayout.EAST);

        JList searchResultList = new JList();
        searchResultList.setModel(searchResultListModel);
        searchResultList.setSelectionModel(searchResultSelectionModel);

        JPanel searchResultPanel = new JPanel();
        searchResultPanel.setLayout(new BorderLayout());
        JScrollPane scrollableSearchResult = new JScrollPane(searchResultList);
        searchResultPanel.add(scrollableSearchResult, BorderLayout.CENTER);

        JPanel selectionOptionsPanel = new JPanel();

        JButton showPersonDetailsButton = new JButton(personDetailsAction);
        selectionOptionsPanel.add(showPersonDetailsButton);

        contentPane.add(searchInputPanel, BorderLayout.NORTH);
        contentPane.add(searchResultPanel, BorderLayout.CENTER);
        contentPane.add(selectionOptionsPanel, BorderLayout.SOUTH);

        mainFrame.setVisible(true);
    }

}

class PersonDetailsAction extends AbstractAction {

    private static final long serialVersionUID = -8816163868526676625L;

    private ListSelectionModel personSelectionModel;
    private DefaultListModel personListModel;

    public PersonDetailsAction(ListSelectionModel personSelectionModel,
            DefaultListModel personListModel) {
        boolean unsupportedSelectionMode = personSelectionModel
                .getSelectionMode() != ListSelectionModel.SINGLE_SELECTION;
        if (unsupportedSelectionMode) {
            throw new IllegalArgumentException(
                    "PersonDetailAction can only handle single list selections. "
                            + "Please set the list selection mode to ListSelectionModel.SINGLE_SELECTION");
        }
        this.personSelectionModel = personSelectionModel;
        this.personListModel = personListModel;
        personSelectionModel
                .addListSelectionListener(new ListSelectionListener() {

                    public void valueChanged(ListSelectionEvent e) {
                        ListSelectionModel listSelectionModel = (ListSelectionModel) e
                                .getSource();
                        updateEnablement(listSelectionModel);
                    }
                });
        updateEnablement(personSelectionModel);
    }

    public void actionPerformed(ActionEvent e) {
        int selectionIndex = personSelectionModel.getMinSelectionIndex();
        PersonElementModel personElementModel = (PersonElementModel) personListModel
                .get(selectionIndex);

        Person person = personElementModel.getPerson();
        String personDetials = createPersonDetails(person);

        JOptionPane.showMessageDialog(null, personDetials);
    }

    private String createPersonDetails(Person person) {
        return person.getId() + ": " + person.getFirstName() + " "
                + person.getLastName();
    }

    private void updateEnablement(ListSelectionModel listSelectionModel) {
        boolean emptySelection = listSelectionModel.isSelectionEmpty();
        setEnabled(!emptySelection);
    }

}

class SearchPersonAction extends AbstractAction {

    private static final long serialVersionUID = 4083406832930707444L;

    private Document searchInput;
    private DefaultListModel searchResult;
    private PersonService personService;

    public SearchPersonAction(Document searchInput,
            DefaultListModel searchResult, PersonService personService) {
        this.searchInput = searchInput;
        this.searchResult = searchResult;
        this.personService = personService;
    }

    public void actionPerformed(ActionEvent e) {
        String searchString = getSearchString();

        List<Person> matchedPersons = personService.searchPersons(searchString);

        searchResult.clear();
        for (Person person : matchedPersons) {
            Object elementModel = new PersonElementModel(person);
            searchResult.addElement(elementModel);
        }
    }

    private String getSearchString() {
        try {
            return searchInput.getText(0, searchInput.getLength());
        } catch (BadLocationException e) {
            return null;
        }
    }

}

class PersonElementModel {

    private Person person;

    public PersonElementModel(Person person) {
        this.person = person;
    }

    public Person getPerson() {
        return person;
    }

    @Override
    public String toString() {
        return person.getFirstName() + ", " + person.getLastName();
    }
}

interface PersonService {

    List<Person> searchPersons(String searchString);
}

class Person {

    private int id;
    private String firstName;
    private String lastName;

    public Person(int id, String firstName, String lastName) {
        this.id = id;
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public int getId() {
        return id;
    }

    public String getFirstName() {
        return firstName;
    }

    public String getLastName() {
        return lastName;
    }

}

class PersonServiceMock implements PersonService {

    private List<Person> personDB;

    public PersonServiceMock() {
        personDB = new ArrayList<Person>();
        personDB.add(new Person(1, "Graham", "Parrish"));
        personDB.add(new Person(2, "Daniel", "Hendrix"));
        personDB.add(new Person(3, "Rachel", "Holman"));
        personDB.add(new Person(4, "Sarah", "Todd"));
        personDB.add(new Person(5, "Talon", "Wolf"));
        personDB.add(new Person(6, "Josephine", "Dunn"));
        personDB.add(new Person(7, "Benjamin", "Hebert"));
        personDB.add(new Person(8, "Lacota", "Browning "));
        personDB.add(new Person(9, "Sydney", "Ayers"));
        personDB.add(new Person(10, "Dustin", "Stephens"));
        personDB.add(new Person(11, "Cara", "Moss"));
        personDB.add(new Person(12, "Teegan", "Dillard"));
        personDB.add(new Person(13, "Dai", "Yates"));
        personDB.add(new Person(14, "Nora", "Garza"));
    }

    public List<Person> searchPersons(String searchString) {
        List<Person> matches = new ArrayList<Person>();

        if (searchString == null) {
            return matches;
        }

        for (Person person : personDB) {
            if (person.getFirstName().contains(searchString)
                    || person.getLastName().contains(searchString)) {
                matches.add(person);
            }

        }
        return matches;
    }
}

एमवीसी मूल बातें स्क्रेन्कास्ट


4
मुझे यह उत्तर +1 पसंद है, Actionजैसा Controllerकि उल्लेख के लिए वास्तव में मुझे लगता है कि सभी EventListenerनियंत्रक हैं ..
nachokk

@nachokk हाँ, वास्तव में। जैसा कि मैंने कहा A controller encapsulates the application code that is executed in order to an user interaction। माउस को ले जाना, एक घटक पर क्लिक करना, एक कुंजी दबाएं, आदि सभी उपयोगकर्ता इंटरैक्शन हैं। इसे और अधिक स्पष्ट करने के लिए मैंने अपना उत्तर अपडेट किया।
रेने लिंक

2

आप एक अलग, सादे जावा वर्ग और दूसरे में नियंत्रक में मॉडल बना सकते हैं।

फिर आप उस के ऊपर स्विंग घटक हो सकते हैं। JTableविचारों में से एक होगा (और तालिका मॉडल वास्तव में दृश्य का हिस्सा होगा - यह केवल "साझा मॉडल" से JTable) में अनुवाद करेगा ।

जब भी तालिका को संपादित किया जाता है, तो इसका तालिका मॉडल कुछ अद्यतन करने के लिए "मुख्य नियंत्रक" बताता है। हालाँकि, नियंत्रक को तालिका के बारे में कुछ नहीं पता होना चाहिए। तो कॉल को अधिक दिखना चाहिए:, updateCustomer(customer, newValue)नहीं updateCustomer(row, column, newValue)

साझा मॉडल के लिए श्रोता (पर्यवेक्षक) इंटरफ़ेस जोड़ें। कुछ घटक (जैसे आपकी तालिका) इसे सीधे लागू कर सकते हैं। एक अन्य पर्यवेक्षक नियंत्रक हो सकता है जो बटन उपलब्धता आदि का समन्वय करता है।


यह करने का एक तरीका है, लेकिन निश्चित रूप से आप इसे सरल या विस्तारित कर सकते हैं यदि इसका उपयोग आपके मामले के लिए ओवरकिल है।

आप नियंत्रक को मॉडल के साथ मर्ज कर सकते हैं और समान श्रेणी प्रक्रिया अपडेट कर सकते हैं और घटक उपलब्धता बनाए रख सकते हैं। आप "साझा किए गए मॉडल" को भी बना सकते हैं TableModel(हालांकि यदि यह केवल तालिका द्वारा उपयोग नहीं किया जाता है, तो मैं कम से कम एक फ्रेंडली एपीआई प्रदान करने की सलाह दूंगा जो तालिका सार को लीक नहीं करता है)

दूसरी ओर, यदि आप अद्यतन के लिए जटिल इंटरफेस हो सकता है ( CustomerUpdateListener, OrderItemListener,OrderCancellationListener ) और समर्पित नियंत्रक (या मध्यस्थ) केवल अलग विचारों के समन्वय के लिए।

यह इस बात पर निर्भर करता है कि आपकी समस्या कितनी जटिल है।


सभी दृश्यों में से लगभग 90% में एक तालिका होती है जहां उपयोगकर्ता एक तत्व को संपादित करने के लिए चुन सकता है। अब तक मैंने जो किया है, वह यह है कि मेरे पास एक डेटा मॉडल है जिसके माध्यम से सभी CRUD ऑपरेशन होते हैं। मैं JTable के डेटा मॉडल को अनुकूलित करने के लिए एक TableModel का उपयोग करता हूं। इसलिए, एक तत्व को अपडेट करने के लिए मैं table.getModel ()। GetModel ()। Update (Element e) को आमंत्रित करेगा। दूसरे शब्दों में, JTable का अभी नियंत्रक है। सभी बटन क्रियाओं को अलग-अलग कक्षाओं में रखा जाता है (मैं उन्हें अलग-अलग संदर्भों में पुन: उपयोग करता हूं) और अंतर्निहित मॉडल के तरीकों के माध्यम से अपना काम करता हूं। क्या यह एक व्यवहार्य डिजाइन है?
सब्रतला

1

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

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

हालाँकि आप इसे कार्यान्वित करते हैं, अपनी कक्षाओं, विधियों और पैकेजों को जावाडॉक के लिए याद रखें ताकि घटकों और उनके रिश्तों का सही वर्णन हो!


@AndyT जबकि आपका अधिकांश स्पष्टीकरण अच्छा है, मेरे पास नियंत्रक के साथ मॉडल के संयोजन पर आपकी सलाह के साथ एक समस्या है। क्या होगा अगर मैं अचानक नियंत्रक बदलना चाहता हूं? अब मुझे लगता है कि आपने मॉडल को नियंत्रक के साथ युग्मित किया है और मॉडल को भी संशोधित करने की आवश्यकता है। आपका कोड अब एक्स्टेंसिबल नहीं है। हालांकि आपका नियंत्रक कम है, मैं इसे किसी मॉडल के साथ नहीं जोड़ूंगा। या एक दृश्य।
ध्रुव गरोला

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

@AndyT सहमत हैं, शायद अगर आप एप्लिकेशन छोटे हैं, तो यह सबसे तेज़ तरीका हो सकता है। लेकिन एक्स्टेंसिबिलिटी की खातिर (विचार करें कि यदि जोड़ एक ही प्रोग्रामर द्वारा नहीं किया गया है), तो यह नुकसान के रूप में काम कर सकता है।
ध्रुव गरोला

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

1
@AndyT: आत्मज्ञान का मार्ग बर्तन के छेद, साँप के तेल के सेल्समैन और आत्म-धर्मी से भरा हुआ है। हालांकि, चीजों को सरल रखने के लिए एक को पढ़ाने के लिए निरंतर समय के लिए अपने स्वयं के शौच में चारदीवारी करने जैसा कुछ भी नहीं है। डिजाइन पैटर्न में कुछ भी गलत नहीं है। हालाँकि, डिज़ाइन पैटर्न जानना सॉफ़्टवेयर डिज़ाइन को जानना समान नहीं है। कुकबुक अप्रोच का उपयोग करके कभी भी कोई सॉफ्टवेयर सॉफ्टवेयर नहीं बनाया गया है। उच्च-प्रदर्शन सॉफ़्टवेयर डिज़ाइन करना जो आवश्यकताओं को पूरा करता है और बनाए रखना आसान है अभी भी एक कला है जिसे मास्टर करने के लिए वर्षों की आवश्यकता होती है।
बिट-ट्विडलर


0

यदि आप GUI के साथ एक प्रोग्राम विकसित करते हैं , तो mvc पैटर्न लगभग वहीं है लेकिन धुंधला है।

मॉडल, दृश्य और नियंत्रक कोड को डिसाइड करना मुश्किल है, और आम तौर पर केवल एक रिफलेक्टर कार्य नहीं है।

आपको पता है कि आपके पास यह तब है जब आपका कोड पुन: प्रयोज्य हो। यदि आपने एमवीसी को सही ढंग से लागू किया है, तो टीयूआई या सीएलआई या आरडब्ल्यूडी या मोबाइल पहला डिज़ाइन लागू करना आसान होना चाहिए तो एक ही कार्यक्षमता के साथ । यह देखने के लिए आसान है कि यह वास्तव में, मौजूदा कोड पर इसके अलावा है।

वास्तव में, मॉडल, दृश्य और नियंत्रक के बीच अन्य अलगाव पैटर्न (पर्यवेक्षक या श्रोता के रूप में) का उपयोग करके बातचीत होती है

मुझे लगता है कि यह पोस्ट इसे विस्तार से बताती है, प्रत्यक्ष गैर एमवीसी पैटर्न से (जैसा कि आप क्यू एंड डी पर करेंगे ) अंतिम पुन: प्रयोज्य कार्यान्वयन के लिए:

http://www.austintek.com/mvc/

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.