Dúvida sobre o uso do Swing no padrão MVC

Pessoal, estou com uma dúvida simples mas que gostaria de um certo esclarecimento:

Tenho uma aplicação Java desenvolvida em cima do padrão de projeto MVC. Para desenvolver a interface gráfica da aplicação eu utilizo componentes Swing que, pelo fato da aplicação ser simples e pensada apenas para estudo, são componentes padrão. Isto é, componentes vindos da própria paleta do IDE e editados pelas propriedades disponíveis…

Sei que muitos programadores criam seus próprios componentes personalizados que estendem as classes dos componentes Swing, o que não é o caso atual.

Para contornar a limitação dos componentes padrão do Swing, pensei em criar uma classe à parte que sirva como portadora dos métodos dos quais sinto falta. Por exemplo, na aplicação preciso ter um método que limpe o texto de todos os JTextComponent que estiverem em cima de um JPanel, e creio eu que não exista um método padrão no JPanel Swing que realize isso. Então utilizaria esta classe à parte, chamando este método toda vez que quisesse limpar um JPanel qualquer.

Gostaria de saber se esta ideia faz sentido e se, por acaso, a classe à parte (enquadrada como uma classe de “modelo”) feriria as regras do padrão MVC.

Deixo um exemplo do método de limpeza dos JTextComponent dentro de um JPanel que comentei.

package home.room.models;

// @author Rodrigo Alberto;

import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.text.JTextComponent;

 
public class UtilidadesSwing {
    
    public void cleanFields(JPanel panel) {       
        JTextComponent textComponent;
        
        for (int i = 0; i < panel.getComponentCount(); i++) {          

            if(JTextComponent.class.isAssignableFrom(panel.getComponent(i).getClass())){System.out.println("BB"+panel.getComponent(i).getName());
                textComponent = (JTextComponent) panel.getComponent(i);
                textComponent.setText("");
            }else{
                try {
                    if(panel.getComponent(i).getClass().equals(JScrollPane.class)) {
                        JScrollPane scrollPane = (JScrollPane) panel.getComponent(i);
             
                        textComponent = (JTextComponent) scrollPane.getViewport().getComponent(0);
                        textComponent.setText("");
                    }else {
                        JComponent component = (JComponent) panel.getComponent(i);

                        for (int j = 0; j < component.getComponentCount(); j++) {
                            textComponent = (JTextComponent) component.getComponent(j);
                            textComponent.setText("");
                        }
                    }
                } catch (ClassCastException e) {
                    System.out.println("\nERRO: "+e);
                }
            }
        }
    }
}

Esse método deveria ser estático e não de instância. Não faz sentido você ter que criar um objeto dessa classe para executar tal método. Eu concentraria todos esses métodos utilitários na mesma classe também ao invés de ter um caminhão de classes cada uma com um método utilitário.

Havia pensado em torna-lo estático também. Fiquei na dúvida se o fato de haverem imports de componentes de visão numa classe de modelo feriria o MVC.

Ferra sim, pois o modelo não deveria saber da existência das classes de visão, só o controller.
O controller é quem deve notificar a camada de visão quando ela precisa limpar os campos.

1 curtida

Então, o que significa quando vc diz “saber da existência”? Seria como o que acontece com os imports?

Porque já vi pessoas dizendo que o modelo apenas não pode ter instancias da visão. Declarações e imports não são necessariamente instancias, ou são?

“Saber da existência” seria o mesmo q “depender de”, pois se está importado, provavlmente está sendo usado em alguma parte. Supondo:

view > controller > service > domain > dao

view = camada mais alta na estrutura
dao = camada mais baixa na estrutura

Uma prática que costumo seguir e evitar que camadas inferiores dependa de classes de camadas superiores (mas tb não é uma regra sagraada, eh mais um direcionamento que ajuda a evitar problemas).

Quero dizer que o modelo não pode depender da visão, ou seja, não pode usar ou importar classes da camada de visão, e na verdade nem do controller.

Se em alguma situação específica o seu modelo precisa enviar alguma notificação para a camada de visão, então você deveria ter uma estrutura de listener, onde o controller se registra como listener do modelo para ser notificado quando precisa realizar alguma ação na visão.

Acredito que seu método possa ser bastante simplificado pois além de estar usando usar reflection, você está chamando chamando o getComponent 8 vezes e não precisa disso tudo, olha só:

public static void cleanFields(Container container) {
    Component[] filhos = container.getComponents();     // pega os filhos do container
    for (Component filho : filhos) {                    // itera sobre cada filho
        if (filho instanceof TextComponent) {           // se é um TextComponent (AWT)
            System.out.println("BB" + filho.getName()); // imprime o nome
            ((TextComponent) filho).setText("");        // limpa o conteúdo
        } else if (filho instanceof JTextComponent) {   // se é um JTextComponent (Swing)
            System.out.println("BB" + filho.getName()); // imprime o nome
            ((JTextComponent) filho).setText("");       // limpa o conteúdo
        } else if (filho instanceof Container) {        // se é um Container
            cleanFields((Container) filho);             // chama o método recursivamente
        }
    }
}

Entendi sobre a parte “depender da visão”, de fato faz sentido. Onde eu poderia logicamente encaixar uma classe utilitária que realiza operações estáticas com a visão? Quer dizer, posso simplesmente refatorar minha classe genérica “UtilidadesSwing” para o pacote service ou realmente é necessário fazer um listener para a escutar dentro do modelo?

Gostei para caramba do método simplificado, vou tentar adapta-lo para remover os itens de JComboBox e JList também.

Como ela é um utilitário da camada de visão, ela deve ficar na camada de visão, provavelmente quem vai acionar ela é o seu controller, já que é responsabilidade dele de trocar mensagens entre visão e modelo.

Você só vai precisar de um listener se você tiver uma situação onde o modelo precisa notificar alguém sobre alguma coisa, mas seria uma situação bastante específica, pois na maioria das vezes é o controller quem deveria saber quando notificar as mudanças.

Vai fundo, só presta atenção pra deixar sempre a recursão do else if (filho instanceof Container) por último, pois tem muitos componentes, como o próprio JTextComponent que estendem Container e nesses casos não faz sentido a recusividade.