Como alguém pode evitar escrever código GUI inchado?

46

Encontro sempre que trabalho com código GUI, o código tende a inchar mais rápido do que outros tipos de código. Também parece mais difícil refatorar. Enquanto em outros tipos de código eu posso refatorar muito facilmente - eu acho que posso decompor uma classe maior em pequenos pedaços de funcionalidade - com a maioria dos frameworks GUI eu estou frequentemente ligado a um framework que requer meu widget / controle / qualquer classe para implementar muito mais coisas mais diretamente no widget / controle / whatever. Às vezes, isso ocorre devido à necessidade de (a) herdar de algum widget / controle / coisa base ou (b) precisar de acesso a métodos protegidos.

Normalmente, eu também tenho que, por exemplo, responder a uma grande variedade de entradas via sinais / eventos / o que quer que seja da estrutura para implementar todos os modos de interação com o usuário. Eu posso precisar de um widget / controle de GUI para lidar com uma grande variedade de entrada / saída que pode incluir:

  1. um clique com o botão direito do mouse / menu de contexto
  2. reagindo a seleções do menu de contexto - que podem ser muitas
  3. uma maneira especial de pintar a GUI
  4. reage à entrada do teclado
  5. botões, caixas de seleção
  6. etc etc

... o tempo todo gerencia as classes sob a GUI representando a lógica de negócios.

Uma GUI simples e direta pode fazer seu código crescer rapidamente, mesmo quando separando a lógica de negócios e usando o MVC, acho que o código GUI é um grande ímã para a mudança.

Existe alguma maneira de gerenciar o código GUI de uma maneira sã e evitar que ele se torne uma janela quebrada? Ou é uma massa de manipuladores de eventos aleatórios / métodos substituídos realmente o melhor que podemos fazer para o código GUI?

    
por Doug T. 17.04.2012 / 02:31
fonte

10 respostas

36

A coisa a ser lembrada sobre o código da GUI é que ele é orientado a eventos e o código orientado a eventos sempre terá a aparência de uma massa de manipuladores de eventos organizados aleatoriamente. Onde fica realmente confuso é quando você tenta encaixar o código não orientado a eventos na classe. Claro, tem a aparência de fornecer suporte para os manipuladores de eventos e você pode manter seus manipuladores de eventos agradáveis e pequenos, mas todo esse código de suporte extra flutuando faz com que sua fonte da GUI pareça inchada e confusa.

Então, o que você pode fazer sobre isso e como você pode tornar as coisas mais fáceis de refatorar? Bem, primeiro eu mudaria minha definição de refatoração de algo que faço ocasionalmente para algo que faço continuamente enquanto codifico. Por quê? Porque você quer que a refatoração permita que você modifique mais facilmente seu código, e não o contrário. Eu não estou simplesmente pedindo para você mudar a semântica aqui, mas pedindo que você faça um pouco de calistenia mental para ver seu código de maneira diferente.

As três técnicas de refatoração que eu uso mais comumente são Renomear , Extrair Método , e Extrair Classe . Se eu nunca aprendi uma única refatoração, esses três ainda me permitiriam manter meu código limpo e bem estruturado, e pelo conteúdo da sua pergunta, parece-me que você provavelmente se verá usando as mesmas três refatorações quase que constantemente. para manter seu código GUI fino e limpo.

Você pode ter a melhor separação possível entre a GUI e a lógica de negócios no mundo, e ainda assim o código da GUI pode parecer um código que a mina foi detonada no meio dela. Meu conselho é que não atrapalhe ter uma classe extra ou duas para ajudá-lo a gerenciar sua GUI adequadamente, e isso não precisa necessariamente ser suas classes Visualizar se você estiver aplicando o padrão MVC - embora freqüentemente você descubra que as turmas intermediárias são tão parecidas com a sua opinião, que muitas vezes você sente vontade de mesclá-las por conveniência. Minha opinião sobre isso é que não custa nada adicionar uma camada específica de GUI adicional para gerenciar toda a lógica visual, no entanto, você provavelmente quer avaliar os benefícios e os custos de fazer isso.

Meu conselho, portanto, é:

  • Não faça nada diretamente atrás de sua GUI, exceto para invocar e definir como a GUI será conectada à Visualização (ou a uma camada intermediária).
  • Não tente aplicar cada coisa relacionada à visualização em uma única classe - ou mesmo em uma única classe por janela de GUI - a menos que isso faça sentido para você. Sua alternativa é criar um monte de pequenas e fáceis de gerenciar classes para gerenciar sua lógica de GUI.
  • Quando seus métodos estão começando a parecer um pouco maiores que 4-5 linhas de código, examine se isso é necessário e se é possível extrair um método ou dois para que você possa manter seus métodos enxutos, mesmo que isso significa uma classe com muitos outros métodos.
  • Se suas classes estiverem começando a parecer realmente grandes, comece removendo TODAS as funcionalidades duplicadas e, em seguida, veja se você pode agrupar logicamente seus métodos, de modo que possa extrair outra ou duas classes.
  • Pense em refatorar toda vez que você escrever uma linha de código. Se você conseguir que uma linha de código funcione, veja se consegue refatorá-la para evitar a duplicação da funcionalidade ou para torná-la um pouco mais enxuta sem alterar o comportamento.
  • Aceite o inevitável, que você sempre sentirá que uma parte ou outra em seu sistema começará a se sentir um pouco inchada, especialmente se você negligenciar a refatoração conforme avança. Mesmo com uma base de código bem-fatorada, você ainda pode sentir como se houvesse mais que você poderia fazer. Esta é a realidade do software de escrita, que você vai encontrar-se sempre sentindo que algo mais poderia ter sido feito "melhor", então você precisa encontrar um equilíbrio entre fazer um trabalho profissional e gold-plating.
  • Aceite que o limpador que você tenta manter seu código, menos inchado será o seu código.
por 17.04.2012 / 04:54
fonte
22

Acho que muitos dos problemas que você está enfrentando podem ser rastreados até uma causa simples. A maioria dos desenvolvedores não trata o código da GUI como código 'real'. Eu não tenho nenhuma evidência ou estatística aqui, apenas a minha intuição.

Talvez eles achem que é 'apenas apresentação' e não é importante. ' Não há lógica de negócios lá ', eles dizem, ' por que teste de unidade '? Eles riem quando você menciona orientação a objetos e escreve código limpo. Eles nem sequer tentam fazer as coisas melhor. Não há estrutura para começar, eles apenas dão um código e deixam apodrecer enquanto outros adicionam seu próprio toque ao longo do tempo. Uma bela bagunça, código de graffiti.

O código GUI tem seus desafios únicos, portanto, deve ser tratado de maneira diferente e com respeito. Ele precisa de amor e desenvolvedores que querem escrevê-lo. Os que irão mantê-lo fino e dar boa estrutura e padrões corretos.

    
por 17.04.2012 / 03:29
fonte
7

Por algum motivo, o código GUI cria um ponto cego nos desenvolvedores sobre a separação de interesses. Talvez seja porque todos os tutoriais colocam tudo em uma classe. Talvez seja porque a representação física faz as coisas parecerem mais ligadas do que elas. Talvez seja porque as aulas são construídas lentamente para que as pessoas não reconheçam que precisam de refatoração, como o proverbial sapo sendo fervido ao aumentar lentamente o calor.

Qualquer que seja o motivo, a solução é tornar suas aulas muito menores. Eu faço isso continuamente me perguntando se é possível colocar o que estou digitando em uma classe separada. Se é possível colocar em outra classe, e eu posso pensar em um nome razoável e simples para essa classe, então eu faço isso.

    
por 17.04.2012 / 19:45
fonte
5

Você pode querer dar uma olhada no padrão Model View Presenter / Passive View. Ray Ryan deu uma boa palestra sobre uma OI do Google sobre as melhores práticas de arquitetura para o GWT.

link

É fácil abstrair as ideias para outros frameworks e idiomas. O principal benefício do MVP (na minha opinião) é a capacidade de teste unitário. E você só tem isso, se o seu código não está inchado e não espaguete (a julgar pela sua pergunta, é isso que você quer). Ele funciona introduzindo uma camada lógica de exibição chamada apresentador. A visão atual é desacoplada desta através de uma interface (e, portanto, pode ser facilmente ridicularizada em testes de unidade). Agora, como sua camada de lógica de visualização (o apresentador) é liberada dos componentes internos da estrutura da GUI concreta, você pode organizá-la como um código normal e não está vinculado a Hierarquia de herança de Swings. Idealmente, você poderia alternar as implementações de GUI em diferentes estruturas, desde que elas estejam em conformidade com a mesma interface.

    
por 17.04.2012 / 12:29
fonte
5

Minha resposta consiste em quatro partes: estrutura, simplicidade, teste e sintaxe.

Os três primeiros são realmente difíceis de fazer!

Estrutura significa prestar muita atenção ao uso da menor quantidade de código e da quantidade máxima de estruturas, bibliotecas, etc.

Simplicidade significa manter as coisas simples desde o design inicial até a implementação real. Manter a navegação simples, usando plugins simples, mantendo o layout bastante "simples", tudo vai ajudar aqui. Agora, eles podem ser "vendidos" para clientes / usuários que podem ver rapidamente as vantagens de páginas que funcionam em PCs, iPad, dispositivos móveis e outros dispositivos.

Testes significam incluir ferramentas de teste de navegador (webrat e capivara em mente com meu trabalho de trilhos) que captam problemas de navegadores diretos quando um código melhor pode ser projetado para lidar com eles no início ao contrário do "patch" frequente de código por desenvolvedores diferentes, como eles são "descobertos" por usuários de diferentes navegadores.

Sintaxe. É realmente útil usar um verificador de código / IDE / editor-plugin, etc. para seu HTML, CSS, Javascript, etc. A vantagem que os navegadores obtêm ao manipular HTML mal formado funciona contra você quando diferentes navegadores têm um desempenho diferente com Então, uma ferramenta que verifica o seu formato HTML é essencial. Ter um HTML bem formado é muito útil para se ter HTML puro, já que códigos ruins devem ter mais visibilidade.

    
por 17.04.2012 / 03:00
fonte
4

A solução que encontrei é código declarativo. Usando apenas o código procedural é uma receita para o código GUI do espaguete. Claro, uma "maneira especial de pintar o widget" provavelmente continuará sendo o código. Mas isso é código isolado em uma classe. Manipuladores de eventos, atalhos de teclado, tamanhos de janela - todas essas coisas confusas são melhor declaradas.

    
por 17.04.2012 / 14:12
fonte
4

Há muitas ótimas respostas aqui.

Uma coisa que me ajudou a simplificar o código da GUI é garantir que a GUI tenha seu próprio modelo de dados.

Para dar um exemplo simples, se eu tiver uma GUI com 4 campos de entrada de texto, eu tenho uma classe de dados separada que mantém o conteúdo desses 4 campos de entrada de texto. GUIs mais complicadas requerem mais classes de dados.

Eu desenho uma GUI como uma visão de modelo. O modelo GUI é controlado pelo controlador de aplicativo do modelo de aplicativo - view - controller. A visualização do aplicativo é o modelo da GUI, em vez do próprio código da GUI.

    
por 18.04.2012 / 15:41
fonte
2

Aplicativos como processamento de texto, editores gráficos, etc. possuem interfaces complexas e seu código não pode ser simples. No entanto, para aplicativos de negócios, a GUI não precisa ser tão complexa, mas de alguma forma ainda é.

Algumas das chaves para simplificar a GUI são (a maioria se aplica ao .NET):

  1. Lute por um design mais simples sempre que possível. Evite comportamentos sofisticados, se não forem exigidos pela empresa.

  2. Use um bom provedor de controles.

  3. Não crie funcionalidade de controle personalizado no próprio código do cliente. Em vez disso, crie controles de usuário que estendam o controle original de modo que você possa refletir seus comportamentos específicos nos controles, e não no código do formulário / página de uso.

  4. Use uma estrutura (até mesmo doméstica) para lidar com internacionalização, gerenciamento de recursos, estilos etc., para que você não repita esse código em todas as interfaces de usuário.

  5. Empregue um componente (ou estrutura) para navegação.

  6. Crie diálogos padrão para erros, avisos, confirmação, etc.

por 17.04.2012 / 04:45
fonte
1

Aplique o Design Orientado a Objetos ao seu código e para o desenvolvimento da IU:

  1. Apresentação e modelo separados Use uma biblioteca / estrutura MV-whatever, ou escreva sua própria, para ajudar a separar a lógica de visualização / controlador do modelo de dados. Toda a comunicação com o backend deve ser feita dentro do modelo, e o estado do modelo deve estar sempre em sincronia com o backend.
  2. Desacoplamento Se o objeto A sabe sobre o objeto B, então A pode chamar métodos em B, mas B não deve saber sobre A. Em vez disso, A pode ouvir eventos de B. Ele garante que não haja dependência circular. Se o seu aplicativo tiver muitos eventos entre os componentes, crie um EventBus ou aproveite uma estrutura orientada a eventos, como o Twitter Flight.
  3. Render parcial ou total Se a sua visualização for uma tabela ou uma lista de itens, você poderá ser tentado a criar métodos como "adicionar", "remover" para inserir / excluir um item na / da coleção. Seu código pode facilmente inchar quando você tem que suportar classificação e paginação. Então, meu conselho é: simplesmente re-renderizar toda a visão mesmo quando houver uma mudança parcial. O que sobre o desempenho? Bem, se sua coleção é grande, então você deve fazer paginação de qualquer maneira. Desenvolvedor da Web: certifique-se de que seus manipuladores de eventos sejam delegados ao elemento-raiz da visualização, o que não é alterado.
  4. Ver modelo Quando o estado da sua visualização se torna demasiado complicado de manter, por exemplo, uma Vista de tabela tem de acompanhar os dados da linha, dados da coluna, ordem de classificação, linhas atualmente selecionadas (se suportar multi-check ), etc, você provavelmente deve criar um objeto ViewModel para esses estados. Seu objeto View deve chamar setters no ViewModel se algo mudar na interface do usuário (por exemplo: o usuário verifica uma linha); e deve responder ao evento de mudança do ViewModel, atualizando a interface do usuário. Normalmente, você deve evitar a atualização da interface do usuário se o evento de alteração for acionado pela interface do usuário.

Aqui está um aplicativo pequeno, mas não trivial, para ajudar a ilustrar alguns dos meus pontos. Você pode encontrar o diagrama de interação de código e visualização / modelo aqui: link

    
por 04.03.2013 / 00:27
fonte
0

Você quer dar uma olhada no conceito de "ligação de dados" . Essa é uma maneira de conectar elementos da interface do usuário para abstrair elementos de modelo de maneira declarativa, de modo que os elementos do modelo sejam automaticamente sincronizados com o conteúdo da interface do usuário. Há muitos benefícios dessa abordagem, por exemplo, não ter que gravar manipuladores de eventos para sincronizar dados.

Há suporte a ligação de dados para muitas estruturas de interface do usuário, por exemplo, .NET e Eclipse / JFace .

    
por 04.03.2013 / 08:20
fonte