MVC - Como assim: um controlador separado da apresentação?

30 respostas
peerless

Boa tarde, senhores.

Estava lendo o ótimo artigo do Shoes, onde me bateu uma dúvida. Ele relata que:

Algumas vezes, entretanto, é necessário que o Controller fique isolado desta. Este é o caso, por exemplo, quando possuímos mais de uma interface, como Swing e HTML. Neste caso, pode-se utilizar uma Camada que quase sempre está implícita, a Camada de Aplicação.

Não entendi ao certo, a que nível de isolamento ele se referiu. Isolamento com uma classe privada a classe de apresentação (Como até então, eu conheço…), ou isolamento de classes mesmo?

Se for da segunda opção, como fazer isso funcionar de maneira elegante? (Sem aquele monte de set com objetos zumbis)

[]s Robson

30 Respostas

sergiotaborda

peerless:
Boa tarde, senhores.

Estava lendo o ótimo artigo do Shoes, onde me bateu uma dúvida. Ele relata que:

Algumas vezes, entretanto, é necessário que o Controller fique isolado desta. Este é o caso, por exemplo, quando possuímos mais de uma interface, como Swing e HTML. Neste caso, pode-se utilizar uma Camada que quase sempre está implícita, a Camada de Aplicação.

Não entendi ao certo, a que nível de isolamento ele se referiu. Isolamento com uma classe privada a classe de apresentação (Como até então, eu conheço…), ou isolamento de classes mesmo?

Isolamento assim: Se quiser mudar de Swing para HTML e depois para SWT e de volta para AWT o controller sempre é o mesmo. O controller é independente da tecnologia de apresentação e dependente do objetivo da aplicação ( por isso ele se chama controller : controlador. O controlo é sempre o mesmo)

pcalcado

Imagine que eu tenho uma classe ConsultaUsuarioAction. Se estou utilizando um MVC baseado em POJOs esta classe deve ser algo parecido com:

class ConsultaUsuarioAction{
 public Resposta executar(Requisicao r){
   //pega parametros da requisicao, manda para um objeto de negocios
   String login = r.getParametro("requisicao");

  //recebe a resposta do objecto de negocios e coloca na resposta
   Usuario buscado = gerenciadorUsuarios.buscarPor(login);

  Resposta r = new Resposta();
  r.put("usuario", buscado);
  return r;
 } 
}

Esse cara contêm um pouco da lógica de aplicação do sistema. Eu posso utilizar num template JSP:

Usuario: <%= request.getAttribute("resposta").get("usuario").getNome()%>

Ou poderia ser utilizado num formulário Swing qualquer:

modelDeUmComponenteSwingQualquer.addLine(resposta.get("usuario").getNome());

A mesma ação pode ser utilizada em diversos tipos de interface.

rodrigoallemand

Seguindo as explicações do Shoes, imagine que a cada solicitação de um formulário seu para o cadastro de um cliente, vc invocaria o controller CadastrarClienteController. Se vc estivesse na Web com Struts, vc teria o CadastrarClienteAction. E se vc tivesse a mesma ação em um ambiente cliente servidor com SWT, vc deveria ter o CadastrarClienteController duplicado?!? Não!!! Vc teria um CadastrarClienteController padrão, tanto para o Struts quando para o SWT. Mas na CAMADA DE APRESENTAÇÃO, vc teria uma Action que chamaria este controller e um evento/formulario/ação (ou sei lá o que) pra tratar a mesma coisa no SWT, chamando o mesmo CadastrarClienteController.
Com isso vc teria a camada de controle (*Controller) desacoplada ta tecnologia utilizada para a apresentação (*Arcio, *Component, etc)

peerless

Bom, vamos refinar minha dúvida! De exemplo, o swing.

public class MyView {

private class MyControl {

}
}

Minha dúvida é: Como a MyControl é privada à MyView, ela conhece toda e qualquer mudança da apresentação, seus componentes e seus respectivos valores.

Minha dúvida é quanto a separação, digamos, FÍSICA da classe MyControl a apresentação. Como ela iria conhecer o seu conteúdo?

Se fosse, digamos.

MyView.java e MyControl.java

Eu me confundi ao ler o trexo do artigo mesmo.

pcalcado

Por que ela é privada?

peron

Olá Senhores,

Pegando uma carona na dúvida de nosso amigo, pergunto:

  • Como então estruturar meu Controller, sabendo eu que ele deve aceitar ENTRADAS e gerar SAIDAS (Request, Response), para que ele se torne independente de visualização?
  • Como vocês fazem no item acima, algum exemplo/modelo/idéia?

Obrigado desde já,

Saudações

pcalcado

Não entendi a dúvida, Peron, pode reformular?

sergiotaborda
peerless:
Bom, vamos refinar minha dúvida! De exemplo, o swing.
public class MyView {

private class MyControl {

}
}

Minha dúvida é: Como a MyControl é privada à MyView, ela conhece toda e qualquer mudança da apresentação, seus componentes e seus respectivos valores.

Já começa mal.
1) O controlador não é uma inner classe do view porque ele precisa ser desacoplado
2) A comunicação entre o controller e o view é feita por eventos e interfaces e não por código promiscuo.

public class MySwingView {

   ViewController controller ; // injetar aqui o controlador correto

}

public class MyNotSwingDependantControl implements ViewController{

}
peerless

Para pertencer a, hmm, digamos à instancia da view…

minha dúvida foi, qndo tu citou algo referente a “separar” … ja me tapou o cérebro nesta situação… já imaginei, uma separação de arquivos (.java mesmo) onde teria uma View e uma Control publica…

Desculpe minha ignorancia, mas ainda estou engatinhando… abraços e parabéns pelos artigos, são referencia para toda nossa comunidade.

nbluis

E?
Não é exatamente isso que está se falando ??

peerless
sergiotaborda:
peerless:
Bom, vamos refinar minha dúvida! De exemplo, o swing.
public class MyView {

private class MyControl {

}
}

Minha dúvida é: Como a MyControl é privada à MyView, ela conhece toda e qualquer mudança da apresentação, seus componentes e seus respectivos valores.

Já começa mal.
1) O controlador não é uma inner classe do view porque ele precisa ser desacoplado
2) A comunicação entre o controller e o view é feita por eventos e interfaces e não por código promiscuo.

public class MySwingView {

   ViewController controller ; // injetar aqui o controlador correto

}

public class MyNotSwingDependantControl implements ViewController{

}

Beleza.. mas ai surge minha dúvida..

este controlador seria meu handler que trataria os eventos. Eu teria esta composição, ok.. digamos:

meuButton.addKeyListener(controller);

como o meu controller iria conhecer o "meuBotao" ou a "minhaTable" ou "qualquer-outro-componente-da-view"
?

Abração

nbluis

Seu botão iria fazer uma chamada ao controller… e não o contrário…

o botão faz a chamada ao controler, pega a resposta e apresenta.

Apenas isto, botão não faz calculo, não faz inserção no banco de dados, não tem conexão, não brinca faz magia negra. :lol:

sergiotaborda

peerless:
sergiotaborda:

Já começa mal.

  1. O controlador não é uma inner classe do view porque ele precisa ser desacoplado
  2. A comunicação entre o controller e o view é feita por eventos e interfaces e não por código promiscuo.

Beleza… mas ai surge minha dúvida…

este controlador seria meu handler que trataria os eventos. Eu teria esta composição, ok… digamos:

meuButton.addKeyListener(controller);

Não se vc quer manter o seu controlador independente do view ( que é o que estamos a falar neste topico)
a classe do controlador não pode imports (dependencias) de nada da view. Logo não pode depender de listeners e afins.

A sua View Swing deve traduzir esses eventos em chamadas a métodos genéricos do controlador.
Em outras palavras: A UI Swing envia eventos para a ViewSwing que intrepreta esses comandos e comunica com um controlador generico.

Simplesmente não vai. O caminho é view --> controller e não ha retorno.
Se o controller deseja mudar alguma coisa ele deve modificar o model. O model , sim, avisa a view.
A sequencia de eventos é:

cliente -interage com -> view -notifica-> controller - modifica-> model -notifica-> view -lê-> model.

peerless

Bom, vamos ver se eu entendi:

O Correto, então é que os componentes implementem os listeners dentro da view (ou em uma inner auxiliar), para então chamar o controlador, o qual teria apenas métodos de ligação com o model e de resposta a view, etc e tal?

Bom, ja vi que infelizmente, fui ensinado errado na faculdade.

Logo, teriamos, digamos, 4 camadas:

view
inner (handler listener)
controle
modelo

?

Eu normalmente associava controller a um escravo das implementacoes dos listeners, e tratamento destes eventos.

nbluis

Na verdade não…
seus listeners fazem parte da camada view…
pois estes listeners estão diretamente acoplados a sua view.
Uma view web não via ter estes listeners…

Uma coisa são classes separadas, outra são camadas distintas.

peerless

nbluis:
Na verdade não…
seus listeners fazem parte da camada view…
pois estes listeners estão diretamente acoplados a sua view.
Uma view web não via ter estes listeners…

Uma coisa são classes separadas, outra são camadas distintas.

Na verdade ‘não’ o que? =]

nbluis

Na verdade ‘não’ o que? =]
Não são 4 camadas.
3 Camadas como expliquei acima.
:smiley:

nbluis

Nunca ouviu aquele jargão. “Programação em 3 camadas”.

hiauhauiahuihuia…

peerless

Bom, o problema é que estou em fase de transição deste tipo de arquitetura. Ouvi muito assunto errado sobre isso. Afirmações falsas.

Continuando na idéia do Swing…

Eu não consigo pensar numa situação do model atualizando a view… como um negocio atualizaria um JFrame por exemplo??

Uma linha de código de exemplo, por favor. :smiley:

Abraços!!!

peerless

tfNome.setText(Cliente.getNome()) ;

isso seria um modelo atualizar a view? hehehe

nbluis

O modelo responde ao controlador e o mesmo responde a view…

No caso sua view é que vai colocar os devidos valores nos campos…

pcalcado

Não são Camadas, são simplesmente componentes do MVC. MVC não é sobre Camadas.

http://fragmental.com.br/wiki/index.php/MVC_e_Camadas

peron
pcalcado:
Não entendi a dúvida, Peron, pode reformular?

Claro!

Bom, o Controller tem que receber informações, por exemplo, para apagar uma pessoa, precisa saber qual pessoa receber. Após a remoção, precisa notificar a View de que a pessoa em questão foi apagada com sucesso, ou apresentar uma justificativa do erro.

Nesse sentido, pensando em não ter nenhuma dependência à View, como vocês procedem nessa comunicação entre camadas? De forma à Controller receber as informações que precisa e retornar os dados?

Ex:

class ControllerPessoa {

    public void apagarPessoa(Request req, Response res) {
            //
            Pessoa p = (Pessoa)req.getAttribute("pessoa");
            //..acessa o modelo e apaga a pessoa
           res.setAttribute(RETORNO, "OK");
    }
}

ou algo mais simples:

class ControllerPessoa {

    public String apagarPessoa(Pessoa p) {
            //..acessa o modelo e apaga a pessoa
           return("OK");
    }
}

A pergunta é, como vocês fazem? (Se não for pedir demais, um exemplinho simples :))

Será que me fiz entender? Posso estar confundindo coisas..

Obrigado desde já,

Saudações

nbluis

Oi peron;

Isso vai depender muito do cenário.

Depende de coisas como arquitetura, convenção e até mesmo framework que vc utiliza (se utiliza) para ligar esses componentes.

fabiofalci

Olá!

Pode usar o xwork para ter somente uma classe Action para web e swing.

Na web vcs já sabem com funciona… é só olhar o webwork.

No swing, uma ação da interface (botão por exemplo) chama uma
Action do xwork. Este faz o que tem que fazer e coloca valores
que a view precisa no ActionContext.
Retorna o result (SUCCESS por exemplo) que então dispara um tipo
de Result. Este Result em si é específico da view, por exemplo
um pra swing seria o OpenPanelResult que abre um JPanel.

peerless

Bom, fiz aqui uma aplicação de exemplo no netbeans. Gostaria que, os interessados, dessem uma olhadinha no código, e baseado nisso, pudessem redefinir, dentro da crença correta e postar ai para galera se basear.

eu sei que não é o certo, mas a nomenclatura dos pacotes seguiu mvc… desculpem!

‘‘Refatorem’’ este exemplo, da forma que bem entender. Deste nomenclaturas, etc.

Muito obrigado, novamente. abraços!!

pcalcado

Se você está numa arquitetura web provavelmente vai utilizar o tal MVC2, que não é MVC de verdade. Neste cenário você em pacota o que for passar para a view na resposta como no meu primeiro exemplo ou mantêm os objetos do modelo em algum lugar que a view acesse, comoe scopo de sessão.

Se não estiver preso à web e ao pseudo-MVC MVC2 pode fazer como o Sérgio falou: a cada alteração no model a view é notificada e se atualiza. A forma de fazer isso depende da sua implementação, de qualquer forma dê uma olhada no padrão Observer.

Só uma coisa, lembre-se sempre:

pcalcado:
Não são Camadas, são simplesmente componentes do MVC. MVC não é sobre Camadas.

http://fragmental.com.br/wiki/index.php/MVC_e_Camadas

sergiotaborda

peerless:
Bom, o problema é que estou em fase de transição deste tipo de arquitetura. Ouvi muito assunto errado sobre isso. Afirmações falsas.

Continuando na idéia do Swing…

Eu não consigo pensar numa situação do model atualizando a view… como um negocio atualizaria um JFrame por exemplo??

não pense num JFrame, pense num JTable e seu TableModel
(JFrame é um container, não tem dados associados(normalmente))

Não. O view faria algo como

tfNome.setText(model.getValueFor("nome"));
//ou
tfNome.setText(model.getNome());

o model varia algo como

public String getNome(){
   return cliente.getNome();
}
peerless
sergiotaborda:
peerless:
Bom, o problema é que estou em fase de transição deste tipo de arquitetura. Ouvi muito assunto errado sobre isso. Afirmações falsas.

Continuando na idéia do Swing...

Eu não consigo pensar numa situação do model atualizando a view... como um negocio atualizaria um JFrame por exemplo??

não pense num JFrame, pense num JTable e seu TableModel
(JFrame é um container, não tem dados associados(normalmente))

tfNome.setText(Cliente.getNome()) ;

isso seria um modelo atualizar a view? hehehe

Não. O view faria algo como

tfNome.setText(model.getValueFor("nome"));
//ou
tfNome.setText(model.getNome());

o model varia algo como

public String getNome(){
   return cliente.getNome();
}

Beleza sergio, tá começando a clarear.

Tu poderia baixar meu exemplo, postado logo acima, e avaliar? Valeu! :wink:

sergiotaborda
peerless:
private class ViewHandler extends MouseAdapter {
       private Controlador controle = new Controlador();
        public void mouseClicked(MouseEvent e) {
            if (e.getSource() == btnSet) {
                Model m = new Model();
                m.setNome(tfNome.getText());
                controle.add(m);
                atualizarLista();
            }
            else if (e.getSource() == btnRemove) {
                controle.remove(listaNome.getSelectedIndex());
                atualizarLista();
            }
        }
        
        void atualizarLista() {
                listaNome.removeAll();
                for (int i =0;i<=controle.list().size()-1;i++) {
                    listaNome.add(controle.get(i).getNome());
                }  
        }
    }

O principal problema está neste pedaço.
A view não sabe o que significa "setar" nem "remover"
O que ela sabe é que "quando o botão btnSet for apertado avisa o controlador desse fato"
Quem sabe o que fazer é o controller.

Depois tem o principio "peça, não pergunte" (Aks, don't tell)
Aquele for deveria estar dentro do controlador porque é uma modificação do modelo e não no view( muito menos no view handler).

Eis uma ideia de como seria

class .... implements View{


public String getNome(){
    return txt.getText();
}

public void updateFromModel(Model m){
       txt.setText(m.getNome());
}

private class ViewHandler extends MouseAdapter {
       private Controlador controle = new Controlador();
        public void mouseClicked(MouseEvent e) {
            if (e.getSource() == btnSet) {
                controler.setNewValues(view); // isto é um evento. Poderia usar o padrão observer
            }
            else if (e.getSource() == btnRemove) {
                controler.removeValues(view); // isto é um evento. Poderia usar o padrão observer
            }
        }
        

    }

class Controller{

   Model m = new Model(); // inicia so uma vez

   setNewValues(View view){
                m.setNome(view.getNome());
                atualizarLista(view);

  }

        void atualizarLista(View view) {
                 view.updateFromModel(); // cuidado com loop infinito
        }

Este boneco [url]http://pt.wikipedia.org/wiki/Imagem:ModelViewControllerDiagram.svg[/url]
é tudo(!) o que precisa entender de MVC. As linhas tracejadas significam "envia evento" e as cheias "lê"

Controller, View e Model antes de tudo têm que ser interfaces que falam umas com as outras.
Depois vc implementa cada interface com a tecnologia que mais for apropriada. Como as interfaces garatem independencia , vc pode intercambiar uma das partes sem ter que alterar a outra.

Criado 7 de novembro de 2007
Ultima resposta 7 de nov. de 2007
Respostas 30
Participantes 7