Melhor forma de realizar a divisão de tarefas nas actions do Struts

23 respostas
C

Pessoal, bom dia.

No projeto do qual participo, ficou decidido que seria utilizado DispatchActions, e seria criada uma action por caso de uso. As classes que contém as regras de negócios também foram divididas por caso de uso.

Por exemplo, na parte de manipulação de dados, existe um caso de uso para cadastramento, e outro para manutenção de dados (este ultimo engloba operações do tipo edição, alteração, exclusão etc).

Pois bem, deixa eu dar um exemplo sobre esta implementação de casos de uso, para poder demonstrar uma dúvida que possuo.

Criei uma Action de cadastro, vamos chamar de cadastrarDadosAction. Esta action possui os métodos “inserir”, “inserirSalvo” e “visualizarDadosCadastrados”. Tmb foi criada uma classe que contém as regras de negócios chamada CadastrarDadosBLL. Esta classe contém os métodos “listarUF” e “inserirDados”.

Criei uma outra Action para a manutenção dos dados, vamos chamar de ManterDadosAction. Esta action possui os métodos “iniciar”, “listar”, “excluir”, “alterar”, “alterarSalvo”, “listarDados” e “prepararIniciarPaginaManipulacaoDadosVistoriador”. Também foi criada uma classe que contém as regras de negócios chamada ManterDadosBLL. Esta classe contém os métodos “listarDados”, “obterRegistro”, “alterarRegistro”, “excluirRegistro”, “listarUF”.

Na Action “cadastrarDadosAction”, o método “inserir” faz a carga da página que será utilizada na inseção dos dados, realizando o carregamento de dados para preenchimento de combos, entre outras tarefas.
O método “inserirSalvo” é responsável pela gravação dos dados, mostrando ao final uma página com os dados gravados, para configramação.

Na Action “ManterDadosAction”, o método “iniciar” faz a carga da página.
O método “listar” realiza uma pesquisa com os dados fornecidos pelo usuário, retornando o resultado para a página de manutenção de dados, que irá popular uma grid, que possue ao lado de cada linha os links “excluir” e “alterar”.

O método “excluir” é chamado quando se clica no link “exluir”. Ele realiza a exclusão do registro informado pelo usuário. Como é necessário retornar para a página de manutenção de dados com os dados da grid, e sabendo que este processo tmb será necessário no método “alterarSalvo”, retirei esta função do método listar e a coloquei no método “listarDados”, que é chamado pelos métodos “listar”, “excluir” e “alterarSalvo”, nestes dois ultimos caso não ocorra nenhuma exceção nas regras de negócios.

O método “alterar” deverá carregar os dados numa página para que seja realizada a modificação. Neste caso resolvi utilizar a mesma página usada para o cadastro, só modificando a nela a action e o método que serão chamados no momento da confirmação dos dados, e o título da página. Estas modificações passo para a página via requisição.
Aí já começa algum dos meus problemas.
Esta página eu estou chamando diretamente, tendo em vista que preciso setar algumas modificações. Como esta página de cadastro possui uma combo contendo os estados, devo realizar a carga dos estados antes de prosseguir. Para realizar esta carga, deve existir na minha classe de negócios um método que realize tal tarefa.
Por causa disso necessitei criar o método “listarUF” nas classes de negócios CadastrarDadosBLL e ManterDadosBLL.
Se fosse necessário fazer a carga de 5 campos diferentes, este código seria replicado nas duas actions e nas duas classes de negócios (no caso da classe de negócios o que seria replicado seria mais a chamada, pois o único papel deste método é encapsular o acesso ao médoto da minha classe DAO, que faz o acesso direto aos dados).

Existiria alguma forma melhor de se montar estas actions, de forma a eliminar estas duplicidades?

E quanto a minha decisão de criar um método que concentra um processo comum em várias actions? Está correto? Ou o melhor seria criar um outro método de ação dentro desta action que contenha estes processos, e realizar um forward para ele? Eu tinha feito desta forma inicialmente, mais como estava tendo problemas com a paginação do grid-layout, resolvi mudar. Além que ficou extranho fazer um forward para uma outra ação dentro da minha mesma action, quando poderia acessar diretamente.

23 Respostas

Kleber_Santos

O método “alterar” deverá carregar os dados numa página para que seja realizada a modificação. Neste caso resolvi utilizar a mesma página usada para o cadastro, só modificando a nela a action e o método que serão chamados no momento da confirmação dos dados, e o título da página. Estas modificações passo para a página via requisição.
Aí já começa algum dos meus problemas.

Acho melhor separar as telas de cadastro e alteração.
É complicado fazer isso?

:lol: :lol:

jcranky

Kleber Santos:
Acho melhor separar as telas de cadastro e alteração.
É complicado fazer isso?

Porque? Telas de cadastro e alteração tendem a ser bem parecidas, qual o problema em se fazer um reuso? :?

pcalcado

Olá,

Você está colocando responsabilidades demais na camada de apresentação.

Divida tarefas entre camadas, crie uma camada de negócios e chame-a através das suas Actions, não processe dados de negócio nas Actions.

Shoes

Mauricio_Linhares

Você tá indo no caminho certo, se a coisa é só ir “pra lá e pra cá” com as informações do banco de dados e não tem nenhum código de banco de dados dentro dos Action, tá tudo certo.

É bom definir um action pra cuidar de uma, e apenas uma, entidade. Se você tem uma entidade usuário, crie um UsuarioAction, que faça tudo do usuário, criar, editar, excluir e listar, mas esse action não deve fazer mais nada com nenhuma outra entidade. Quando eu fiz, criei uma classe abstrata CrudAction, que fazia todo o trabalho repetido, e as outras actions implementavam o que havia pra ser implementado, que eram só os métodos de preencher as classes do modelo.

Eu, pessoalmente, não gosto de DispatchActions, porque eles dificultam o uso de ActionForms pra cada método, eu prefiro usar MappingDispatchActions, que me deixam definir um ActionForm pra cada método a ser chamado dentro daquele Action.

E sobre fazer o forward, não faça mesmo não, eu tive um problemão aqui por causa disso no Tomcat, usando um filtro pra fechas as conexões com o banco, quando eu dava o forward, o filtro simplesmente não terminava de executar, evite forwards o máximo possível.

Kleber_Santos

blz, se são “parecidas” crie uma camada de negócios pra isso…

Kleber_Santos

gangrel-br, no que eu entendi ele esta se perdendo numa coisa que eu acho básica e esta acontecendo varios erros, se for utilizar o reuso, isso tem q ser pensado em ultimo caso, pq isso pode afetar as camadas de negócio…

bele

C

Kleber Santos:

Acho melhor separar as telas de cadastro e alteração.
É complicado fazer isso?

:lol: :lol:

Kleber, neste caso até que a tela é simples, mais existem outros casos de usos em que as telas são bem mais complexas. Acredito que separar as telas só aumentaria na duplicação de código. O código que inseri dentro da tela para informar qual action chamar e mudar o título, ficou bem pequeno (acho que são 2 ou 3 linhas).

Não acredito que isto vai ajudar. O que eu estou na dúvida é a melhor forma de separar as actions, para evitar justamente duplicação de código. :roll:

Mauricio_Linhares

caiosiqueira:

Não acredito que isto vai ajudar. O que eu estou na dúvida é a melhor forma de separar as actions, para evitar justamente duplicação de código. :roll:

Se cada Action só faz os serviços de uma entidade, como é que você está encontrando duplicação de código?

C

pcalcado:
Olá,

Você está colocando responsabilidades demais na camada de apresentação.

Divida tarefas entre camadas, crie uma camada de negócios e chame-a através das suas Actions, não processe dados de negócio nas Actions.

Shoes

Olá pcalcado,

acho que você não me entendeu. Eu estou utilizando divisões de camadas. A minha action está chamando um método existente numa classe de negócios. Não processo regras de negócios na action.

O problema é que no modelo que estou usando, foi decidido que deveria ser dividido as actions e classes de negócios por caso de uso.

Como o caso de uso Cadastrar Dados e Manter Dados acabam utilizando uma tela em comum, que é a tela de manipulação de dados, e esta tela possui uma combo que deverá ser preenchida com valores oriundos da base de dados, preciso replicar a chamada a minha classe de persistência nas classes de negócios referentes aos casos de uso Cadastrar Dados e Manter Dados.

Gostaria de saber se seria melhor unificar estas duas classes de negócios e actions em uma única, que representaria a entidade, como o Maurício sugeriu. Eu acredito que seria melhor, mais quero ter certeza para poder conversar com o analista do projeto sobre isso.

C

Por casa do modelo empregado, aonde existe uma action e uma classe de negócios para cada caso de uso.

Neste caso específico, por existirem dois casos de uso que se referenciam há uma mesma entidade, acaba gerando esta duplicação. Se bem que esta duplicação até agora se resumiu a criação de um mesmo método nestas duas classes de negócios que se referenciam a um mesmo método de uma mesma classe de persistência. Só levantei esta bola que achei que este mapeamento ficou extranho.

Kleber_Santos

Concordo com o Marcelo Linhares…

pq?

Mauricio_Linhares

Acho que o problema é que alguém está misturando casos de uso com o modelo de negócios.

Pra mim, casos de uso servem pra ajudar a modelar o sistema e dizer o que ele deve ou não fazer, não como ele deve ser feito. Se você for criar um Action pra cada caso de uso, imagine quando você for fazer as implementações dos casos de uso que tenham como pre-requisito o login no sistema, como é que você vai fazer isso?

Divida as responsabilidades das coisas por entidades, fica muito mais fácil de você lidar, porque mudar uma entidade não vai mudar os milhares de actions que tem casos de uso relacionados a ela, vai mudar só o action dela.

C

Maurício Linhares:

Divida as responsabilidades das coisas por entidades, fica muito mais fácil de você lidar, porque mudar uma entidade não vai mudar os milhares de actions que tem casos de uso relacionados a ela, vai mudar só o action dela.

Beleza cara. Vou conversar com o Analista sobre isso para mudar esta politica de criação de Actions. Também acho que por entidade ficará mais simples.

Aproveitando, deixa lhe perguntar outra coisa. Pretendo usar validação automática. Neste caso acabei criando um único formBean, que serve tanto a tela de manutenção de dados como a que contém o cadastro. Fiz isso para transportar os dados que foram selecionados na tela de manutenção de dados para a tela de cadastro.
Eu conseguiria definir, por exemplo, que determinados campos deste form devem ter seu preenchimento obrigatório na tela de cadastro, e na tela de manutenção não devem ser validados? Como você trataria este caso? Você usa um único formBean, ou o melhor seria criar mais de um, utilizando uma action que extende MappingDispatchActions?

J

Acho legal fazer um ActionClass por form.
Isso seria uma boa ideia ? :roll:

O

Olá Caio,

Quanto a validação, pelo fato de estar utilizando DispatchActions e não estar utilizando Validator, acho que poderia fazer o seguinte, codificar dentro dos seus métodos da Action e chamar a validação na mão, pois me lembro de ter enfrentado certo problema ao utilizar DispatchActions com “validate=true”, portanto mantenha validade=false, implemente o método validate() no ActionForm, e chame manualmente conforme o exemplo abaixo:

public ActionForward save(ActionMapping mapping, ActionForm form, 
HttpServletRequest request, HttpServletResponse response) throws Exception {
        //Criando objetos ActionMessages        
        ActionMessages errors = new ActionMessages();
                
        //Capturando erros de validação por form
        errors = form.validate(mapping, request);
        
        //Verificando se houve erro de validação de form
        if ( errors != null && !errors.isEmpty() ) {
            //Salva o erros encontrados em form.validate() no request
            saveErrors(request, errors);
            
            //Carregando recursos necessários para o form
            setUp(request);
            
            //Redireciona para o próprio form
            return (mapping.findForward(UIConstants.TO_FORM));
        }

Espero que possa ser útil.

Abraço.

Mauricio_Linhares

caiosiqueira:

Aproveitando, deixa lhe perguntar outra coisa. Pretendo usar validação automática. Neste caso acabei criando um único formBean, que serve tanto a tela de manutenção de dados como a que contém o cadastro. Fiz isso para transportar os dados que foram selecionados na tela de manutenção de dados para a tela de cadastro.

Eu conseguiria definir, por exemplo, que determinados campos deste form devem ter seu preenchimento obrigatório na tela de cadastro, e na tela de manutenção não devem ser validados? Como você trataria este caso? Você usa um único formBean, ou o melhor seria criar mais de um, utilizando uma action que extende MappingDispatchActions?

Se você usar o DispatchAction vai ser mais complicado, mas seria possível, usando o MappingDispatchAction é muito mais fácil, porque você poderia definir um ActionForm pra cada método dentro do MappingDispatchAction.

Sobre a validação automática, é possível fazer desse jeito, você pode reutilizar a mesma classe para dois ActionForms diferentes, só que você vai ter que dar um nome diferente pra cada “encarnação” do ActionForm e vai ter que definir as validações específicas de cada um deles lá nas configurações do Validator.

C

oandrade:
Olá Caio,

Quanto a validação, pelo fato de estar utilizando DispatchActions e não estar utilizando Validator, acho que poderia fazer o seguinte, codificar dentro dos seus métodos da Action e chamar a validação na mão, pois me lembro de ter enfrentado certo problema ao utilizar DispatchActions com “validate=true”, portanto mantenha validade=false, implemente o método validate() no ActionForm, e chame manualmente conforme o exemplo abaixo:

Olá Andrade.

Quanto a validação, eu gostaria de fazer usando o validade automático, por já implementar a validação no cliente e no servidor, e possuir algums validações interessantes, como a máscara usada pelo campo.

C

Pessoal, vocês aconselham utilizar o validador automático, ou é melhor fazer na mão?

Pergunto isso pois já comecei a implementar o validadorAutomático aqui. Até que estou conseguindo, mais fiquei decepcionado com um erro que ocorre no javaScript que ele gera. Logo um dos diferenciais dele, que é gerar também o código JavaScript, está com erro. O erro ocorre no seguinte código: var formName = form.getAttributeNode(“name”);

E aí, ainda assim vale a pena utilizar validador automático, ou o melhor é implementar na mão, tanto no cliente como no servidor?

Mauricio_Linhares

Eu nunca vi nenhum erro no Validator…

Que erro é esse que ele está encontrando? Acontece o que com o form?

Eu só escrevo validação quando o validator não tem nenhuma opção, o que é raríssimo.

C

Maurício Linhares:
Eu nunca vi nenhum erro no Validator…

Que erro é esse que ele está encontrando? Acontece o que com o form?

Eu só escrevo validação quando o validator não tem nenhuma opção, o que é raríssimo.

Olá Maurício.

No caso ele gera o javaScript na página. O problema é quando o browser vai executar o código javaScript referente a validação required, dá erro na seguinte linha:

var formName = form.getAttributeNode(“name”);

Será que pode ser algum bug que já tenha sido corrigido? Qual versão do Struts você tá usando?

Outra coisa. Eu estou fazendo da forma que você indicou, criando uma mapeamento para cada método da minha action. Estou também fazendo o mapeamento das minhas validações no arquivo de configurações do Validator utilizando o path do mapeamento dos métodos da Action. Eu declarei neste arquivo o mapeamento do método inserirSalvo, para que a validação seja realizada quando esta action for executada. Só que eu gostaria de colocar também o mapeamento do método alterarSalvo, para que a mesma validação seja executada quando o usuário chamar esta action. Você saberia me informar como realizar esta configuração no validator? Assim, a validação seria executada no form quando somente um destes dois mapeamentos fosse executado.

Atualmente esotu usando somente um formBean para as duas páginas, pois não sei como fazer a action receber um formBean da página de manutenção de dados, e preencher e enviar outro formBean. Além do mais eu preciso armazenar os dados da pesquisa, pois no final da atualização dos dados deverei exibir novamente a tela de manutenção de dados, com a pesquisa realizada pelo usuário e devidamente atualizada.

Mauricio_Linhares

Mas ele não está executando a validação “required” no JavaScript?

A versão que eu uso é a 1.2.7 e ela funciona normalmente, inclusive o Javascript.

Não entendi o problema das configurações dos mapeamentos, cada mapeamento pode ter um ActionForm diferente, você só precisa mudar o atributo “name” pra indicar qual é o ActionForm que ele deve utilizar.

Também não entendi nada da segunda. Se você precisa de 2 ActionForms diferentes, porque não usa os dois diferentes?

C

Maurício Linhares:
Mas ele não está executando a validação “required” no JavaScript?

A versão que eu uso é a 1.2.7 e ela funciona normalmente, inclusive o Javascript.

Não entendi o problema das configurações dos mapeamentos, cada mapeamento pode ter um ActionForm diferente, você só precisa mudar o atributo “name” pra indicar qual é o ActionForm que ele deve utilizar.

Também não entendi nada da segunda. Se você precisa de 2 ActionForms diferentes, porque não usa os dois diferentes?

Olá Marício.

É isso que ocorre. Quando é chamado a função validateCadastroVistoriadorForm(this) (no me caso) do JavaScript, o browser informa que existe um erro no JavaScript, justamente na função validateRequired. A linha que ocorre o erro é a que passei na mensagem anterior.

Quanto ao problema do ActionForm, pensando melhor eu preciso somente de 1.

Eu necessito de duas telas para realizar a função de alteração de dados: Em uma tela, o usuário faz uma pesquisa, que retornará vários resultados a serem exibidos numa grid.

O usuário selecionará nesta grid o registro que deseja modificar. Feito esta seleção, será executada uma action que abrirá a minha outra tela (a de cadastro), devidamente preenchida.

Depois que o usuário terminar de modificar os dados, ele chamará uma action que persistirá estas modificações no banco, e que deverá retornar para a minha primeira tela (a de pesquisa). Esta tela de pesquisa deverá exibir para o usuário os dados da pesquisa que ele realizou anteriormente, só que com os dados relativos ao registro que ele modificou devidamente atualizados. Ou seja, preciso possuir num mesmo form os dados do registro (para inclusão / modificação) e os dados da pesquisa, concorda?

Atualmente eu utilizo somente um ActionForm, que possui dados referentes a tela de cadastro e a de pesquisa. Suponhamos que este Action form precise de duas validaçõs distintas:

  • A primeira seria realizada nos dados do cadastro (na saída da tela de cadastro), no momento em que o usuário executar a a action de inclusão dos dados no banco, ou a de alteração dos dados no banco. Este Validator resume-se em princípio na verificação se os campos foram devidamente preenchidos;

  • A segunda será realizada no valor que o usuário irá informar para pesquisa (na tela de pesquisa), no momento que pressionar o botão
    "pesquisar", que chamará a action que retornará os dados desejados pelo usuário. Este Validator resume-se em princípio na verificação se o campo de valor de pesquisa foi devidamente preenchido.

Bom, aqui tenho um dilema. Necessito de um único form, pois no processo de alteração de um registro do banco de dados, necessito ter armazenado os dados da pesquisa realizada (pois quando voltar para esta tela os dados da pesquisa deverão continuar sendo exibidos), e os dados do cadastro, para que o usuário possa realizar as devidas alterações e depois enviar para a action que gravará no banco.
Ao mesmo tempo, deverei realizar validações diferentes no mesmo ActionForm, dependendo da tela em que me encontre.

E a minha pergunta neste caso seria: Como resolver este problema?

Se eu usar o método Attribute para indicar um nome diferente para o form em cada mapeamento da action, de forma a utilizar um nome de form na tela de cadastro e um outro na tela de pesquisa, eu conseguiria utilizar estes nomes na configuração do Validator? Ou o Validator só aceita o nome declarado no mapeamento do ValidatorForm no Struts (na verdade uso o ValidatorForm, por causa da validação automática)?

C

Pessoal,

tirando a parte do JavaScript que ainda falta resolver, já consegui definir como trabalhar com as actions e o validator no sistema, e está funcionando que é uma belza. :lol:

Quanto ao problema com o JavaScript, vou tentar resolver. Não conseguindo, abro um outro tópico no outro forum, tendo em vista que aqui é específico para discuções sobre padrões de projetos e arquiteturas, e não sobre problemas de framework. Como a conversa acabou indo para este lado (por causa dos meus problemas), o melhor é terminar as conversas por aqui e continuar num tópico futuro que será postado no forum correto. Isto se eu não conseguir resolver antes o problema com o JavaScript.

No mais, quero agradecer a todos. A ajuda que vocês me deram foi de grande valia para entender melhor como funciona as Actions e Validators no Struts, e ajudaram a definir uma boa forma para se trabalhar com estes recursos.

Um grande abraço, e que Deus os abençoem.

Criado 22 de junho de 2005
Ultima resposta 23 de jun. de 2005
Respostas 23
Participantes 7