Шаблон MVC та гойдалки


80

Одним із шаблонів дизайну, який мені найважче зрозуміти в "реальному житті Свінг", є шаблон MVC. Я переглянув чимало публікацій на цьому веб-сайті, де обговорюється шаблон, але я все ще не відчуваю, що я чітко розумію, як скористатися шаблоном у моїй програмі Java Swing.

Скажімо, у мене є JFrame, який містить таблицю, пару текстових полів і кілька кнопок. Я б, мабуть, використовував TableModel, щоб "з'єднати" JTable з базовою моделлю даних. Однак усі функції, що відповідають за очищення полів, перевірку полів, блокування полів разом із діями кнопок, зазвичай надходять безпосередньо в JFrame. Однак хіба це не поєднує контролер і вигляд шаблону?

Наскільки я бачу, мені вдається "правильно" реалізувати шаблон MVC, дивлячись на JTable (і модель), але все стає каламутним, коли я дивлюсь на всю JFrame в цілому.

Я б дуже хотів почути, як інші ставляться до цього. Як ви працюєте, коли вам потрібно показати таблицю, пару полів та деякі кнопки користувачеві, використовуючи шаблон MVC?


2
Ось відповідний приклад .
trashgod

Для всіх, хто прийде на цю вечірку - Swing - це НЕ чистий MVC - він запозичує важку концепцію, але "
руйнує

Відповіді:


106

Книгою, яку я настійно рекомендую для MVC в розмаху, була б "Шаблони дизайну на перший план" Фрімена та Фрімена. Вони мають дуже вичерпне пояснення MVC.

Стислий підсумок

  1. Ви користувач - ви взаємодієте з поданням. Вигляд - це ваше вікно до моделі. Коли ви щось робите для подання (наприклад, натискаєте кнопку Відтворити), тоді подання повідомляє контролеру, що ви зробили. Завдання контролера - це впоратись.

  2. Контролер просить модель змінити свій стан. Контролер приймає ваші дії та інтерпретує їх. Якщо натиснути на кнопку, завдання контролера - з’ясувати, що це означає, і яким чином слід керувати моделлю на основі цієї дії.

  3. Контролер також може попросити вигляд змінити. Коли контролер отримує дію з подання, йому може знадобитися повідомити про те, щоб в результаті змінився вигляд. Наприклад, контролер може вмикати або вимикати певні кнопки або пункти меню в інтерфейсі.

  4. Модель повідомляє подання, коли його стан змінився. Коли щось змінюється в моделі, виходячи з якоїсь дії, яку ви вчинили (наприклад, натискання кнопки), або якоїсь іншої внутрішньої зміни (наприклад, розпочалася наступна пісня у списку відтворення), модель повідомляє уявлення про те, що її стан змінився.

  5. Погляд задає модель стану. Вигляд отримує стан, який він відображає, безпосередньо з моделі. Наприклад, коли модель повідомляє представлення про те, що почала відтворюватися нова пісня, подання запитує назву моделі у пісні та відображає її. Подання може також запитувати модель про стан як результат того, що контролер вимагає певних змін у поданні.

введіть тут опис зображення Джерело (Якщо вам цікаво, що таке "вершковий контролер", згадайте печиво Oreo, де контролер - це кремовий центр, вид - верхній бісквіт, а модель - нижній бісквіт.)

Гм, в разі , якщо ви зацікавлені, ви можете завантажити досить розважальну пісню про шаблон MVC з тут !

Одна проблема, з якою ви можете зіткнутися з програмуванням Swing, полягає у об’єднанні потоку SwingWorker та EventDispatch із шаблоном MVC. Залежно від вашої програми, вашому поданню або контролеру може знадобитися розширити SwingWorker і замінити doInBackground()метод, де розміщується ресурсомістка логіка. Це можна легко поєднати з типовим шаблоном MVC, і це характерно для додатків Swing.

EDIT # 1 :

Крім того, важливо розглядати MVC як свого роду композит з різних моделей. Наприклад, ваша модель може бути реалізована за допомогою шаблону Observer (вимагає, щоб View був зареєстрований як спостерігач для моделі), тоді як ваш контролер може використовувати шаблон Strategy.

EDIT # 2 :

Я хотів би додатково відповісти на ваше запитання. Ви повинні відобразити кнопки таблиці тощо у поданні, що, очевидно, реалізує ActionListener. У вашому actionPerformed()методі ви виявляєте подію та надсилаєте її до відповідного методу в контролері (пам’ятайте - у поданні є посилання на контролер). Отже, коли натискається кнопка, подія виявляється видом, надсилається до методу контролера, контролер може безпосередньо попросити подання відключити кнопку або щось інше. Далі контролер буде взаємодіяти та модифікувати модель (яка в основному матиме методи getter та setter, а також деякі інші для реєстрації та сповіщення спостерігачів тощо). Як тільки модель буде змінена, вона викличе оновлення для зареєстрованих спостерігачів (це буде вигляд у вашому випадку). Отже, тепер подання оновиться.


Я справді прочитав книгу, але мені було важко застосувати шаблон до SWING. Я також прочитав кілька місць, де читали, що JFrame також можна розглядати як як представляє подання, так і контролер.
sbrattla

... JFrame - це компонент, а не лист. як правило, оновлення, зроблені контролером, надсилаються до JFrame, який піклується про решту, отже, це може створити ілюзію, що це контролер, але насправді це не так, оскільки він не змінив модель, лише вид. якщо ваш JFrame якось змінив модель безпосередньо - ви робите це неправильно.
Дхрюв Гайрола

... знову ж таки, ключове слово тут "безпосередньо". у вашому випадку ви можете прослухати клацання миші в таблиці та надіслати логіку методам у контролері, які модифікують модель таблиці.
Дхрув Гайрола

2
@DhruvGairola Опис другого пункту стосується третього пункту, третій і для пунктів мають однакові дубльовані описи. Ви можете виправити їх, будь ласка.
Режим Наруто Біджу

Ця пісня - класика! =D
aaiezza

36

Мені не подобається ідея того, що подання повідомляється моделлю при зміні даних. Я передав би цю функцію контролеру. У цьому випадку, якщо ви зміните логіку програми, вам не потрібно втручатися в код представлення даних. Завдання подання призначене лише для компонентів додатків + макет, ні більше, ні менше. Макетування в swing - це вже багатослівне завдання, чому нехай воно заважає логіці програм?

Моє уявлення про MVC (з яким я зараз працюю, поки що добре):

  1. Вид є найнімішим із трьох. Він нічого не знає про контролер та модель. Його проблема полягає лише в протезуванні та компонуванні гойдалок.
  2. Модель теж німа, але не така німа, як вигляд. Він виконує наступні функціональні можливості.
    • a. коли контролер викликає одного зі своїх установників, він запускає повідомлення для своїх слухачів / спостерігачів (як я вже сказав, я передав би цю роль контролеру). Я віддаю перевагу SwingPropertyChangeSupport для досягнення цього, оскільки він вже оптимізований для цієї мети.
    • b. функціональність взаємодії з базою даних.
  3. Дуже розумний контролер. Дуже добре знає вигляд та модель. Контролер має дві функції:
    • a. Він визначає дію, яку виконуватиме подання, коли користувач взаємодіє з ним.
    • b. Він слухає модель. Як і те, що я вже казав, коли буде викликаний сеттер моделі, модель запускатиме повідомлення на контролер. Робота контролера - інтерпретувати це сповіщення. Можливо, знадобиться відобразити зміну у поданні.

Зразок коду

Вид:

Як я вже говорив, створення подання вже багатослівне, тому просто створіть власну реалізацію :)

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

Ідеально поєднати ці три для перевірки. Я надав лише свою реалізацію Model and Controller.

Модель:

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
Цікаво, але це менш ефективно, коли одна суттєва модель відображається в декількох поданнях ... Тоді ваша конструкція може призвести до того, що "великий контролер" обробляє одну модель, але керує всіма пов'язаними представленнями. І це стає ще складнішим, якщо ви намагаєтеся повторно використати набір "малої моделі", завдяки об'єднанню у "велику модель", оскільки подання відображає інформацію, що надсилається у декількох "маленьких моделях".
Yves Martin

1
@onepotato Я щойно спробував ваші коди. Коли я натискаю кнопку, я можу отримати коди в setUpViewEvents () для запуску. Однак, коли я роблю model.setSomething (123), коди у propertyChange не запускаються. Я навіть розміщу println безпосередньо під Object newVal = evt.getNewValue (); і він не друкується.
AmuletxHeart

10
Це НЕ архітектурний шаблон MVC , а тісно пов'язаний шаблон MVP (Model-View-Presenter). У типовому MVC саме робота моделі - повідомляти вид, коли він змінився , саме те, що вам "не подобається". Подивіться на цю діаграму, щоб побачити, як працюють взаємодії в типовому MVC.
MaxAxeHax

25

Шаблон MVC - це модель того, як може бути структурований користувальницький інтерфейс. Тому він визначає 3 елементи Model, View, Controller:

  • Модель Модель - це абстракція чогось, що представляється користувачеві. В swing у вас є диференціація графічних моделей та моделей даних. Моделі графічного інтерфейсу абстрагують стан компонента інтерфейсу, такого як ButtonModel . Моделі даних абстрагують структуровані дані, які користувацький інтерфейс представляє користувачеві, як TableModel .
  • Перегляд Представлення - це компонент інтерфейсу користувача, який відповідає за представлення даних користувачеві. Таким чином, він відповідає за всі питання, що залежать від інтерфейсу користувача, такі як макет, креслення тощо. Наприклад, JTable .
  • Контролер Контролер інкапсулює код програми, який виконується для взаємодії користувача (рух миші, клацання миші, натискання клавіші тощо). Контролерам може знадобитися введення для їх виконання, і вони видають вихідні дані. Вони зчитують свої дані з моделей та оновлюють моделі в результаті виконання. Вони також можуть перебудувати інтерфейс користувача (наприклад, замінити компоненти інтерфейсу користувача або показати повністю новий вигляд). Однак вони не повинні знати про компоненти інтерфейсу користувача, оскільки ви можете інкапсулювати реструктуризацію в окремий інтерфейс, який контролер лише викликає. В swing контролер зазвичай реалізується ActionListener або Action .

Приклад

  • Червоний = модель
  • Зелений = вигляд
  • Синій = контролер

введіть тут опис зображення

Коли Buttonнатискається клавіша, вона викликає файл ActionListener. ActionListenerЗалежить тільки від інших моделей. Він використовує деякі моделі як вхідні дані, а інші як результат чи результат. Це як аргументи методу та повернені значення. Моделі повідомляють інтерфейс користувача, коли вони оновлюються. Отже, немає необхідності в логіці контролера знати компонент ui. Об’єкти моделі не знають інтерфейсу користувача. Повідомлення здійснюється за зразком спостерігача. Таким чином, об'єкти моделі знають лише, що є хтось, хто хоче отримати повідомлення, якщо модель зміниться.

У Java swing є деякі компоненти, які також реалізують модель і контролер. Наприклад, javax.swing.Action . Він реалізує модель інтерфейсу користувача (властивості: включення, маленький значок, ім’я тощо) і є контролером, оскільки розширює ActionListener .

Докладне пояснення, приклад програми і вихідний код : https://www.link-intersystems.com/blog/2013/07/20/the-mvc-pattern-implemented-with-java-swing/ .

Основи MVC менш ніж за 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;
    }
}

Основи екранізації MVC


4
Мені подобається ця відповідь +1, якщо згадати, Actionадже Controllerнасправді я думаю, що всі EventListenerвони є контролерами ..
nachokk

@nachokk Так, справді. Як я вже сказав A controller encapsulates the application code that is executed in order to an user interaction. Переміщення миші, клацання на компоненті, натискання клавіші тощо - це все взаємодія користувача. Щоб було зрозуміліше, я оновив свою відповідь.
René Link

2

Ви можете створити модель в окремому простому класі Java, а контролер в іншому.

Тоді ви можете мати компоненти Swing. JTableбуде одним із видів (і модель таблиці фактично буде частиною подання - вона лише переводиться із "спільної моделі" на JTable).

Щоразу, коли таблиця редагується, її модель таблиці повідомляє "головному контролеру" щось оновити. Однак контролер не повинен нічого знати про таблицю. Тож дзвінок повинен виглядати більше як:, updateCustomer(customer, newValue)ні updateCustomer(row, column, newValue).

Додайте інтерфейс слухача (спостерігача) для спільної моделі. Деякі компоненти (наприклад, ваша таблиця) можуть реалізувати це безпосередньо. Іншим спостерігачем може бути контролер, який координує наявність кнопок тощо.


Це один із способів це зробити, але, звичайно, ви можете спростити або розширити його, якщо це надмірно для вашого випадку використання.

Ви можете об’єднати контролер із моделлю та мати однакові оновлення класу та підтримувати доступність компонентів. Ви навіть можете зробити "спільну модель" a TableModel(хоча, якщо вона використовується не тільки таблицею, я б рекомендував принаймні надати більш дружній API, який не витікає абстракцій таблиць)

З іншого боку, ви можете мати складні інтерфейси для оновлень ( CustomerUpdateListener, OrderItemListener,OrderCancellationListener ) і спеціалізований контролер (або посередник) тільки координації різних думок для.

Це залежить від того, наскільки складною є ваша проблема.


Близько 90% усіх подань складається з таблиці, де користувач може вибрати елемент для редагування. Дотепер я робив те, що у мене є модель даних, через яку проходять усі операції CRUD. Я використовую TableModel для адаптації моделі даних до JTable. Отже, для оновлення елемента я б викликав table.getModel (). GetModel (). Update (Елемент e). Іншими словами, на даний момент JTable є контролером. Всі дії кнопок розміщені в окремих класах (я використовую їх повторно в різних контекстах) і виконують свою роботу за допомогою методів базової моделі. Це життєздатний дизайн?
sbrattla

1

Для належного розділення, як правило, у вас буде клас контролера, якому клас Frame буде делегувати. Існують різні способи встановлення взаємозв’язків між класами - ви можете реалізувати контролер і розширити його за допомогою основного класу подання, або використовувати автономний клас контролера, який Frame викликає, коли відбуваються події. Представлення зазвичай отримує події від контролера шляхом реалізації інтерфейсу слухача.

Іноді одна або кілька частин шаблону MVC є тривіальними або настільки `` тонкими '', що додає зайвої складності для їх відокремлення. Якщо ваш контролер заповнений однолінійними викликами, наявність його в окремому класі може в кінцевому підсумку затуманити основну поведінку. Наприклад, якщо всі події, які ви обробляєте, пов’язані з TableModel і є простими операціями додавання та видалення, ви можете вибрати реалізацію всіх функцій маніпулювання таблицею в цій моделі (а також зворотних викликів, необхідних для відображення цього в JTable). Це не справжній MVC, але він уникає додавання складності там, де це не потрібно.

Однак, коли ви його впроваджуєте, пам’ятайте JavaDoc про свої класи, методи та пакети, щоб компоненти та їх взаємозв’язки були належним чином описані!


@AndyT, хоча більшість ваших пояснень хороші, у мене проблема з вашими порадами щодо поєднання моделі з контролером. що якщо я хочу раптово змінити контролер? тепер я виявляю, що ви поєднали модель із контролером і вам також потрібно змінити модель. ваш код більше не розширюється. як би короткий не був ваш контролер, я б не поєднував його з моделлю. або вид.
Дхрюв Гайрола

Я б не погодився - це багато в чому залежить від вашої заявки. Якщо ваша модель не є більш досконалою, ніж об'єкт List, а ваш контролер робить трохи більше, ніж додає та видаляє елементи, створення трьох окремих класів (модель списку, контролер та адаптер для вашої моделі для роботи з JTable) є надмірним. Це простіше переформатувати в тому малоймовірному випадку, коли вам потрібен інший контролер, ніж вибивати класи прокладок задля якоїсь невідомої потреби в майбутньому.
AndyT 07.03.11

@AndyT погодився, можливо, якщо ваша заявка невелика, це може бути найшвидшим способом. але для розширюваності (подумайте, якщо додавання зроблено не тим самим програмістом), це може спрацювати як недолік.
Друв Гайрола, 07

2
@AndyT: Я не знаю, як довго ви розробляєте програмне забезпечення, але ваша публікація демонструє, що ви прийняли принцип KISS. Занадто багато розумних, але недосвідчених розробників Java сприймають шаблони дизайну, як вони є Біблією (шаблони дизайну - це трохи більше, ніж програмування вирізання та вставки на високому рівні). У більшості випадків пуристичний підхід шляхом створення окремих контролерів та класів перегляду служить лише для того, щоб зробити обслуговування будь-кого, крім оригінального розробника, кошмаром для програм, що перевищують пару сотень рядків коду. Якщо ви сумніваєтесь, будьте простими, дурними!
bit-twiddler

1
@AndyT: Шлях до просвітлення пронизаний отворами для горщиків, продавцями зміїної олії та самоправедниками. Однак немає нічого подібного на те, щоб довго валятися у власній дефекації, щоб навчити людей бути простими. У шаблонах дизайну немає нічого поганого. Однак знання шаблонів проектування - це не те саме, що знання дизайну програмного забезпечення. Жоден проривний програмний продукт ніколи не був побудований з використанням підходу кулінарної книги. Розробка високопродуктивного програмного забезпечення, яке відповідає вимогам і просте в обслуговуванні, як і раніше є видом мистецтва, який вимагає років для засвоєння.
bit-twiddler


0

Якщо ви розробляєте програму з графічним інтерфейсом , шаблон mvc майже є, але розмитий.

Розрізання моделі, подання та коду контролера складно, і зазвичай це не лише завдання рефактора.

Ви знаєте, що він є у вас, коли ваш код можна використовувати багаторазово. Якщо ви правильно впровадили MVC, має бути легко впровадити TUI або CLI, або RWD, або мобільний перший дизайн з тією ж функціональністю. Легко побачити, як це зроблено, ніж це зробити насправді, до того ж на існуючому коді.

Насправді взаємодія між моделлю, видом та контролером відбувається за допомогою інших шаблонів ізоляції (як спостерігач чи слухач)

Думаю, у цій публікації це детально пояснено, починаючи від прямого шаблону, що не відповідає MVC (як це робитиметься щодо запитань та розробок ) і закінчуючи реалізацією багаторазового використання:

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.