Proposta de refactory (VRaptor)

10 respostas
leonardofl

Olá pessoal,

eu tenho um projeto web que fiz com o VRaptor 3, o WikiLibras - http://code.google.com/p/wiki-libras

Neste sistema há um formulário em que o usuário envia alguns dados, mas esse formulário é muito grande, trata-se do cadastro de um “sinal”. Como o sinal pode conter vários “símbolos” (http://code.google.com/p/sign-model/) então é preciso uma certa lógica de controle para repetir parte o formulário. Tudo isso me levou a modelar a situação no VRaptor da seguinte forma: tenho UM formulário pra cadastrar o sinal que possui várias páginas. Aí pra dar conta disso, criei um componente SessionScoped que acompanha o vai e volta das páginas do formulário.

Mas pelo o que tinha entendido do VRaptor, a prática seria criar um controller por formulário, ou entidade sendo cadastrada (posso estar bem errado). Aí isso me levou a criar um Controller enorme!

Gostaria de saber se alguém poderia me dar uma dica de como melhorar esse código, e dividir meu controller em mais classes (boa parte da lógica não adianta muito delegar pq é pra mexer no Result no VRaptor). Uma coisa que tinha pensado seria criar um Controller para cada página do formulário, mas não sei como isso não atrapalharia a formação da URL das páginas…

Bom, eis meu controller: http://code.google.com/p/wiki-libras/source/browse/trunk/wikilibras/src/br/usp/wikilibras/control/SignController.java

tks
Leonardo

10 Respostas

dreampeppers99

Primeiramente parabéns pelo projeto, fanstástica sua iniciativa. ;)

Não conheço muito bem o VRaptor mas esse refatoração pode ser feita independente da tecnologia, divida seu controller de forma que você ache mais lógica (ex: tipo um controller para cada um dos models) e depois você apenas (inicialmente) delega as chamadas.

Inicialmente você continua com o SignController (mas meio que agindo como um façade).

Extraindo a lógica, isso abaixo.

public Sign editSymbolForm() {

        // símbolo a ser editado já está determinado na sessão (signEditor)
        int signIndex = this.signEditionSession.getSignIndex();

        // fornece informações sobre locações e contatos disponíveis para o formulário

        // lista com grupos de locações
        List<String> locationGroups = new ArrayList<String>();
        locationGroups.add(ALL);
        // lista com todas as locações
        List<String> locations = new ArrayList<String>();
        for (Location s : Location.values()) {
            locations.add(s.toString());
        }
        // mapa de listas de locações por grupo
        Map<String, List<String>> locMap = new HashMap<String, List<String>>();
        locMap.put(ALL, locations);
        for (LocationGroup g : LocationGroup.values()) {
            locationGroups.add(g.toString());
            for (Location s : Location.values()) {
                if (s.locationGroup() == g) {
                    if (locMap.get(g.toString()) == null)
                        locMap.put(g.toString(), new ArrayList<String>());
                    locMap.get(g.toString()).add(s.toString());
                }
            }
        }

        result.include("contacts", Contact.values());
        result.include("locations", Location.values());
        result.include("locationGroups", locationGroups);
        result.include("locationsMap", locMap);
        result.include("signIndex", signIndex);
        result.include("newSign", this.signEditionSession.isNewSign());
        result.include("twoHands", true); // padrão para caso de inclusão de sinal

        // caso estejamos editando o símbolo
        if (!this.signEditionSession.isNewSign() && (this.signEditionSession.getSign().getSymbols().size() >= signIndex)) {

            // devemos passar para o fomrulário o símbolo a ser editado
            Symbol symbol = this.signEditionSession.getSign().getSymbols().get(signIndex - 1);
            this.signEditionSession.setTwoHands(symbol.getLeftHand() != null);
            result.include("symbol", symbol);
            result.include("twoHands", this.signEditionSession.isTwoHands());
        }

        return this.signEditionSession.getSign();
    }

Viraria só uma delegação, algo mais ou menos assim.

public Sign editSymbolForm() {
        return symbolController.signEditionSession.getSign();
    }

Com essa refatoração (extrair lógica para classes) você vai diminuir o nível de acoplamento da sua SignController e dividir essa responsabilidade em mais componentes menores e mais lógicos.

Lucas_Cavalcanti

a regra de criar um controller seria a princípio um controller por recurso…

mas como a orientação a objetos nos diz, classes mto grandes tendem a ter responsabilidades demais, então em todo caso vc deveria quebrar classes grandes em classes menores, que fazem sentido juntas

leonardofl

Eh… o problema é que meu recurso (o sinal) é algo enorme, oq me levou a fazer isso… (bom, pelo menos oq me induziu a fazer o controller gigante não foi a toa)

Mas valeu pessoal, vou pensar no que vocês falaram e tentar dividir essa controller…

Só uma coisa, será q seria melhor criar vários controllers menores, ou só quebrar o controller grande? (se é q essa pergunta faz sentido…)

Lucas_Cavalcanti

criar vários controllers menores == quebrar o grande :wink:

se vc quiser manter as URIs do mesmo jeito, vc pode colocar nos controllers quebrados @Path("/sinal") na classe (ou coisa do tipo)

leonardofl

Ah, boa dica! Valeu!

leonardofl

Olá!

Comecei a fazer meu refactory.

Criei um novo controller para separar o formulário de sintaxe, e ele ficou com a seguinte cara:

@Resource
@Path("/sign")
public class SignSyntaxController {

...

    // prepara o formulário
    public Sign editSyntaxForm() {
        ....

        return this.signEditionSession.getSign(); // (*)
    }

    // processa o formulário preenchido
    public void editSyntax(Sign sign, String[] word, String[] literal, String[] inherence) {
       ....
    }
}

Obs: o controller original é o SignController.
Os métodos do código acima foram retirados do SignController.

Agora o PROBLEMA.

Na linha marcada com (*) o que acontece é que o vraptor tenta chamar o formulário jsp/signSyntax/editSyntaxForm.jsp, mas eu gostaria que continuasse no mesmo lugar, o jsp/sign/editSyntaxForm.jsp. É possível? O Path lá em cima não deveria ter resolvido isso?

valeu…

Lucas_Cavalcanti

o @Path() só tem a ver com a URL, não com os jsps…

vc precisa mesmo que os jsps estejam na mesma pasta? não fica mais organizado separá-los também?

em todo caso dá pra configurar isso e fazer o vraptor usar o @Path como pasta do jsp tb.

leonardofl

Não. Mas no meu caso específico acho que vai ficar melhor organizado, pq se não vai ficar um jsp por pasta e um monte de pastas.

Lucas_Cavalcanti

ter uma lógica por controller também não é muito bom…

para resolver o seu problema, vc precisa sobrescrever a convenção do VRaptor, criando a classe:

@Component
public class MeuPathResolver implements PathResolver {
   //.. implementação aqui
}

vc pode se basear no DefaultPathResolver:

ou fazer como está aqui:
http://vraptor.caelum.com.br/documentacao/configuracoes-avancadas-sobrescrevendo-as-convencoes-e-comportamento-do-vraptor/

leonardofl

Valeu =)

Criado 14 de julho de 2011
Ultima resposta 1 de ago. de 2011
Respostas 10
Participantes 3