Blocos personalizados: guia de estilo

Ao longo dos anos, a equipe da Blockly e da Blockly Games aprendeu muitas lições que se aplicam a quem está desenvolvendo novos blocos. A seguir estão uma coleção de erros que cometemos ou erros comumente cometidos por outras pessoas.

Estas são lições gerais que aprendemos usando o estilo visual do Blockly e podem não se aplicar a todos os casos de uso ou designs. Existem outras soluções. Essa também não é uma lista completa dos problemas que os usuários podem encontrar e como evitá-los. Cada caso é um pouco diferente e tem suas próprias vantagens e desvantagens.

1. Condicionais x loops

Os blocos mais difíceis para novos usuários são condicionais e loops. Muitos ambientes baseados em blocos agrupam esses dois blocos na mesma categoria "Controles", com os dois blocos com a mesma forma e a mesma cor. Isso geralmente leva à frustração de novos usuários, porque eles confundem os dois blocos. O Blockly recomenda mover condicionais e loops para categorias separadas de "Lógica" e "Loops", cada uma com uma cor diferente. Isso deixa claro que essas são ideias distintas que se comportam de maneira diferente, apesar de terem formas semelhantes.

Recomendação: mantenha condicionais e loops separados.

2. Listas baseadas em um

Os programadores novatos reagem mal quando encontram listas baseadas em zero pela primeira vez. Como resultado, o Blockly segue o exemplo de Lua e Lambda Moo com base em um da indexação de listas e strings.

Para usos mais avançados do Blockly, as listas baseadas em zero têm suporte para facilitar a transição para texto. Para públicos mais jovens ou novatos, a indexação baseada em um ainda é recomendada.

Recomendação: um é o primeiro número.

3. Entradas do usuário

Há três maneiras de receber um parâmetro do usuário. Um menu suspenso é o mais restritivo e é bom para tutoriais e exercícios simples. Um campo de entrada permite mais liberdade e é bom para atividades mais criativas. Uma entrada de bloco de valor (geralmente com um bloco de sombra) oferece a oportunidade de calcular um valor (por exemplo, um gerador aleatório) em vez de apenas ser um valor estático.

Recomendação: escolha um método de entrada apropriado para seus usuários.

4. Imagens de bloco ao vivo

A documentação dos blocos precisa incluir imagens dos blocos a que se refere. Fazer capturas de tela é fácil. No entanto, se houver 50 dessas imagens e o aplicativo for traduzido para 50 idiomas, de repente um deles vai manter 2.500 imagens estáticas. Depois, o esquema de cores muda e 2.500 imagens precisam ser atualizadas de novo.

Para sair desse pesadelo de manutenção, a Blockly Games substituiu todas as capturas de tela por instâncias do Blockly em execução no modo somente leitura. O resultado parece idêntico a uma imagem, mas é garantido que está atualizado. O modo somente leitura possibilitou a internacionalização.

Recomendação: se você oferecer suporte a mais de um idioma, use o modo somente leitura.

5. Seu outro lado esquerdo

O feedback de crianças nos EUA (embora curiosamente não de outros países) revelou uma grande confusão entre esquerda e direita. Isso foi resolvido com a adição de setas. Se a direção for relativa (a um avatar, por exemplo), o estilo da seta será importante. Uma → seta reta ou uma seta para virar ↱ é confusa quando o avatar está voltado para a direção oposta. O mais útil é uma seta circular ⟳, mesmo nos casos em que o ângulo virado é menor do que a seta indica.

Recomendação: complemente o texto com ícones Unicode sempre que possível.

6. Blocos de alto nível

Sempre que possível, uma abordagem de nível mais alto precisa ser tomada, mesmo que isso reduza o desempenho ou a flexibilidade de execução. Considere esta expressão do Apps Script:

SpreadsheetApp.getActiveSheet().getDataRange().getValues()

Em um mapeamento 1:1 que preserva todos os recursos possíveis, a expressão acima seria criada usando quatro blocos. No entanto, o Blockly visa um nível superior e forneceria um bloco que encapsula toda a expressão. O objetivo é otimizar para o caso dos 95%, mesmo que isso torne os 5% restantes mais difíceis. O objetivo do Blockly não é substituir idiomas baseados em texto, mas ajudar os usuários a superar a curva de aprendizado inicial para que possam usar idiomas baseados em texto.

Recomendação: não converta às cegas toda a API em blocos.

7. Valores de retorno opcionais

Muitas funções na programação baseada em texto executam uma ação e retornam um valor. Esse valor de retorno pode ou não ser usado. Um exemplo é a função pop() de uma pilha. O pop pode ser chamado para conseguir e remover o último elemento ou apenas para remover o último elemento com o valor de retorno sendo ignorado.

var last = stack.pop();  // Get and remove last element.
stack.pop();  // Just remove last element.

Linguagens baseadas em bloco geralmente não são boas para ignorar um valor de retorno. Um bloco de valor precisa se conectar a algo que aceite o valor. Há várias estratégias para lidar com esse problema.

a) Contornar o problema. A maioria das linguagens baseadas em blocos projeta a linguagem para evitar esses casos. Por exemplo, o Scratch não tem blocos que tenham efeitos colaterais e um valor de retorno.

b) Forneça dois blocos. Se o espaço na caixa de ferramentas não for um problema, uma solução simples é fornecer dois blocos de cada tipo, um com e outro sem valor de retorno. A desvantagem é que isso pode gerar uma caixa de ferramentas confusa, com muitos blocos quase idênticos.

c) Modificar um bloco. Use um menu suspenso, uma caixa de seleção ou outro controle que permita que o usuário escolha se há um valor de retorno ou não. Em seguida, o bloco muda de forma dependendo das opções. Um exemplo disso pode ser mostrado no bloco de acesso à lista do Blockly.

d) Use o valor. A primeira versão do App Inventor criou um bloco de barras especial que comia qualquer valor conectado. Os usuários não entendiam o conceito, e a segunda versão do App Inventor removeu o bloco de barra vertical e, em vez disso, recomendou que os usuários simplesmente atribuissem o valor a uma variável descartável.

Recomendação: cada estratégia tem prós e contras. Escolha a certa para seus usuários.

8. Blocos crescentes

Alguns blocos podem exigir um número variável de entradas. Por exemplo, um bloco de adição que soma um conjunto arbitrário de números, um bloco if/elseif/else com um conjunto arbitrário de cláusulas elseif ou um construtor de lista com um número arbitrário de elementos inicializados. Há várias estratégias, cada uma com suas vantagens e desvantagens.

a) A abordagem mais simples é fazer o usuário compor o bloco em blocos menores. Um exemplo seria adicionar três números aninhando dois blocos de adição de dois números. Outro exemplo seria fornecer apenas blocos if/else e fazer o usuário aninhá-los para criar condições elseif.

A vantagem dessa abordagem é a simplicidade inicial, tanto para o usuário quanto para o desenvolvedor. A desvantagem é que, nos casos em que há um grande número de aninhamentos, o código fica muito complicado para o usuário ler e manter.

b) Uma alternativa é expandir dinamicamente o bloco para que haja sempre uma entrada livre no final. Da mesma forma, o bloco exclui a última entrada se houver duas entradas livres no final. Essa é a abordagem usada pela primeira versão do App Inventor.

Os blocos que cresceram automaticamente não eram bem-vindos pelos usuários do App Inventor por alguns motivos. Primeiro, sempre houve uma entrada livre e o programa nunca foi "concluído". Em segundo lugar, inserir um elemento no meio da pilha era frustrante, porque envolvia desconectar todos os elementos abaixo da edição e reconectá-los. Dito isso, se a ordem não é importante e os usuários podem se sentir confortáveis com buracos no programa, essa é uma opção muito conveniente.

c) Para resolver o problema, alguns desenvolvedores adicionam botões +/- aos blocos que adicionam ou removem entradas manualmente. Abra Roberta usa dois desses botões para adicionar ou remover entradas da parte inferior. Outros desenvolvedores adicionam dois botões em cada linha para que a inserção e a exclusão do meio da pilha possam ser acomodadas. Outros adicionam dois botões para cima/para baixo em cada linha para que a reordenação da pilha possa ser acomodada.

Essa estratégia é um espectro de opções que variam de apenas dois botões por bloco a quatro botões por linha. Por um lado, há o perigo de os usuários não conseguirem realizar as ações necessárias. Na outra, a interface é tão cheia de botões que parece a ponte da nave espacial Enterprise.

d) A abordagem mais flexível é adicionar um balão mutador ao bloco. Isso é representado como um único botão que abre uma caixa de diálogo de configuração para o bloco. Os elementos podem ser adicionados, excluídos ou reorganizados à vontade.

A desvantagem dessa abordagem é que os mutadores não são intuitivos para usuários novatos. A introdução de mutadores requer alguma forma de instrução. Apps baseados em blocos voltados para crianças mais novas não devem usar mutadores. mas são inestimáveis para usuários avançados.

Recomendação: cada estratégia tem prós e contras. Escolha a certa para seus usuários.

9. Geração de código limpo

Os usuários avançados do Blockly podem ver o código gerado (JavaScript, Python, PHP, Lua, Dart etc.) e reconhecer imediatamente o programa que criaram. Isso significa que é necessário fazer um esforço extra para manter a leitura desse código gerado por máquina. Parênteses desnecessários, variáveis numéricas, espaços em branco e modelos de código detalhados atrapalham a produção de um código elegante. O código gerado precisa incluir comentários e estar em conformidade com os guias de estilo do Google.

Recomendação: tenha orgulho do seu código gerado. Mostre ao usuário.

10. Dependência de idioma

Um efeito colateral do desejo de usar código limpo é que o comportamento do Blockly é amplamente definido em termos de como a linguagem de compilação cruzada se comporta. A linguagem de saída mais comum é o JavaScript, mas se a Blockly fizer a compilação cruzada para uma linguagem diferente, nenhuma tentativa indevida será feita para preservar o comportamento exato nas duas linguagens. Por exemplo, em JavaScript, uma string vazia é falsa, enquanto em Lua ela é verdadeira. A definição de um único padrão de comportamento para a execução do código do Blockly, independentemente do idioma de destino resultaria em código insustentável, que parece ter sido gerado pelo compilador GWT.

Recomendação: o Blockly não é um idioma, então permite que o idioma existente afete o comportamento.