El patrón MVC y Swing

80

Uno de los patrones de diseño que encuentro más difícil de entender en la "vida real de Swing" es el patrón MVC. He revisado bastantes publicaciones en este sitio que discuten el patrón, pero todavía no siento que tenga una comprensión clara de cómo aprovechar el patrón en mi aplicación Java Swing.

Digamos que tengo un JFrame que contiene una tabla, un par de campos de texto y algunos botones. Probablemente usaría un TableModel para "puentear" el JTable con un modelo de datos subyacente. Sin embargo, todas las funciones responsables de borrar campos, validar campos, bloquear campos junto con las acciones de los botones normalmente irían directamente al JFrame. Sin embargo, ¿eso no mezcla el controlador y la vista del patrón?

Por lo que puedo ver, me las arreglo para conseguir que el patrón MVC se implemente "correctamente" al mirar el JTable (y el modelo), pero las cosas se complican cuando miro el JFrame completo como un todo.

Realmente me gustaría saber cómo actúan los demás con respecto a esto. ¿Cómo se hace cuando se necesita mostrar una tabla, un par de campos y algunos botones a un usuario que usa el patrón MVC?

sbrattla
fuente
2
Aquí tienes un ejemplo relacionado .
trashgod
Para cualquier otra persona que venga a esta fiesta - Swing NO es un MVC puro - toma prestado mucho del concepto, pero "colapsa" la "vista y el controlador" juntos
MadProgrammer

Respuestas:

106

Un libro que le recomiendo encarecidamente para MVC en swing sería "Head First Design Patterns" de Freeman y Freeman. Tienen una explicación muy completa de MVC.

Breve resumen

  1. Eres el usuario, interactúas con la vista. La vista es su ventana al modelo. Cuando haces algo en la vista (como hacer clic en el botón Reproducir), la vista le dice al controlador lo que hiciste. Es el trabajo del controlador manejar eso.

  2. El controlador le pide al modelo que cambie su estado.El controlador toma sus acciones y las interpreta. Si hace clic en un botón, el trabajo del controlador es averiguar qué significa eso y cómo se debe manipular el modelo en función de esa acción.

  3. El controlador también puede solicitar que cambie la vista.Cuando el controlador recibe una acción de la vista, es posible que deba indicarle a la vista que cambie como resultado. Por ejemplo, el controlador podría habilitar o deshabilitar ciertos botones o elementos de menú en la interfaz.

  4. El modelo notifica a la vista cuando su estado ha cambiado. Cuando algo cambia en el modelo, ya sea en base a alguna acción que realizó (como hacer clic en un botón) o algún otro cambio interno (como la siguiente canción de la lista de reproducción ha comenzado), el modelo notifica a la vista que su estado ha cambiado.

  5. La vista pregunta al modelo por el estado. La vista obtiene el estado que muestra directamente del modelo. Por ejemplo, cuando el modelo notifica a la vista que ha comenzado a reproducirse una nueva canción, la vista solicita el nombre de la canción del modelo y lo muestra. La vista también puede solicitar el estado del modelo como resultado de que el controlador solicita algún cambio en la vista.

ingrese la descripción de la imagen aquí Fuente (en caso de que se esté preguntando qué es un "controlador cremoso", piense en una galleta Oreo, con el controlador como el centro cremoso, la vista es la galleta superior y el modelo la galleta inferior).

Um, en caso de que esté interesado, puede descargar una canción bastante entretenida sobre el patrón MVC de aquí. !

Un problema que puede enfrentar con la programación de Swing implica fusionar el hilo SwingWorker y EventDispatch con el patrón MVC. Dependiendo de su programa, es posible que su vista o controlador tenga que extender SwingWorker y anular eldoInBackground() método donde se coloca la lógica de uso intensivo de recursos. Esto se puede fusionar fácilmente con el patrón MVC típico y es típico de las aplicaciones Swing.

EDITAR # 1 :

Además, es importante considerar MVC como una especie de combinación de varios patrones. Por ejemplo, su modelo podría implementarse usando el patrón Observer (requiriendo que la Vista esté registrada como un observador en el modelo) mientras que su controlador podría usar el patrón Strategy.

EDITAR # 2 :

Además, me gustaría responder específicamente a su pregunta. Debería mostrar los botones de su tabla, etc. en la Vista, lo que obviamente implementaría un ActionListener. En su actionPerformed()método, detecta el evento y lo envía a un método relacionado en el controlador (recuerde, la vista contiene una referencia al controlador). Entonces, cuando se hace clic en un botón, la vista detecta el evento, se envía al método del controlador, el controlador puede pedir directamente a la vista que desactive el botón o algo así. A continuación, el controlador interactuará y modificará el modelo (que en su mayoría tendrá métodos getter y setter, y algunos otros para registrar y notificar a los observadores, etc.). Tan pronto como se modifique el modelo, llamará a una actualización de los observadores registrados (esta será la vista en su caso). Por lo tanto, la vista ahora se actualizará.

Dhruv Gairola
fuente
Realmente he leído el libro, pero me ha resultado difícil aplicar el patrón a SWING. También he leído un par de lugares en los que he leído que un JFrame también puede verse como una representación de una vista y un controlador.
sbrattla
... el JFrame es un componente, no una hoja. Normalmente, las actualizaciones realizadas por el controlador se envían al JFrame, que se encarga del resto, por lo tanto, esto puede dar la ilusión de que es un controlador, pero en realidad, este no es el caso porque no ha cambiado el modelo. solo la vista. si su JFrame ha cambiado de alguna manera el modelo directamente, lo está haciendo mal.
Dhruv Gairola
... de nuevo, la palabra clave aquí es "directamente". en su caso, puede escuchar los clics del mouse en la tabla y enviar la lógica a los métodos en el controlador que modifican el modelo de la tabla.
Dhruv Gairola
2
@DhruvGairola La descripción del segundo punto es para el tercer punto, el tercero y los puntos tienen las mismas descripciones duplicadas. ¿Puede corregirlos por favor?
Modo Naruto Biju
¡Esa canción es un clásico! =D
aaiezza
36

No me gusta la idea de que la vista sea la que notifica el modelo cuando cambian sus datos. Delegaría esa funcionalidad al controlador. En ese caso, si cambia la lógica de la aplicación, no es necesario que interfiera con el código de la vista. La tarea de la vista es solo para los componentes de las aplicaciones + diseño ni más ni menos. El diseño en swing ya es una tarea detallada, ¿por qué dejar que interfiera con la lógica de las aplicaciones?

Mi idea de MVC (con la que estoy trabajando actualmente, hasta ahora todo bien) es:

  1. La vista es la más tonta de las tres. No sabe nada sobre el controlador y el modelo. Su preocupación es solo la prótesis y el diseño de los componentes del columpio.
  2. El modelo también es tonto, pero no tan tonto como la vista. Realiza las siguientes funcionalidades.
    • a. cuando uno de sus establecedores es llamado por el controlador, disparará una notificación a sus oyentes / observadores (como dije, delegaría este rol al controlador). Prefiero SwingPropertyChangeSupport para lograr esto, ya que ya está optimizado para este propósito.
    • segundo. funcionalidad de interacción con la base de datos.
  3. Un controlador muy inteligente. Conoce muy bien la vista y el modelo. El controlador tiene dos funcionalidades:
    • a. Define la acción que ejecutará la vista cuando el usuario interactúe con ella.
    • segundo. Escucha al modelo. Como he dicho, cuando se llama al configurador del modelo, el modelo disparará una notificación al controlador. Es el trabajo del controlador interpretar esta notificación. Es posible que deba reflejar el cambio en la vista.

Muestra de código

La vista :

Como dije, la creación de la vista ya es detallada, así que solo cree su propia implementación :)

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

Es ideal para interconectar los tres con fines de prueba. Solo proporcioné mi implementación de Model and Controller.

El modelo :

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

El controlador :

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

El principal, donde está configurado el 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
fuente
4
Interesante, pero es menos eficiente cuando se muestra un modelo de entidad única en varias vistas ... Entonces su diseño puede llevar a un "gran controlador" que maneja un solo modelo pero que administra todas las vistas relacionadas. Y se vuelve aún más complicado si intenta reutilizar un conjunto de "modelo pequeño", gracias a una agregación en un "modelo grande" porque una vista muestra información distribuida en múltiples entidades de "modelo pequeño".
Yves Martin
1
@onepotato Acabo de probar tus códigos. Cuando presiono un botón, puedo hacer que los códigos en setUpViewEvents () se activen. Sin embargo, cuando hago un model.setSomething (123), los códigos en propertyChange no se activan. Incluso puse un println directamente debajo de Object newVal = evt.getNewValue (); y no se imprime.
AmuletxHeart
10
Este NO es el patrón arquitectónico MVC , sino el patrón MVP (Model-View-Presenter) estrechamente relacionado . En un MVC típico, es precisamente el trabajo del modelo notificar a la vista cuando ha cambiado , exactamente lo que "no le gusta". Mire este diagrama para ver cómo funcionan las interacciones en un MVC típico.
MaxAxeHax
25

El patrón MVC es un modelo de cómo se puede estructurar una interfaz de usuario. Por tanto define los 3 elementos Modelo, Vista, Controlador:

  • Modelo Un modelo es una abstracción de algo que se le presenta al usuario. En swing tienes una diferenciación de modelos de interfaz gráfica de usuario y modelos de datos. Los modelos de GUI abstraen el estado de un componente de interfaz de usuario como ButtonModel . Los modelos de datos abstraen datos estructurados que la interfaz de usuario presenta al usuario como TableModel .
  • Vista La vista es un componente de la interfaz de usuario que se encarga de presentar datos al usuario. Por lo tanto, es responsable de todas las cuestiones que dependen de la interfaz de usuario, como el diseño, dibujo, etc. Por ejemplo JTable .
  • Controlador Un controlador encapsula el código de la aplicación que se ejecuta para la interacción del usuario (movimiento del mouse, clic del mouse, pulsación de teclas, etc.). Los controladores pueden necesitar entrada para su ejecución y producen salida. Leen su entrada de modelos y actualizan modelos como resultado de la ejecución. También pueden reestructurar la interfaz de usuario (por ejemplo, reemplazar componentes de la interfaz de usuario o mostrar una vista completamente nueva). Sin embargo, no deben conocer los componentes de la interfaz de usuario, porque puede encapsular la reestructuración en una interfaz separada que el controlador solo invoca. En swing, un controlador normalmente lo implementa un ActionListener o Action .

Ejemplo

  • Rojo = modelo
  • Verde = ver
  • Azul = controlador

ingrese la descripción de la imagen aquí

Cuando Buttonse hace clic en, se invoca a ActionListener. losActionListener único depende de otros modelos. Utiliza algunos modelos como entrada y otros como resultado o salida. Es como argumentos de método y valores de retorno. Los modelos notifican a la interfaz de usuario cuando se actualizan. Por lo tanto, no es necesario que la lógica del controlador conozca el componente de la interfaz de usuario. Los objetos del modelo no conocen la interfaz de usuario. La notificación se realiza mediante un patrón de observador. Por lo tanto, los objetos del modelo solo saben que hay alguien que quiere recibir una notificación si el modelo cambia.

En java swing hay algunos componentes que implementan un modelo y un controlador también. Por ejemplo, javax.swing.Action . Implementa un modelo de interfaz de usuario (propiedades: habilitación, icono pequeño, nombre, etc.) y es un controlador porque extiende ActionListener .

Una explicación detallada, aplicación de ejemplo y código fuente : https://www.link-intersystems.com/blog/2013/07/20/the-mvc-pattern-implemented-with-java-swing/ .

Conceptos básicos de MVC en menos de 260 líneas:

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

Screencast de conceptos básicos de MVC

René Link
fuente
4
Me gusta esta respuesta +1, por mencionar Actionque, Controllerde hecho, supongo que todos EventListenerson controladores ..
nachokk
@nachokk Sí, de hecho. Como dije A controller encapsulates the application code that is executed in order to an user interaction. Mover el mouse, hacer clic en un componente, presionar una tecla, etc. son interacciones del usuario. Para que quede más claro, actualicé mi respuesta.
René Link
2

Puede crear un modelo en una clase Java simple separada y un controlador en otra.

Entonces puedes tener componentes Swing además de eso. JTablesería una de las vistas (y el modelo de tabla sería de facto parte de la vista; solo se traduciría del "modelo compartido" a JTable).

Siempre que se edita la tabla, su modelo de tabla le dice al "controlador principal" que actualice algo. Sin embargo, el controlador no debería saber nada sobre la mesa. Por tanto, la llamada debería parecerse más a:, updateCustomer(customer, newValue)not updateCustomer(row, column, newValue).

Agregue una interfaz de escucha (observador) para el modelo compartido. Algunos componentes (por ejemplo, su tabla) podrían implementarlo directamente. Otro observador podría ser el controlador que coordina la disponibilidad de los botones, etc.


Esa es una forma de hacerlo, pero, por supuesto, puede simplificarlo o ampliarlo si es excesivo para su caso de uso.

Puede fusionar el controlador con el modelo y tener las mismas actualizaciones de proceso de clase y mantener la disponibilidad de los componentes. Incluso puede hacer que el "modelo compartido" sea un TableModel(aunque si no solo lo usa la tabla, recomendaría al menos proporcionar una API más amigable que no pierda abstracciones de la tabla)

Por otro lado, puede tener interfaces complejas para las actualizaciones ( CustomerUpdateListener, OrderItemListener,OrderCancellationListener ) y el controlador dedicado (o mediador) sólo para la coordinación de los diferentes puntos de vista.

Depende de lo complicado que sea su problema.

Konrad Garus
fuente
Aproximadamente el 90% de todas las vistas consta de una tabla donde el usuario puede seleccionar un elemento para editar. Lo que he hecho hasta ahora es que tengo un modelo de datos por el que pasan todas las operaciones CRUD. Utilizo un TableModel para adaptar el modelo de datos a JTable. Entonces, para actualizar un elemento, invocaría table.getModel (). GetModel (). Update (Element e). En otras palabras, JTable es el controlador en este momento. Todas las acciones de los botones se colocan en clases separadas (las reutilizo en diferentes contextos) y hacen su trabajo a través de los métodos del modelo subyacente. ¿Es ese un diseño viable?
sbrattla
1

Para una separación adecuada, normalmente tendría una clase de controlador a la que delegaría la clase Frame. Hay varias formas de configurar las relaciones entre las clases: puede implementar un controlador y extenderlo con su clase de vista principal, o usar una clase de controlador independiente que el marco llama cuando ocurren eventos. La vista normalmente recibiría eventos del controlador implementando una interfaz de escucha.

A veces, una o más partes del patrón MVC son triviales, o tan 'delgadas' que agregan una complejidad innecesaria para separarlas. Si su controlador está lleno de llamadas de una línea, tenerlo en una clase separada puede terminar ofuscando el comportamiento subyacente. Por ejemplo, si todos los eventos que está manejando están relacionados con un TableModel y son operaciones simples de agregar y eliminar, puede optar por implementar todas las funciones de manipulación de la tabla dentro de ese modelo (así como las devoluciones de llamada necesarias para mostrarlo en el JTable). No es un verdadero MVC, pero evita agregar complejidad donde no es necesario.

Independientemente de cómo lo implemente, recuerde JavaDoc sus clases, métodos y paquetes para que los componentes y sus relaciones se describan correctamente.

AndyT
fuente
@AndyT, aunque la mayor parte de su explicación es buena, tengo un problema con su consejo sobre cómo combinar el modelo con el controlador. ¿Qué pasa si quiero cambiar repentinamente el controlador? ahora encuentro que ha acoplado el modelo con el controlador y también necesita modificar el modelo. su código ya no es extensible. por muy corto que sea su controlador, no lo combinaría con un modelo. o una vista.
Dhruv Gairola
No estaría en desacuerdo, depende mucho de su aplicación. Si su modelo no es más sofisticado que un objeto List, y su controlador hace poco más que agregar y eliminar elementos, crear tres clases separadas (modelo List, Controller y adaptador para que su modelo funcione con JTable) es excesivo. Es más fácil refactorizarlo en el improbable caso de que necesite un controlador diferente, que producir clases de corrección en aras de alguna necesidad futura desconocida.
AndyT
@AndyT estuvo de acuerdo, tal vez si su aplicación es pequeña, esta podría ser la forma más rápida. pero en aras de la extensibilidad (considere si la adición no la realiza el mismo programador), podría funcionar como una desventaja.
Dhruv Gairola
2
@AndyT: No sé cuánto tiempo lleva desarrollando software, pero su publicación demuestra que ha adoptado el principio KISS. Demasiados desarrolladores de Java inteligentes, pero sin experiencia, adoptan patrones de diseño como si fueran la Biblia (los patrones de diseño son poco más que una programación de alto nivel de cortar y pegar). En la mayoría de los casos, adoptar un enfoque purista mediante la creación de clases de controlador y vista separadas solo sirve para que el mantenimiento por parte de cualquier persona que no sea el desarrollador original sea una pesadilla para programas de más de un par de cientos de líneas de código. En caso de duda, ¡manténgalo simple, estúpido!
bit-twiddler
1
@AndyT: El camino hacia la iluminación está plagado de baches, vendedores de aceite de serpiente y santurrones. Sin embargo, no hay nada como tener que revolcarse en la propia defecación durante un período prolongado de tiempo para enseñarle a mantener las cosas simples. No hay nada de malo en los patrones de diseño. Sin embargo, conocer los patrones de diseño no es lo mismo que conocer el diseño de software. Nunca se ha creado ningún producto de software innovador con un enfoque de libro de cocina. El diseño de software de alto rendimiento que cumpla con los requisitos y sea fácil de mantener sigue siendo una forma de arte que requiere años para dominarla.
bit-twiddler
0

Si desarrolla un programa con una GUI , el patrón mvc casi está ahí pero borroso.

Diseñar el código de modelo, vista y controlador es difícil y normalmente no es solo una tarea de refactorización.

Sabes que lo tienes cuando tu código es reutilizable. Si ha implementado correctamente MVC, debería ser fácil implementar una TUI o una CLI o un RWD o un primer diseño móvil con la misma funcionalidad. Es fácil verlo hecho que hacerlo en realidad, además en un código existente.

De hecho, las interacciones entre el modelo, la vista y el controlador ocurren usando otros patrones de aislamiento (como Observador o Escucha)

Supongo que esta publicación lo explica en detalle, desde el patrón directo no MVC (como lo hará en una sesión de preguntas y respuestas ) hasta la implementación reutilizable final:

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

albfan
fuente