Паттерн MVC и Swing

80

Один из шаблонов проектирования, который мне труднее всего понять в «реальной жизни Swing», - это шаблон MVC. Я прочитал довольно много сообщений на этом сайте, в которых обсуждается шаблон, но я все еще не чувствую, что имею четкое представление о том, как использовать этот шаблон в моем приложении Java Swing.

Допустим, у меня есть JFrame, содержащий таблицу, пару текстовых полей и несколько кнопок. Я бы, вероятно, использовал TableModel для «соединения» JTable с базовой моделью данных. Однако все функции, отвечающие за очистку полей, проверку полей, блокировку полей вместе с действиями кнопок, обычно выполняются непосредственно в JFrame. Однако разве это не смешивает контроллер и представление шаблона?

Насколько я могу судить, мне удается «правильно» реализовать шаблон MVC, глядя на JTable (и модель), но все становится запутанным, когда я смотрю на весь JFrame в целом.

Я действительно хотел бы услышать, как другие относятся к этому. Что делать, когда вам нужно отобразить таблицу, пару полей и несколько кнопок для пользователя, использующего шаблон MVC?

sbrattla
источник
2
Вот связанный с этим пример .
trashgod 07
Для всех, кто приходит на эту вечеринку - Swing НЕ является чистым MVC - он многое заимствует из концепции, но «сворачивает» «представление и контроллер» вместе
MadProgrammer

Ответы:

106

Книгу, которую я настоятельно рекомендую вам для MVC в разгаре, будет "Head First Design Patterns" Фримена и Фримена. У них есть очень исчерпывающее объяснение MVC.

Краткое содержание

  1. Вы пользователь - вы взаимодействуете с представлением. Вид - это ваше окно в модель. Когда вы что-то делаете с представлением (например, нажимаете кнопку Play), представление сообщает контроллеру, что вы сделали. С этим должен справиться контроллер.

  2. Контроллер просит модель изменить свое состояние. Контроллер принимает ваши действия и интерпретирует их. Если вы нажмете кнопку, задача контроллера - выяснить, что это означает и как следует управлять моделью на основе этого действия.

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

  4. Модель уведомляет представление об изменении его состояния. Когда что-то изменяется в модели на основе выполненного вами действия (например, нажатия кнопки) или какого-либо другого внутреннего изменения (например, запуск следующей песни в списке воспроизведения), модель уведомляет представление о том, что ее состояние изменилось.

  5. Представление запрашивает у модели состояние. Вид получает состояние, отображаемое непосредственно из модели. Например, когда модель уведомляет представление о начале воспроизведения новой песни, представление запрашивает название песни у модели и отображает его. Представление также может запрашивать у модели состояние в результате того, что контроллер запрашивает некоторые изменения в представлении.

введите описание изображения здесь Источник (если вам интересно, что такое «кремообразный контроллер», подумайте о печенье Oreo, где контроллер - это кремовый центр, вид - это верхнее печенье, а модель - нижнее печенье.)

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

Одна из проблем, с которой вы можете столкнуться при программировании Swing, связана с объединением потока SwingWorker и EventDispatch с шаблоном MVC. В зависимости от вашей программы вашему представлению или контроллеру может потребоваться расширить SwingWorker и переопределить doInBackground()метод, в котором размещается ресурсоемкая логика. Его можно легко объединить с типичным шаблоном MVC, что типично для приложений Swing.

РЕДАКТИРОВАТЬ №1 :

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

РЕДАКТИРОВАТЬ № 2 :

Отдельно хочу ответить на ваш вопрос. Вы должны отображать кнопки таблицы и т. Д. В представлении, что, очевидно, будет реализовывать ActionListener. В своем actionPerformed()методе вы обнаруживаете событие и отправляете его соответствующему методу в контроллере (помните - представление содержит ссылку на контроллер). Поэтому, когда кнопка нажата, событие обнаруживается представлением, отправляется методу контроллера, контроллер может напрямую попросить представление отключить кнопку или что-то в этом роде. Затем контроллер будет взаимодействовать с моделью и изменять ее (которая в основном будет иметь методы получения и установки, а также некоторые другие для регистрации и уведомления наблюдателей и т. Д.). Как только модель будет изменена, она вызовет обновление для зарегистрированных наблюдателей (в вашем случае это будет представление). Следовательно, представление теперь обновляется.

Дхрув Гайрола
источник
Я действительно читал книгу, но мне было трудно применить шаблон к SWING. Я также прочитал пару мест, где читал, что JFrame также можно рассматривать как представляющий представление и контроллер.
sbrattla 07
... JFrame - это компонент, а не лист. обычно обновления, сделанные контроллером, отправляются в JFrame, который позаботится обо всем остальном, следовательно, это может создать иллюзию того, что он является контроллером, но на самом деле это не так, потому что он не изменил модель, только вид. если ваш JFrame каким-то образом напрямую изменил модель - вы делаете это неправильно.
Дхрув Гайрола,
... опять же, ключевое слово здесь - "напрямую". в вашем случае вы можете прослушивать щелчки мыши по таблице и отправлять логику методам контроллера, которые изменяют модель таблицы.
Дхрув Гайрола,
2
@DhruvGairola. Описание второй точки предназначено для третьей точки, третья и для точек имеют одинаковые повторяющиеся описания. Вы можете исправить их, пожалуйста.
Naruto Biju Mode
Это классическая песня! =D
aaiezza
36

Мне не нравится идея о представлении, которое уведомляется моделью при изменении данных. Я бы делегировал эту функцию контроллеру. В этом случае, если вы измените логику приложения, вам не нужно вмешиваться в код представления. Задача представления только для компонентов приложения + верстка ни больше ни меньше. Верстка в разгаре - это уже многословная задача, почему она может мешать логике приложения?

Моя идея MVC (с которой я сейчас работаю, пока неплохо):

  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
    }
}

Main, где настраивается 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);
        ...
    }
}
Bnrdo
источник
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 элемента Модель, Представление, Контроллер:

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

пример

  • Красный = модель
  • Зеленый = вид
  • Синий = контроллер

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

Когда Buttonщелкают, он вызывает ActionListener. ActionListenerЗависит только от других моделей. Он использует одни модели как входные, а другие как результат или выход. Это как аргументы метода и возвращаемые значения. Модели уведомляют пользовательский интерфейс о своем обновлении. Таким образом, логике контроллера не нужно знать компонент пользовательского интерфейса. Объекты модели не знают пользовательского интерфейса. Уведомление выполняется шаблоном наблюдателя. Таким образом, объекты модели знают только то, что есть кто-то, кто хочет получить уведомление, если модель изменится.

В 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. Перемещение мыши, щелчок по компоненту, нажатие клавиши и т. Д. - все это действия пользователя. Чтобы было понятнее, я обновил свой ответ.
Рене Линк,
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 (Element e). Другими словами, JTable прямо сейчас является Контроллером. Все действия кнопок помещены в отдельные классы (я повторно использую их в разных контекстах) и выполняют свою работу с помощью методов базовой модели. Это жизнеспособный дизайн?
sbrattla 07
1

Для правильного разделения у вас обычно есть класс контроллера, которому класс Frame делегирует. Существуют различные способы настройки отношений между классами - вы можете реализовать контроллер и расширить его с помощью основного класса представления или использовать автономный класс контроллера, который Frame вызывает при возникновении событий. Представление обычно получает события от контроллера, реализуя интерфейс слушателя.

Иногда одна или несколько частей шаблона MVC являются тривиальными или настолько «тонкими», что добавляют ненужную сложность для их разделения. Если ваш контроллер заполнен вызовами одной строки, размещение его в отдельном классе может в конечном итоге запутать базовое поведение. Например, если все обрабатываемые вами события связаны с TableModel и представляют собой простые операции добавления и удаления, вы можете реализовать все функции управления таблицами в этой модели (а также обратные вызовы, необходимые для отображения ее в JTable). Это не настоящий MVC, но он позволяет избежать добавления сложности там, где это не нужно.

Как бы вы это ни реализовали, помните в JavaDoc свои классы, методы и пакеты, чтобы компоненты и их отношения были правильно описаны!

AndyT
источник
@AndyT, хотя большая часть вашего объяснения хороша, у меня проблема с вашим советом по объединению модели с контроллером. что если я захочу внезапно сменить контроллер? теперь я обнаружил, что вы соединили модель с контроллером и вам также нужно изменить модель. ваш код больше не является расширяемым. каким бы коротким ни был ваш контроллер, я бы не стал совмещать его с моделью. или вид.
Дхрув Гайрола,
Я бы не стал возражать - это во многом зависит от вашего приложения. Если ваша модель не более сложна, чем объект List, и ваш контроллер делает немного больше, чем добавляет и удаляет элементы, создание трех отдельных классов (модель списка, контроллер и адаптер для вашей модели для работы с JTable) является излишним. Проще реорганизовать его в том маловероятном случае, когда вам понадобится другой контроллер, чем выпускать классы прокладок ради каких-то неизвестных будущих потребностей.
AndyT 07
@AndyT согласился, возможно, если ваше приложение маленькое, это может быть самый быстрый способ. но ради расширяемости (учтите, если добавление сделано не одним и тем же программистом), это может работать как недостаток.
Дхрув Гайрола,
2
@AndyT: Я не знаю, как долго вы разрабатываете программное обеспечение, но ваше сообщение демонстрирует, что вы приняли принцип KISS. Слишком много умных, но неопытных Java-разработчиков воспринимают шаблоны проектирования, как библию (шаблоны проектирования - это не что иное, как высокоуровневое программирование методом вырезания и вставки). В большинстве случаев использование пуристического подхода путем создания отдельных классов контроллера и представления служит только для того, чтобы сделать обслуживание программы кем-либо, кроме исходного разработчика, кошмаром для программ размером более пары сотен строк кода. Если сомневаетесь, делайте это просто, глупо!
bit-twiddler
1
@AndyT: Путь к просветлению пронизан ямами, продавцами змеиного масла и самодовольными. Тем не менее, ничто не может сравниться с необходимостью погрязать в собственной дефекации в течение длительного периода времени, чтобы научить человека сохранять простоту. В шаблонах проектирования нет ничего плохого. Однако знание шаблонов проектирования - это не то же самое, что знание дизайна программного обеспечения. Ни один прорывной программный продукт никогда не создавался с использованием подхода «поваренной книги». Разработка высокопроизводительного программного обеспечения, отвечающего требованиям и простого в обслуживании, по-прежнему остается искусством, на освоение которого требуются годы.
bit-twiddler
0

Если вы разрабатываете программу с графическим интерфейсом , шаблон mvc почти присутствует, но размыт.

Разделить код модели, представления и контроллера сложно, и обычно это не только задача рефакторинга.

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

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

Я предполагаю, что этот пост объясняет это подробно, от прямого шаблона не MVC (как вы сделаете в Q&D ) до окончательной реализации многократного использования:

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

Альфан
источник