Escolar Documentos
Profissional Documentos
Cultura Documentos
Felinne Hermans
Prefácio de Jon Skeet
TRIPULAÇÃO
Machine Translated by Google
Longo prazo
Curto prazo
Filtro memória
memória
Em formação
1 2 3
4
Trabalhando
memória
Uma visão geral dos três processos cognitivos que este livro abrange: STM, LTM e memória de
trabalho. As setas rotuladas 1 representam informações que chegam ao seu cérebro. As setas rotuladas
2 indicam as informações que prosseguem em seu STM. A seta 3 representa as informações que viajam
do STM para a memória de trabalho, onde são combinadas com as informações do LTM (seta 4).
A memória de trabalho é onde a informação é processada enquanto você pensa sobre ela.
Machine Translated by Google
O cérebro do programador
Machine Translated by Google
Machine Translated by Google
O cérebro do programador
O QUE TODO PROGRAMADOR PRECISA SABER SOBRE COGNIÇÃO
FELIENNE HERMANS
TRIPULAÇÃO
ILHA ABRIGO
Machine Translated by Google
Para obter informações on-line e encomendar este e outros livros de Manning, visite
www.manning.com. A editora oferece descontos neste livro quando encomendado em quantidade.
Para mais informações por favor entre em contato
Nenhuma parte desta publicação pode ser reproduzida, armazenada em sistema de recuperação ou transmitida, de
qualquer forma ou por meios eletrônicos, mecânicos, fotocópias ou outros, sem autorização prévia por escrito do editor.
Muitas das designações usadas por fabricantes e vendedores para distinguir seus produtos são reivindicadas como marcas
registradas. Onde essas designações aparecem no livro, e a Manning Publications estava ciente de uma reivindicação de
marca registrada, as designações foram impressas em maiúsculas iniciais ou todas em maiúsculas.
Reconhecendo a importância de preservar o que foi escrito, é política da Manning ter os livros que publicamos impressos em
papel sem ácido, e envidamos nossos melhores esforços para isso.
Reconhecendo também nossa responsabilidade de conservar os recursos de nosso planeta, os livros de Manning são
impressos em papel que é pelo menos 15% reciclado e processado sem o uso de cloro elementar.
O autor e o editor fizeram todos os esforços para garantir que as informações contidas neste livro estivessem corretas no
momento da impressão. O autor e o editor não assumem e, por meio deste, se isentam de qualquer responsabilidade
perante qualquer parte por qualquer perda, dano ou interrupção causada por erros ou omissões, sejam esses erros ou
omissões resultantes de negligência, acidente ou qualquer outra causa, ou do uso de as informações aqui contidas.
ISBN 9781617298677
breve conteúdo
PARTE 1 NA MELHOR LEITURA DO CÓDIGO ................................................... .....1
v
Machine Translated by Google
vi BREVE CONTEÚDO
conteúdo
prefácio xiii
prefácio xv
agradecimentos xvii sobre
este livro xix
sobre o autor xxii
sobre a ilustração da capa xxiii
vii
Machine Translated by Google
viii CONTEÚDO
CONTEÚDO ix
x CONTEÚDO
CONTEÚDO XI
xii CONTEÚDO
epílogo 221
índice 223
Machine Translated by Google
prefácio
Passei muito da minha vida pensando em programação, e se você está lendo este livro,
provavelmente também. Eu não gastei quase tanto tempo pensando em pensar, no entanto. O
conceito de nossos processos de pensamento e como interagimos com o código como humanos
tem sido importante para mim, mas não houve nenhum estudo científico por trás disso. Deixe-
me dar três exemplos.
Sou o principal contribuidor de um projeto .NET chamado Noda Time, fornecendo um
conjunto alternativo de tipos de data e hora para os embutidos no .NET. Tem sido um ótimo
ambiente para mim dedicar tempo ao design da API, principalmente no que diz respeito à
nomenclatura. Tendo visto os problemas causados por nomes que fazem parecer que alteram
um valor existente, mas na verdade retornam um novo valor, tentei usar nomes que fazem o
código com erros parecer errado quando você o lê. Por exemplo, o tipo LocalDate tem um
método PlusDays em vez de AddDays. Espero que este código pareça errado para a maioria dos desenvolvedo
data.MaisDias(1);
amanhã = hoje.MaisDias(1);
data.AdicionarDias(1);
xiii
Machine Translated by Google
xiv PREFÁCIO
Parece que está apenas modificando a data e não é um bug, embora seja tão incorreto quanto o
primeiro exemplo.
O segundo exemplo também é do Noda Time, mas não é tão específico. Considerando que muitas
bibliotecas tentam (por boas razões) fazer todo o trabalho duro sem que os desenvolvedores tenham
que pensar muito, explicitamente queremos que os usuários do Noda Time pensem bastante em seu
código de data e hora antecipadamente. Tentamos forçar os usuários a pensar no que eles realmente
estão tentando alcançar, sem ambiguidade - e então tentamos tornar mais fácil expressar isso
claramente no código.
Finalmente, há um exemplo conceitual de quais valores as variáveis mantêm em Java e C# e o que
acontece quando você passa um argumento para um método. Parece que tenho tentado contrariar a
noção de que objetos são passados por referência em Java durante a maior parte da minha vida e,
quando faço as contas, provavelmente é esse o caso. Suspeito que estou tentando ajudar outros
desenvolvedores a ajustar seus modelos mentais há cerca de 25 anos.
Acontece que a forma como os programadores pensam tem sido importante para mim há muito
tempo, mas sem nenhuma ciência por trás disso, apenas suposições e experiências duramente
conquistadas. Este livro ajuda a mudar isso, embora não seja exatamente o início desse processo para mim.
Conheci Felienne Hermans na conferência NDC em Oslo em 2017, quando ela fez sua apresentação
“Programação é escrita é programação”. Minha reação no Twitter diz tudo: “Preciso de muito tempo
para entender tudo. Mas uau. Uau." Eu vi Feli enne fazer essa apresentação (evoluindo com o tempo, é
claro) pelo menos três vezes agora e tirei algo novo dela a cada vez. Finalmente, havia algumas
explicações cognitivas para as coisas que eu vinha tentando fazer — e também algumas surpresas que
me desafiavam a ajustar minha abordagem.
Reações alternadas de “Ah, isso faz sentido agora!” e “Ah, eu não tinha pensado nisso!” foram o
ritmo de fundo ao ler este livro. Além de algumas sugestões práticas imediatas, como o uso de
flashcards, suspeito que o impacto do livro será mais sutil. Talvez seja um pouco mais de deliberação
sobre quando colocar uma linha em branco no código. Talvez seja uma mudança nas tarefas que damos
aos novos membros da equipe, ou mesmo apenas uma mudança no tempo dessas tarefas. Talvez seja
como explicamos conceitos no Stack Overflow.
Seja qual for o impacto, Felienne forneceu um tesouro de ideias para pensar e processar na
memória de trabalho e passar para a memória de longo prazo – pensar sobre pensar é viciante!
—JON SKEET
ENGENHEIRO DE RELAÇÕES DE EQUIPE DESENVOLVEDOR , GOOGLE
Machine Translated by Google
prefácio
Quando comecei a ensinar crianças a programar há cerca de 10 anos, rapidamente percebi que
não tinha a menor ideia de como as pessoas usam seus cérebros para qualquer coisa, especialmente
para programação. Embora eu tenha aprendido muito sobre programação na universidade, nenhum
curso em minha formação em ciência da computação me preparou para pensar sobre programação.
xv
Machine Translated by Google
xvi PREFÁCIO
indicam que você tem suposições erradas sobre o código. Incompreensão do código existente
pode ser causado por uma falta de habilidades em como ler código.
O objetivo deste livro é primeiro ajudá-lo a entender como seu cérebro processa o código.
Compreender o que seu cérebro faz quando apresentado a novas informações ajudará
você seja um programador melhor porque programadores profissionais se deparam com novos
informações com frequência. Assim que soubermos como o código afeta o cérebro, falaremos sobre métodos para
melhorar suas habilidades de processamento de código.
Machine Translated by Google
agradecimentos
Percebo perfeitamente como sou tremendamente sortudo por poder concluir um livro sobre um tópico que
Ame. Há tantas coisas na minha vida que aconteceram exatamente da maneira certa no
momento exato, sem o qual minha vida teria sido muito diferente, e este
livro certamente não teria sido escrito. Dezenas de pequenos e grandes encontros
com pessoas incríveis contribuíram para este livro e para minha carreira. eu quero
citar alguns muito importantes.
Marlies Aldewereld primeiro me colocou no caminho da programação e do aprendizado de
idiomas. Marileen Smit me ensinou psicologia suficiente para escrever este livro. Greg Wilson
tornou o tema da educação em programação mainstream novamente. Peter Nabbe e Rob
Hoogerwood deu exemplos de classe mundial de como ser um grande professor. Stefan
Hanenberg me deu conselhos que moldaram a trajetória de minha pesquisa. Katja Mordaunt deu
início ao primeiro clube de leitura de códigos. O pensamento de Llewellyn Falco sobre a forma de koans
meu pensamento em aprender extensivamente. E Rico Huijbers é meu farol em qualquer tempestade.
Além dessas pessoas, é claro, preciso agradecer ao pessoal da Manning...
Marjan Bace, Mike Stephens, Tricia Louvar, Bert Bates, Mihaela Batinic´, Becky Rein hart,
Melissa Ice, Jennifer Houle, Paul Wells, Jerry Kuch, Rachel Head, Sébastien Porte bois, Candace
Gillholley, Chris Kaufmann, Matko Hrvatin, Ivan Martinovic ´, Branko
Latincic, e Andrej Hofšuster por aceitar essa vaga ideia de livro e transformá-la em
algo legível e sensato.
A todos os revisores: Adam Kaczmarek, Adriaan Beiertz, Alex Rios, Ariel Gamiño, Ben
McNamara, Bill Mitchell, Billy O'Callaghan, Bruno Sonnino, Charles Lam, Claudia
Maderthaner, Clifford Thurber, Daniela Zapata Riesco, Emanuele Origgi, George
xvii
Machine Translated by Google
xviii AGRADECIMENTOS
Onofrei , George Thomas, Gilberto Taccari, Haim Raman, Jaume Lopez, Joseph Pere nia,
Kent Spillner, Kimberly Winston-Jackson, Manuel Gonzalez, Marcin Sÿk, Mark Harris, Martin
Knudsen, Mike Hewitson, Mike Taylor, Orlando Méndez Morales, Pedro
Seromenho, Peter Morgan, Samantha Berk, Sebastian Felling, Sébastien Portebois,
Simon Tschöke, Stefano Ongarello, Thomas Overby Hansen, Tim van Deurzen, Tuomo
Kalliokoski, Unnikrishnan Kumar, Vasile Boris, Viktor Bek, Zachery Beyel e Zhijun
Liu, suas sugestões ajudaram a tornar este livro melhor.
Machine Translated by Google
Embora este livro apresente muitos tópicos da ciência cognitiva, é, em última análise, um livro
destinado especificamente para programadores. Sempre contextualizaremos o funcionamento do cérebro
com resultados de estudos sobre programação e linguagens de programação especificamente.
xix
Machine Translated by Google
Sua prática diária também é um lugar onde o conhecimento deve ser aplicado. Imagino que você
possa ler este livro por um período prolongado e aplicar as lições de um capítulo à sua prática de
programação antes de passar a ler mais capítulos:
ÿ O Capítulo 1 examina os três processos cognitivos que desempenham um papel na
programação e como cada um está associado ao seu próprio tipo de confusão. ÿ O Capítulo
2 discute como ler código rapidamente e ter uma noção de seu funcionamento. ÿ O Capítulo 3
ensina como aprender a sintaxe e os conceitos de programação melhor e mais facilmente. ÿ O
Capítulo 4 ajuda você a ler códigos complexos. ÿ O Capítulo 5 mostra técnicas que ajudam
você a alcançar uma compreensão mais profunda
código desconhecido.
Este livro contém muitos exemplos de código-fonte tanto em listagens numeradas quanto em linha
com o texto normal. Em ambos os casos, o código-fonte é formatado em uma fonte de largura fixa
como esta para separá-lo do texto comum. Às vezes, o código também está em negrito para realçar
o código que foi alterado em relação às etapas anteriores do capítulo, como quando um novo recurso
é adicionado a uma linha de código existente.
Em muitos casos, o código-fonte original foi reformatado; adicionamos quebras de linha e recuo
retrabalhado para acomodar o espaço de página disponível no livro. Em casos raros, mesmo isso não
foi suficiente, e as listagens incluem marcadores de continuação de linha (ÿ). Além disso, os
comentários no código-fonte geralmente são removidos das listagens quando o código é descrito no
texto. Anotações de código acompanham muitas das listagens, destacando conceitos importantes.
Sobre o autor
DR. FELIENNE HERMANS é professora associada da Universidade de Leiden, na Holanda
onde pesquisa educação em programação e linguagens de programação. Ela também
é professor-educador na academia de professores da Vrije Universiteit Amsterdam,
especializando-se em didática da ciência da computação, e leciona no Lyceum Kralingen high
escola em Roterdã.
Felienne também é a criadora da linguagem de programação Hedy para
programadores iniciantes e é apresentadora do podcast Software Engineering Radio, um dos maiores
podcasts sobre software na web.
xxii
Machine Translated by Google
xxiii
Machine Translated by Google
Machine Translated by Google
Parte 1
Ler código é uma parte essencial da programação, mas como um desenvolvedor profissional
oper, você pode não saber como. A leitura de código não é ensinada ou praticada com
frequência, e conhecer o código é confuso e muitas vezes um trabalho árduo. Os primeiros
capítulos deste livro ajudarão você a entender por que a leitura de código é tão difícil e o que
você pode fazer para melhorar nisso.
Machine Translated by Google
Machine Translated by Google
A confusão faz parte da programação. Quando você aprende uma nova linguagem de programação,
conceito, ou estrutura, as novas ideias podem assustá-lo. Ao ler desconhecido
código ou código que você escreveu há muito tempo, você pode não entender o que o
código faz ou porque foi escrito do jeito que foi. Sempre que você começa a trabalhar em um novo
domínio de negócios, novos termos e jargões podem se chocar em seu cérebro.
Não é um problema ficar confuso por um tempo, é claro, mas você não quer ser
confuso por mais tempo do que o necessário. Este capítulo ensina você a reconhecer e decodificar
sua confusão. Talvez você nunca tenha pensado sobre isso, mas existem maneiras diferentes
estar confuso. Não saber o significado de um conceito de domínio é um tipo diferente de
confusão do que tentar ler um algoritmo complicado passo a passo.
3
Machine Translated by Google
Diferentes tipos de confusão estão relacionados a diferentes tipos de processos cognitivos. Usando
vários exemplos de código, este capítulo detalhará três tipos diferentes de confusão e
explicar o que acontece em sua mente.
Ao final deste capítulo, você será capaz de reconhecer as diferentes maneiras que
código pode causar confusão e entender o processo cognitivo que está acontecendo em seu
cérebro em cada caso. Uma vez que você conheça os três tipos diferentes de confusão, e
os três processos cognitivos relacionados, os capítulos posteriores ensinarão como melhorar
esses processos cognitivos.
22222ÿn
8 IMPRIMIR B$
9 DEVOLUÇÃO
22222ÿn
Estou assumindo que a maioria dos leitores deste livro não está tão familiarizada com o APL e não saberá
o significado do operador T. Portanto, a confusão aqui reside na falta de conhecimento.
Com base no nome do método, você pode adivinhar a funcionalidade. No entanto, para aprofundar
entender o que o código faz, você precisaria navegar até a definição de
toBinaryString() em outro lugar no código e continue lendo lá. Então, o
problema aqui é a falta de informação. As informações sobre exatamente como o toBinary String()
funciona não estão prontamente disponíveis, mas precisam ser encontradas em outro lugar no
código.
Machine Translated by Google
A confusão aqui está relacionada à falta de poder de processamento. É muito difícil segurar todos os
valores intermediários das variáveis e as ações correspondentes em sua mente em
o mesmo tempo. Se você realmente deseja calcular mentalmente o que este programa faz, você
provavelmente usar uma caneta e papel para rabiscar alguns valores intermediários, ou até mesmo escrever
ao lado das linhas no snippet de código, conforme mostrado neste exemplo. Nestes três
programas, vimos que a confusão, embora sempre irritante e desconfortável, pode
tem três fontes diferentes. Em primeiro lugar, a confusão pode ser causada pela falta de conhecimento
a linguagem de programação, algoritmo ou domínio em mãos. Mas a confusão também pode ser
causado por não ter acesso total a todas as informações que você precisa para entender o código.
Especialmente porque o código hoje em dia geralmente usa várias bibliotecas, módulos e pacotes,
entender o código pode exigir uma navegação extensa na qual você precisa reunir novos
informações ao mesmo tempo que lembra o que você estava fazendo em primeiro lugar. Finalmente,
às vezes o código é mais complicado do que seu cérebro pode processar, e o que confunde
você é uma falta de poder de processamento.
Agora vamos mergulhar nos diferentes processos cognitivos que estão associados a cada um
esses três tipos de confusão.
você tem que pesquisar em muitos lugares diferentes, você pode esquecer algumas das coisas que você
já li. Finalmente, quando você precisa processar muitas informações, isso afeta
a memória de trabalho, que é onde seu pensamento acontece.
Aqui está um breve resumo de como os diferentes tipos de confusão estão relacionados ao
processos cognitivos diferentes:
Esses três processos cognitivos não estão apenas em jogo na leitura de código, mas em todas as
atividades cognitivas, incluindo (no contexto de programação) escrever código, projetar
a arquitetura de um sistema, ou escrever documentação.
primeiro vai para o seu STM, que tem um tamanho limitado. As estimativas diferem, mas a maioria dos cientistas
concordam que apenas alguns itens se encaixam no STM e certamente não mais do que uma dúzia.
Por exemplo, ao ler um programa, palavras-chave, nomes de variáveis e estruturas de dados usados são
armazenados temporariamente no STM.
No programa Java, o maior processo cognitivo em jogo é o STM. Você primeira linha de processo
1 da listagem 1.6, que ensina que o parâmetro de entrada n da função é um inteiro. Nesse ponto, você não tem
certeza do que a função fará, mas pode continuar
lendo enquanto também lembrando que n é um número. O conhecimento de que n é um inteiro é armazenado em
seu STM por um tempo. Você então continua na linha 2, onde toBinaryS tring() indica a você o que a função
retornará. Você pode não se lembrar disso
funcionar em um dia, ou mesmo em uma hora. Quando seu cérebro resolveu o problema em
lado—neste caso, entendendo a função—o STM é esvaziado.
A confusão pode ser causada aqui por não saber sobre o funcionamento interno de
toBinaryString().
Embora o STM desempenhe um grande papel na compreensão deste programa, o LTM é
envolvidos na leitura deste programa também. Na verdade, nosso LTM está envolvido em tudo o que fazemos.
Então, ao ler o programa Java, você também usa seu LTM.
Por exemplo, se você estiver familiarizado com Java, como presumo que a maioria dos leitores esteja, você sabe
que as palavras-chave public class e public static void main podem ser desconsideradas se você estiver
solicitado a explicar o que a função faz. É provável que você nem tenha notado que o
método é de fato chamado “mian” e não “main”.
O terceiro processo cognitivo que desempenha um papel na programação é a memória de trabalho. STM
e LTM são principalmente dispositivos de armazenamento. Eles detêm informações, seja por um curto período de tempo
depois de ler ou ouvir, no caso de STM, ou por muito tempo, no caso de LTM.
Machine Translated by Google
O pensamento real, no entanto, não acontece no LTM ou STM, mas na memória de trabalho. É aqui
que novos pensamentos, ideias e soluções são formados. Se você pensar no LTM como um disco
rígido e no STM como RAM, a memória de trabalho é melhor comparada ao processador do cérebro.
Ao ler o programa BASIC, você usa seu LTM—por exemplo, ao lembrar o significado de palavras-
chave como LET e EXIT. Além disso, você usa seu STM para armazenar algumas das informações
que encontra, como o fato de B$ começar como uma string vazia.
No entanto, seu cérebro faz muito mais enquanto você lê o programa BASIC. Você está
mentalmente tentando executar o código, para entender o que está acontecendo. Esse processo é
chamado de rastreamento – a compilação e execução mental do código. A parte do cérebro usada
para fazer o rastreamento e outras tarefas cognitivamente complexas é chamada de memória de trabalho.
Você pode compará-lo com o processador de um computador, que realiza cálculos.
Ao rastrear programas muito complexos, você pode sentir a necessidade de observar os valores
de variáveis, seja no código ou em uma tabela separada.
O fato de seu cérebro sentir a necessidade de armazenar informações externamente pode ser
um sinal de que sua memória de trabalho está muito cheia para processar mais informações.
Abordaremos essa sobrecarga de informações e como evitar que o cérebro se sobrecarregue no capítulo 4.
Até agora neste capítulo, focamos especificamente nos processos cognitivos que acontecem
quando você lê código. No entanto, esses três processos cognitivos também estão envolvidos em
muitas outras tarefas relacionadas à programação.
Machine Translated by Google
Longo prazo
Curto prazo
Filtro memória
memória
Em formação
1 2 3
4
Trabalhando
memória
Figura 1.2 Uma visão geral dos três processos cognitivos que este livro aborda: STM, LTM e
memória de trabalho. As setas rotuladas 1 representam informações que chegam ao seu cérebro.
As setas rotuladas 2 indicam as informações que prosseguem em seu STM. A seta 3 representa
as informações que viajam do STM para a memória de trabalho, onde são combinadas com as
informações do LTM (seta 4). A memória de trabalho é onde a informação é processada enquanto você pensa sobre ela.
EXERCÍCIO 1.1 Para praticar sua compreensão recém-adquirida dos três processos
cognitivos envolvidos na programação, preparei três programas.
Desta vez, porém, nenhuma explicação é dada sobre o que os trechos de código fazem. Você
terá, portanto, que ler os programas e decidir o que eles fazem por si mesmo. Os programas
são novamente escritos em APL, Java e BASIC, nessa ordem.
No entanto, cada um dos programas executa uma operação diferente, então você não pode
confie em sua compreensão do primeiro programa para apoiar a leitura do outro
programas.
Leia os programas com atenção e tente determinar o que eles fazem. Ao fazer
isso, reflita sobre os mecanismos que você usa. Use as perguntas da tabela a seguir para
orientar sua auto-análise.
Machine Translated by Google
Resumo
ÿ A confusão durante a codificação pode ser causada por três problemas: falta de conhecimento,
falta de informações de fácil acesso ou falta de poder de processamento no cérebro.
ÿ Três processos cognitivos estão envolvidos quando você lê ou escreve código. ÿ
O primeiro processo é a recuperação da informação do LTM, onde o significado
de palavras-chave é armazenado, por
exemplo. ÿ No segundo processo, as informações sobre o programa em questão são armazenadas em seu
STM, que pode conter temporariamente informações como o nome de um método ou
variável.
13
Machine Translated by Google
O foco deste capítulo é a leitura de código. A leitura de código é uma parte maior da vida profissional de um
programador do que você imagina. Pesquisas indicam que quase 60% dos
o tempo dos programadores é gasto entendendo ao invés de escrever código .
a rapidez com que você pode ler o código, sem perder a precisão, pode ajudá-lo a melhorar seu
habilidades de programação substancialmente.
No capítulo anterior você aprendeu que STM é onde as informações são armazenadas primeiro
quando você lê o código. Este capítulo começará ajudando você a entender por que é tão
difícil processar muitas informações armazenadas em código. Se você sabe o que acontece no seu
cérebro quando você está lendo código rapidamente, você pode monitorar mais facilmente sua compreensão. Em
seguida, mostrarei métodos para melhorar suas habilidades de código, por exemplo, praticando a leitura rápida de vários
trechos de código. Ao final do capítulo, você saberá por que
ler o código é tão difícil. Você também entenderá como ler código mais rapidamente e
esteja ciente das técnicas que você pode usar para continuar melhorando suas habilidades de leitura de código.
A leitura do código é feita por vários motivos: para adicionar um recurso, para encontrar um bug ou para
construir uma compreensão de um sistema maior. O que todas as situações em que você está lendo código têm em
comum é que você está procurando informações específicas presentes em
o código. Exemplos de informações que você pode estar procurando são o local certo para implementar um novo
recurso, o local de um determinado bug, onde você editou pela última vez
o código ou como um determinado método é implementado.
Ao melhorar sua capacidade de encontrar rapidamente informações relevantes, você pode reduzir o
número de vezes que você tem que voltar ao código. Um nível mais alto de habilidade na leitura de código
também pode reduzir a frequência com que você precisa navegar pelo código para encontrar
em formação. O tempo que você economiza pesquisando pelo código pode ser gasto na correção
bugs ou adicionando novos recursos para que você possa se tornar um programador mais eficaz.
No capítulo anterior, pedi que você lesse programas em três linguagens de programação diferentes para ter uma
noção das três partes diferentes do cérebro em funcionamento. Mergulhar
1
Consulte “Medindo a compreensão do programa: um estudo de campo em larga escala com profissionais” de Xin Xia et al.
(2017), https://ieeexplore.ieee.org/abstract/document/7997917.
Machine Translated by Google
para o papel do STM com mais profundidade, veja o seguinte programa Java que implementa o
algoritmo de ordenação por inserção. Você pode olhar para o programa por não mais de três
minutos. Use um relógio ou cronômetro para saber quando o tempo acabou. Após os três minutos,
cubra o código Java com uma folha de papel ou com a mão.
Mantendo o código coberto, tente reproduzi-lo da melhor maneira possível.
}
}
STM LTM
for (i = 1; i
Original int [ ] array int
Seu STM conseguiu armazenar algumas das informações que você acabou de ler. Seu LTM adicionado a
esse conhecimento de duas maneiras. Primeiro, você pôde contar com o conhecimento sintático de Java.
Talvez você tenha se lembrado de “um loop for sobre o array”, que seu LTM sabe que é equivalente a
for (int i = 0; i < array.length; i++). Talvez você também tenha se lembrado de “imprimir tudo
elementos do array”, que em Java é para (i = 0; i < array.length; i++)
{System.out.println(array[i])}.
Segundo, você pode confiar no fato de saber que o código está implementando a classificação por
inserção. Isso pode ter ajudado você a preencher algumas lacunas no programa, que você
não conseguia lembrar com precisão. Por exemplo, talvez você não se lembre de ler o código que dois
valores do array foram trocados, mas porque você está familiarizado
com o tipo de inserção, você sabia que isso deveria acontecer em algum lugar.
Quais informações são recuperadas do seu LTM, é claro, depende do que você tem
armazenado lá. Alguém menos experiente em Java provavelmente recuperará muito menos de seu
LTM, então a imagem pode parecer muito diferente para você. Observe também que nesta reprodução
estão presentes alguns comentários que não estavam no código Java original. Em experimentos que
realizei com programadores lembrando o código-fonte, descobri que
às vezes as pessoas adicionam comentários ao código reproduzido para facilitar a memorização. As
pessoas, por exemplo, primeiro escreveriam “imprimir a matriz” e depois preencheriam o
implementação. Você também fez isso?
Machine Translated by Google
Comentários são, é claro, frequentemente usados para descrever código que já está escrito, mas como
você pode ver neste exemplo, eles têm diversos usos e também podem ser usados como memória
ajuda para código futuro. Discutiremos o uso de comentários mais extensivamente mais adiante.
capítulos.
No capítulo 1, expliquei como LTM e STM colaboram quando você está lendo código.
Você acabou de experimentar essa colaboração com mais profundidade ao reproduzir alguns
partes do programa de classificação por inserção Java a partir de informações armazenadas em seu LTM.
Para aprofundar sua compreensão de quão extensivamente você confia em seu LTM quando
lendo e entendendo o código, vamos fazer outro exercício. É o mesmo exercício que
anterior: inspecione o programa por no máximo três minutos, depois cubra
o código e tente reproduzi-lo da melhor maneira possível sem espiar!
O código Java na próxima listagem deve ser usado como um exercício de memória. Estude por três
minutos e tente reproduzir com sua melhor habilidade.
Você não pode armazenar fisicamente todas as informações que estão presentes no código que você está
lendo em seu STM para processá-lo. Como você aprendeu no capítulo 1, o STM armazena
informações que você lê ou ouve por um breve período de tempo. E quando digo breve, eu
realmente significa breve! Pesquisas indicam que ele não pode reter informações por mais de 30 segundos.
Após 30 segundos, você terá que armazenar as informações em seu LTM, ou
estar perdido para sempre. Imagine que alguém está lendo um número de telefone para você pelo telefone,
e você não tem um lugar para anotá-lo. Se você não encontrar um lugar para anotá-lo
em breve (por exemplo, um cache físico), você não se lembrará do número de telefone.
O tempo durante o qual você pode se lembrar de informações não é a única limitação de
STM—a segunda limitação é o tamanho.
Como em um computador, em seu cérebro o armazenamento de longo prazo é muito maior do que o
armazenamento de curto prazo. Mas enquanto para a RAM você pode estar falando de alguns gigabytes de
memória, o armazenamento rápido do seu cérebro é muito menor. Seu STM tem apenas alguns slots disponíveis
para informação. George Miller, um dos mais influentes pesquisadores da ciência cognitiva do século anterior,
descreveu esse fenômeno em seu artigo de 1956 “The Magical Number Seven, Plus or Minus Two: Some
Limits on Our Capacity for Processing
Em formação."
Pesquisas mais recentes indicam que o STM é ainda menor, estimando uma capacidade de
entre duas e seis coisas. Esse limite vale para quase todas as pessoas, e os cientistas até agora
não encontraram maneiras confiáveis de aumentar o tamanho do STM. Não é um milagre que
humanos podem fazer qualquer coisa com no máximo 1 byte de memória?
Para lidar com esses limites, seu STM colabora com seu LTM para entender
o que você está lendo ou lembrando. A próxima seção detalha como o STM colabora com o LTM para superar
suas limitações de tamanho.
Especialista
Média Especialista Média
jogador jogador jogador jogador
Figura 2.3 O primeiro experimento de xadrez de De Groot no qual especialistas e jogadores de xadrez medianos foram
solicitados a se lembrar de uma configuração de xadrez. Jogadores experientes foram capazes de recordar mais peças do que jogadores médios.
jogadores experientes se tornaram especialistas em primeiro lugar; jogadores experientes podiam encaixar
mais peças de xadrez em suas memórias, o que lhes permitia jogar melhor também em partidas de xadrez.
No entanto, de Groot não estava totalmente convencido, então ele realizou outro experimento. Foi
semelhante ao primeiro experimento: novamente, jogadores médios e experientes foram solicitados a lembrar
as configurações de xadrez depois de inspecionar as configurações por um breve período de tempo.
A diferença estava nas próprias configurações de xadrez. Em vez de mostrar aos participantes uma configuração
de xadrez real como no experimento 1, ele mostrou a eles tabuleiros de xadrez com peças colocadas
aleatoriamente – e não apenas um pouco aleatórias; as peças de xadrez foram montadas em configurações
totalmente irreais. De Groot, então, comparou novamente o desempenho dos jogadores experientes com os
jogadores médios. Os resultados foram diferentes para o segundo experimento: ambos os tipos de jogadores
tiveram um desempenho igualmente ruim!
Os resultados do experimento 1 e do experimento 2 levaram de Groot a se aprofundar em como exatamente
os dois grupos de jogadores de xadrez se lembravam das configurações. Descobriu-se que, em ambos os
experimentos, os jogadores médios se lembraram principalmente das configurações, peça por peça.
Eles tentariam relembrar as configurações dizendo “uma torre em A7, um peão em B5, um rei em C8” e assim
por diante.
Alguns jogadores experientes também podem conectar as configurações ao seu próprio histórico de jogo
pessoal ou ao histórico de jogos que assistiram ou leram. Eles podem se lembrar de uma configuração como
“o jogo que joguei naquele sábado chuvoso de março contra Betsie, mas com roque à esquerda”. Essas
também são informações armazenadas no LTM. Lembrar uma configuração pensando em uma experiência
anterior também ocupa apenas alguns slots no STM.
Jogadores médios, no entanto, que estavam tentando se lembrar de todas as peças individuais, rapidamente
ficariam sem STM. Eles não podiam agrupar informações de maneira lógica como os especialistas faziam. Isso
explicava por que os jogadores medianos tiveram um desempenho pior do que os especialistas no experimento
1; quando o STM estava cheio, nenhum novo local podia ser armazenado.
De Groot chamou os grupos em que as pessoas combinavam pedaços de informação. Ele considerou a
“abertura siciliana”, por exemplo, um pedaço, que pode caber em um slot no STM. A teoria dos pedaços
também explica adequadamente por que ambos os tipos de jogadores tiveram desempenho igual no
experimento 2. Em uma configuração aleatória de xadrez, os jogadores experientes não podiam mais confiar
no repositório de tabuleiros em seu LTM para dividir rapidamente as peças em grupos maiores.
EXERCÍCIO 2.1 Talvez você esteja convencido pelos experimentos de de Groot, mas seria ainda
mais poderoso experimentar o chunking!
Machine Translated by Google
Olhe para esta frase por cinco segundos e tente lembrá-la da melhor maneira possível
posso:
EXERCÍCIO 2.2 Agora tente se lembrar desta frase examinando-a por cinco segundos:
EXERCÍCIO 2.3 Vamos tentar mais um. Esta frase novamente consiste em três palavras e
nove caracteres diferentes. Observe esta frase por cinco segundos e tente lembrá-la:
A terceira frase foi muito mais fácil do que as outras duas, certo? Você pode se
lembrar dessa frase com grande facilidade. Isso porque você pode dividir os
caracteres da terceira frase em palavras. Você pode então se lembrar de apenas três
pedaços: “gato”, “amores” e “bolo”. A quantidade de três elementos está bem abaixo
da capacidade do seu STM, então é possível lembrar toda essa frase facilmente,
enquanto nos dois primeiros exemplos o número de elementos provavelmente excede
os limites do seu STM.
CHUNKING EM CÓDIGO
Até agora neste capítulo, você viu que quanto mais informações você armazenou sobre um
tópico específico, mais fácil é dividir as informações em partes. Jogadores de xadrez experientes
têm muitas configurações de xadrez diferentes armazenadas em seu LTM, para que possam
se lembrar melhor de um tabuleiro. No exercício anterior, no qual você foi solicitado a lembrar
de caracteres, letras e palavras, você conseguiu lembrar as palavras com muito mais facilidade
do que caracteres desconhecidos. As palavras são mais fáceis de lembrar porque você pode
recuperar seu significado do seu LTM.
A descoberta de que é mais fácil lembrar quando você tem muito conhecimento sobre algo
em seu LTM também se aplica à programação. No restante deste capítulo, exploraremos os
resultados da pesquisa sobre programação e agrupamento especificamente. Depois disso,
vamos mergulhar em como praticar o código em pedaços e como escrever um código que seja
fácil de fragmentar.
Machine Translated by Google
Programas regulares
20 Especialistas
18
16
14 Intermediários
12
10
8
6 Iniciantes
4
2
1 2 3 4 5
Número de programas
Programas embaralhados
Figura 2.4 O número de linhas de código que
20
18
iniciantes, intermediários e especialistas podem
16 lembrar nos experimentos de McKeithen et al. A
14
12 imagem superior mostra o desempenho dos
10
8
Especialistas
participantes em programas regulares, onde os
Intermediários
6 Iniciantes especialistas claramente se destacam. A imagem
4
2 inferior mostra o desempenho em programas
1 2 3 4 5 embaralhados e sem sentido; aqui, o desempenho
Número de programas de especialistas, intermediários e iniciantes é muito semelhante.
2
“Organização do Conhecimento e Diferenças de Habilidades em Programadores de Computador” por Katherine B. McKeithen et al.
(1981), http://mng.bz/YA5a.
Machine Translated by Google
Mesmo uma pessoa inteligente capaz de programar bem em uma linguagem ou contexto de programação diferente
terá dificuldades com palavras-chave, estruturas e conceitos de domínio desconhecidos quando ainda não estiverem
armazenados em seu LTM. Você aprenderá estratégias para fazer isso
de forma rápida e confiável no próximo capítulo.
3
“A informação disponível em breves apresentações visuais” por George Sperling (1960), http://mng.bz/O1ao.
Machine Translated by Google
Em 50 milissegundos eles nunca seriam capazes de realmente ler as letras, porque o tempo de reação de um olho
humano é de cerca de 200 milissegundos (um quinto de segundo) – o que é rápido, mas não rápido o suficiente. No
entanto, neste experimento, os participantes conseguiram se lembrar de todas as letras em uma linha ou coluna
aleatória da grade em cerca de 75% dos casos. Como os participantes estavam lembrando de uma linha aleatória,
o fato de terem conseguido 75% das vezes significava que haviam armazenado muitas ou todas as 9 ou 12 letras
na grade, o que é mais do que caberia no STM.
Não era o caso que esses participantes tivessem excelentes memórias. Quando os participantes foram
solicitados a reproduzir todas as letras, eles se saíram muito pior do que quando foram solicitados a nomear um
conjunto específico de letras. Normalmente, os participantes conseguiam se lembrar de cerca de metade das letras.
Essa segunda descoberta se encaixa com o que já sabemos sobre o STM, que também era conhecido quando
Sperling conduziu seus experimentos: ele pode armazenar até cerca de seis itens. O fato de muitos dos participantes
serem capazes de se lembrar de todas as letras quando solicitados apenas três ou quatro de cada vez, no entanto,
mostrou que a grade de letras em sua totalidade estava armazenada em algum lugar, e que “algum lugar” precisava
ser diferente de o STM com sua capacidade limitada. Sperling chamou a localização da informação proveniente do
sentido visual de memória icônica. Como mostrado em seus experimentos, nem todas as informações armazenadas
na memória icônica podem ser processadas no STM.
Você pode usar esse conhecimento para tentar ler o código com mais eficiência, primeiro olhando para ele por
um breve período de tempo e depois refletindo sobre o que viu. Este exercício de “código de relance” ajudará você
a obter uma imagem inicial do código.
EXERCÍCIO 2.4
Selecione um trecho de código que lhe seja familiar. Pode ser algo da sua própria base de código ou
um pequeno e simples código do GitHub. Não importa muito qual código você seleciona ou a
linguagem de programação usada. Algo do tamanho de meia página funciona melhor e, se possível,
é incentivada a impressão em papel.
Observe o código por alguns segundos, remova-o de vista e tente responder às seguintes perguntas:
Quando os participantes conseguiram repetir todas as 21 palavras-chave de forma confiável, os pesquisadores pediram
para listar todas as palavras-chave. Se você também os memorizou, tente anotá-los
agora, para que você possa se comparar com as pessoas no estudo.
A partir da ordem em que os participantes repetiram as palavras-chave, McKeithen et al.
foram capazes de obter informações sobre as conexões que criaram entre as palavras-chave.
Os resultados do estudo mostraram que os iniciantes agruparam as palavras-chave ALGOL de maneiras diferentes dos
especialistas. Iniciantes, por exemplo, costumavam usar frases como memória
ajudas, como “VERDADEIRO É REAL E FALSO”. Os especialistas, no entanto, usariam seus conhecimentos prévios
conhecimento de programação para agrupar as palavras-chave; por exemplo, combinaram
VERDADEIRO e FALSO, e SE, ENTÃO e ENTÃO. Este estudo confirmou novamente que os especialistas
pensar sobre o código de uma maneira diferente do que os iniciantes fazem.
Depois de ter feito o exercício anterior de lembrar e fragmentar várias vezes, você
comece a desenvolver uma intuição sobre quais tipos de código são fragmentáveis. Do de Groot
estudo com enxadristas, sabemos que situações habituais ou previsíveis, como
aberturas famosas, facilidade de fragmentação. Então, se seu objetivo é criar tabuleiros de xadrez fáceis
4
Os resultados deste experimento foram relatados no mesmo artigo, “Knowledge Organization and Skill
Diferences in Computer Programmers” por Katherine B. McKeithen et al.
Machine Translated by Google
para lembrar, use uma abertura bem conhecida. Mas o que podemos fazer no código para facilitar
ler? Vários pesquisadores estudaram maneiras de escrever código de tal forma que seja mais fácil de fragmentar e,
portanto, mais fácil de processar.
Se você quiser escrever um código que seja fácil de fragmentar, use padrões de design. Aqueles
foram as descobertas de Walter Tichy, professor de ciência da computação na Karlsruhe
Instituto de Tecnologia da Alemanha. Tichy investigou o chunking no código, mas isso
aconteceu um pouco por acaso. Ele não estava estudando habilidades de memória de código, mas em vez disso
estava analisando padrões de design. Ele estava especialmente interessado na questão da
se os padrões de projeto ajudam os programadores quando eles estão mantendo o código (adicionando
novos recursos ou correção de bugs).
Tichy começou pequeno, com um grupo de alunos, testando se dar a eles informações sobre padrões de projeto
os ajudava a entender o código.5 Ele dividiu os alunos em
dois grupos: um grupo recebeu código com documentação, enquanto o outro grupo
recebeu o mesmo código, mas sem documentação sobre padrões de projeto. Os resultados do
O estudo de Tichy mostrou que ter um padrão de design presente no código era mais útil para realizar tarefas de
manutenção quando os programadores sabiam que o padrão
estava presente no código.
Tichy também realizou um estudo semelhante em profissionais.6 Nesse caso, os participantes
começou com o exercício de modificação de código, depois fez um curso sobre
Padrões de design. Após o curso, eles modificaram novamente o código, com ou sem design
padronizar. Os resultados deste estudo sobre os profissionais são apresentados na figura 2.7. Deveria ser
observou que os participantes deste estudo mantiveram código diferente após o teste, então
eles não estavam familiarizados com o código usado após o curso. O estudo usou duas bases de código: os
participantes que viram a base de código A antes do curso usaram a base de código B depois,
e vice versa.
Esta figura apresenta os resultados do estudo de Tichy com gráficos box-and - whisker.7
mostra que depois de fazer um curso sobre padrões de projeto (como visto nos gráficos à direita
rotulado como “pós-teste”), o tempo que os participantes precisaram para manter o código foi menor para o
código com padrões, mas não para o código sem padrões. Os resultados deste estudo indicam que adquirir
conhecimento sobre padrões de projeto, que provavelmente irá melhorar
sua capacidade de fragmentação, ajuda a processar o código mais rapidamente. Você também pode ver nos gráficos
que há uma diferença de efeito para diferentes padrões de projeto: a diminuição no tempo é
maior para o padrão observador do que para o padrão decorador.
5
Consulte "Dois experimentos controlados avaliando a utilidade das informações do padrão de projeto durante o programa
Manutenção”, de Lutz Prechelt, Barbara Unger e Walter Tichy (1998), http://mng.bz/YA9K.
6
“Um experimento controlado comparando a manutenibilidade de programas projetados com e sem design
Patterns—A Replication in a Real Programming Environment,” por Marek Vokácÿ, Walter Tichy, Dag IK
Sjøberg, Erik Arisholm, Magne Aldrin (2004), http://mng.bz/G6oR.
7
A caixa representa metade dos dados, sendo a linha superior a mediana do terceiro quartil e a linha inferior a
mediana do primeiro quartil. A linha dentro da caixa representa a mediana e os “bigodes” mostram os valores mínimo e
máximo.
Machine Translated by Google
Decorador
Pré-teste Pós-teste
minutos
“Sem padrões” indica o tempo que
40
os participantes levaram para
20 modificar o código original sem
Sem Com Sem Com
padrões padrões padrões padrões padrões de projeto, enquanto “com
padrões” mostra o tempo que eles
levaram para o código que incluía
ESCREVA COMENTÁRIOS
Você deve escrever comentários ou deve codificar “documentar-se”? Essa é uma pergunta que
muitas vezes provoca debates entre os programadores. Pesquisadores estudaram essa questão também
e encontrei várias direções interessantes para mergulhar.
A pesquisa mostrou que quando o código contém comentários, os programadores levam
mais tempo para lê-lo. Você pode pensar que isso é uma coisa ruim - que os comentários o atrasam
baixo - mas, na verdade, isso é um sinal de que os comentários estão sendo lidos quando os programadores
leia o código. No mínimo, isso está mostrando que você não está adicionando comentários ao seu
código para nada. Martha Elizabeth Crosby, pesquisadora da Universidade do Havaí,
estudou como os programadores leem o código e qual o papel dos comentários em sua leitura
comportamento.8 O trabalho de Crosby mostra que os iniciantes focam muito mais nos comentários do que
programadores experientes fazem. A Parte 4 deste livro abordará o processo de integração
colegas juniores em sua equipe com mais profundidade, mas as descobertas de Crosby sugerem que
adicionar comentários ao código pode ser uma boa maneira de facilitar para novos programadores
para entender sua base de código.
Além de apoiar programadores iniciantes, os comentários também desempenham um papel na forma como
código de bloco de desenvolvedores. Dissertação de Quiyin Fan de 2010 na Universidade de Maryland,
“Os Efeitos de Beacons, Comentários e Tarefas no Processo de Compreensão do Programa
em Manutenção de Software”, mostrou que os desenvolvedores dependem muito de comentários
8
“Como lemos algoritmos? Um estudo de caso” por Martha E. Crosby e Jan Stelovsky, https://ieeexplore.ieee
.org/document/48797.
Machine Translated by Google
ao ler o código. Em particular, comentários de alto nível como “Esta função imprime um
dada árvore binária em ordem” pode ajudar os programadores a fragmentar pedaços maiores de código.
Comentários de baixo nível, por outro lado, como “Incrementar i (por um)” após uma linha que
lê i++;, pode criar um fardo no processo de fragmentação.
DEIXE SINALIZADORES
Uma última coisa que você pode fazer para facilitar o processo de fragmentação de código é incluir bea cons.
Beacons são partes de um programa que ajudam um programador a entender o que o
código faz. Você pode pensar em um beacon como uma linha de código, ou mesmo parte de uma linha de código,
que seu olho cai, o que faz você pensar: “Aha, agora eu vejo”.
Beacons normalmente indicam que um pedaço de código contém certas estruturas de dados,
algoritmos ou abordagens. Como exemplo de beacons, considere o seguinte Python
código que percorre uma árvore binária.
classe Nó:
def __init__(self, key): self.left = Nenhum
self.right = Nenhum
self.val = chave
Este código Python contém vários beacons dos quais um leitor pode deduzir que este
O código usa uma árvore binária como estrutura de dados:
ÿ Conteúdo da string no código que diz respeito às árvores (“O conteúdo da árvore é”)
Machine Translated by Google
Além de nomes, construções de programação específicas como uma troca ou inicialização para
uma lista vazia também pode ser beacons.
Os beacons têm uma relação com os pedaços, mas a maioria dos pesquisadores os vê como conceitos diferentes.
Beacons são geralmente vistos como partes menores do código do que pedaços. Crosby,
cujo trabalho sobre o uso de comentários eu resumi anteriormente, também estudou o papel dos bea cons. Ela
descobriu que programadores experientes, mas não novatos, fazem uso intenso de bea cons quando estão lendo e
compreendendo código.9 O exercício a seguir irá
ajudá-lo a reconhecer beacons úteis.
EXERCÍCIO 2.5
Selecionar os tipos certos de beacons para usar no código pode exigir alguma prática. Usa isto
exercício para praticar deliberadamente o uso de beacons no código.
9
Consulte “Os papéis que os sinalizadores desempenham na compreensão para programadores iniciantes e experientes” de Martha E. Crosby,
Jean Scholtz e Susan Wiedenbeck, http://mng.bz/zGKX.
Machine Translated by Google
Passo 2: Estude o
código Estude o código selecionado e tente resumir o significado do código.
Etapa 3: observe ativamente os beacons que
você usa Sempre que tiver um momento “aha” em que você se aproxima um pouco da funcionalidade de
o código, pare e anote o que o levou a essa conclusão. Isso poderia
ser um comentário, um nome de variável, um nome de método ou um valor intermediário - todos
aqueles podem ser balizas.
Passo 4: Reflita
Quando você tiver uma compreensão completa do código e uma lista de beacons, reflita
usando estas perguntas:
EXERCÍCIO 2.6
Este exercício ajuda você a reconhecer com quais conceitos você está familiarizado e quais conceitos são mais difíceis
para você, testando sua memória de leitura de código. O subjacente
suposição é que, como mostrado pelos experimentos descritos, conceitos familiares são mais fáceis
lembrar. O que você pode lembrar é o que você sabe, então esses exercícios podem ser
usado para (auto)diagnóstico do seu conhecimento de código.
Passo 2: Estude o
código Estude um pouco o código selecionado, por no máximo dois minutos. Defina um temporizador para que você
não perca a noção do tempo. Depois que o cronômetro acabar, feche ou cubra o código.
Passo 4: Reflita
Quando tiver certeza de ter reproduzido todo o código possível, abra o código original e compare. Reflita usando estas
perguntas:
Resumo ÿ O
STM tem capacidade de dois a seis elementos. ÿ Para
superar a limitação de tamanho, seu STM colabora com seu LTM quando
você se lembra das informações.
ÿ Quando você lê uma nova informação, seu cérebro tenta dividir a informação em partes
reconhecíveis chamadas pedaços. ÿ Quando você não tem conhecimento suficiente em
seu LTM, precisa confiar na leitura de código de baixo nível, como letras e palavras-chave. Ao
fazer isso, você rapidamente ficará sem espaço em seu STM.
ÿ Quando seu LTM armazena informações relevantes suficientes, você pode se lembrar de
conceitos abstratos como “um loop for em Java” ou “classificação de seleção em Python”
em vez do código em um nível inferior, ocupando menos espaço em seu STM. ÿ Quando
você lê o código, ele é armazenado primeiro na memória icônica. Apenas um bit do código é
enviado posteriormente para o STM. ÿ Lembrar o código pode ser usado como uma
ferramenta para (auto)diagnóstico do seu conhecimento de codificação. Como você pode
lembrar mais facilmente o que já sabe, as partes do código que você lembra podem revelar
os padrões de design, construções de programação e conceitos de domínio com os quais
você está mais familiarizado. ÿ O código pode conter características que facilitam o
processamento, como design
padrões, comentários e sinalizadores explícitos.
Machine Translated by Google
Como aprender
programação
sintaxe rapidamente
Este capítulo se concentra em como as pessoas aprendem a se lembrar das coisas. Este capítulo irá
ajudá-lo a entender por que certos conhecimentos permanecem, enquanto outros são esquecidos. Por
exemplo, em um ponto você provavelmente aprendeu que System.out.print()
33
Machine Translated by Google
é o método que imprime em Java. No entanto, você não tem todos os métodos do Java
memorizados. Tenho certeza de que você às vezes sentiu a necessidade de procurar uma
sintaxe específica. Por exemplo, você saberia se deve usar addDays(), addTimespan() ou
plusDays() para adicionar um dia a um DateTime?
Você pode não se importar muito em saber a sintaxe de cor – afinal, todos nós podemos
procurar informações online, certo? No entanto, como o capítulo anterior mostrou, o que você
já sabe influencia a eficiência com que você processa o código. Portanto, memorizar a sintaxe
de programação, os conceitos e as estruturas de dados o ajudarão a processar o código
mais rápido.
Este capítulo apresenta quatro técnicas importantes que o ajudarão a memorizar melhor
e mais facilmente os conceitos de programação. Isso fortalecerá seu armazenamento a longo
prazo de conceitos de programação, o que, por sua vez, permitirá melhor fragmentação e
leitura de código. Se você já teve dificuldade em lembrar a sintaxe do Flexbox em CSS, a
ordem dos parâmetros no método boxplot() do matplotlib ou a sintaxe de funções anônimas
em JavaScript, este capítulo o cobriu!
Mesmo que você seja um programador profissional, talvez precise pesquisar algumas das
sintaxes específicas para esses exercícios. Neste capítulo, exploraremos não apenas por que
é difícil lembrar a sintaxe correta, mas também como aprimorá-la. Primeiro, porém,
examinaremos mais profundamente por que é tão importante que você possa se lembrar do código.
Muitos programadores acreditam que se você não conhece um determinado trecho de
sintaxe, você pode simplesmente procurá-lo na internet e que, portanto, o conhecimento de
sintaxe não é tão importante. Há duas razões pelas quais “só olhando as coisas” pode não
ser uma ótima solução. A primeira razão foi abordada no capítulo 2: o que você já sabe afeta
em grande medida a eficiência com que você pode ler e entender o código. Quanto mais
conceitos, estruturas de dados e sintaxe você conhece, mais código você pode facilmente
fragmentar e, assim, lembrar e processar.
Chris Parnin, professor da North Carolina State University, estudou extensivamente o que acontece quando
os programadores são interrompidos no trabalho.1 Parnin registrou
10.000 sessões de programação por 85 programadores. Ele observou com que frequência os desenvolvedores
foram interrompidos por e-mails e colegas (o que foi muito!), mas ele também examinou
o que acontece depois de uma interrupção. Parnin determinou que as interrupções são, sem surpresa, bastante
perturbadoras para a produtividade. O estudo mostrou que normalmente leva
cerca de um quarto de hora para voltar a editar o código após uma interrupção. Quando
interrompido durante a edição de um método, os programadores puderam retomar seu trabalho
em menos de um minuto em apenas 10% dos casos.
Os resultados de Parnin mostraram que os programadores muitas vezes esqueciam informações vitais sobre o
código em que estavam trabalhando enquanto estavam longe do código. Você pode reconhecer
aquele “O que eu estava fazendo de novo?” sentimento depois de retornar ao código de uma pesquisa. Os
programadores do estudo de Parnin muitas vezes também precisavam fazer um esforço deliberado para reconstruir
o contexto. Por exemplo, eles podem navegar para vários locais na base de código para recuperar
detalhes antes de continuar o trabalho de programação.
Agora que você sabe por que lembrar de sintaxe é importante, vamos mergulhar em como
aprenda a sintaxe rapidamente.
1
Consulte “Estratégias de retomada para tarefas de programação interrompidas” por Chris Parnin e Spencer Rugaber
(2011), http://mng.bz/0rpl.
Machine Translated by Google
EXERCÍCIO 3.1 Pense nos 10 principais conceitos de programação que você sempre teve
dificuldade para lembrar.
Faça um conjunto de flashcards para cada um dos conceitos e tente usá-los. Você pode
também faça isso de forma colaborativa em um grupo ou equipe, onde você pode descobrir que
você não é o único que luta com certos conceitos.
determinado conceito. Isso é um sinal de que você ainda não conhece esse conceito de cor. Escrever
o conceito que você decidiu procurar em um lado de um cartão e o código que você encontrou
o outro lado.
Contagem
Frente Costas
Se você acertar uma carta várias vezes seguidas, você pode descartá-la. Claro, se você se encontrar
com dificuldades novamente, você sempre pode colocar o cartão de volta. Se você usar um aplicativo
para fazer flashcards, os aplicativos normalmente diminuirão o conjunto para você, não mostrando
cartões que você já conhece bem.
lembrou
70
60
50
40
30
20
10
Figura 3.2 Um gráfico que ilustra quanta
informação você lembra depois de ser exposto
a ela. Após dois dias, apenas 25% do
10 20 30 40 50 horas conhecimento permanece em seu LTM.
Machine Translated by Google
Comparamos o LTM a um disco rígido, mas o armazenamento no cérebro na verdade não funciona
hierarquicamente, da mesma forma que os arquivos são organizados em pastas com subpastas. Como
mostra a Figura 3.3, as memórias no cérebro são organizadas mais em uma estrutura de rede.
Isso porque os fatos estão ligados a um grande número de outros fatos. A consciência dessas conexões
entre diferentes fatos e memórias é importante para entender por que as pessoas esquecem de informações,
que é o tema que vamos discutir a seguir.
lar Pinguim
Coisas Águia
Pomba
pássaros.txt Jardim zoológico
casas.txt Cabana
Pardal
Figura 3.3 Duas formas de organização de dados: à esquerda, um sistema de arquivos hierárquico;
à direita, memórias organizadas em rede
A CURVA DO ESQUECIMENTO
Hermann Ebbinghaus foi um filósofo e psicólogo alemão que se interessou em estudar as capacidades da
mente humana na década de 1870. Naquela época, a ideia de medir as capacidades mentais das pessoas
era relativamente desconhecida.
Ebbinghaus queria entender os limites da memória humana usando sua própria memória como teste.
Ele queria empurrar-se o mais longe possível. Ele percebeu que tentar lembrar palavras ou conceitos
conhecidos não era um teste verdadeiro porque as memórias são armazenadas com associações ou
relacionamentos entre si. Por exemplo, se você tentar lembrar a sintaxe de uma compreensão de lista,
saber a sintaxe de um loop for pode ajudá-lo.
Para criar um meio mais justo de avaliação, Ebbinghaus construiu um grande conjunto de palavras
curtas e sem sentido, como wix, maf, kel e jos. Ele então conduziu um extenso conjunto de experimentos,
usando a si mesmo como cobaia. Durante anos, ele estudou listas dessas palavras sem sentido lendo-as
em voz alta, com a ajuda de um metrônomo para acompanhar o ritmo, e registrou quanta prática era
necessária para ser capaz de lembrar cada lista perfeitamente.
Machine Translated by Google
Em 1880, depois de uma década, ele estimou que havia passado quase 1.000 horas praticando e podia
recitar 150 por minuto. Ao testar-se em diferentes intervalos, Ebbing haus foi capaz de estimar o intervalo de
tempo de sua memória. Ele resumiu suas descobertas em
sua publicação de 1885 Über das Gedächtnis (Memória: Uma Contribuição para a Psicologia Experimental).
Seu livro continha a fórmula para o esquecimento mostrada na figura 3.4, que se encontra em
a base do conceito da curva do esquecimento.
1,84
b = 100 x
Figura 3.4 Fórmula de Ebbinghaus
(log10t) 1,25 + 1,84 para estimar quanto tempo uma memória durará
Um estudo recente do professor holandês Jaap Murre, da Universidade de Amsterdã, confirmou que a fórmula
de Ebbinghaus está amplamente correta.2
2
Veja “Replicação e Análise da Curva de Esquecimento de Ebbinghaus” de Jaap Murre (2015), https://journals.plos
.org/plosone/article?id=10.1371/journal.pone.0120644.
3
Ver “Manutenção do Vocabulário de Língua Estrangeira e o Efeito de Espaçamento” por Harry Bahrick et al. (1993),
www.gwern.net/docs/spacedrepetition/1993-bahrick.pdf.
Machine Translated by Google
grupo estudado em um intervalo de duas semanas. A evocação diminuiu nos anos seguintes,
mas permaneceu consistentemente maior para o mesmo grupo de palavras que eles estudaram
por mais tempo.
Em resumo, você se lembra por mais tempo se estudar por um período mais longo. Isso
não significa que você precise passar mais tempo estudando; significa que você deve estudar
em intervalos mais espaçados. Revisitar seu conjunto de flashcards uma vez por mês será
suficiente para ajudar sua memória a longo prazo, e também é relativamente factível! Isso, é
claro, contrasta fortemente com a educação formal, onde tentamos acumular todo o
conhecimento em um semestre, ou com bootcamps que buscam educar as pessoas em três
meses. O conhecimento aprendido dessa maneira só é válido se você continuar repetindo o
que aprendeu com frequência depois.
DICA A maior lição desta seção é que a melhor maneira que a ciência conhece
para evitar o esquecimento é praticar regularmente. Cada repetição fortalece sua
memória. Após várias repetições espaçadas por um longo período, o conhecimento
deve permanecer em seu LTM para sempre. Se você já se perguntou por que
esqueceu muito do que aprendeu na faculdade, é por isso! A menos que você
revisite o conhecimento, ou seja forçado a pensar sobre ele, você perderá memórias.
Você deve ter notado antes que eu não lhe disse para simplesmente ler os dois lados dos
flashcards. Em vez disso, pedi que você lesse o lado que contém o prompt, que é o que faz
você lembrar da sintaxe.
Isso porque a pesquisa mostrou que tentar ativamente lembrar torna as memórias mais
fortes. Mesmo que você não saiba a resposta completa, as memórias são mais fáceis de
encontrar quando você já tentou encontrá-las antes. O restante deste capítulo explorará essa
descoberta com mais profundidade e ajudará você a aplicá-la ao aprendizado de programação.
FORÇA DE ARMAZENAMENTO
A força de armazenamento indica quão bem algo está armazenado no LTM. Quanto mais você estuda
algo, mais forte é a memória dele, até que se torna praticamente impossível esquecê-lo. Você pode
imaginar esquecer que 4 vezes 3 é 12? No entanto, nem todas as informações que você armazenou em
seu cérebro são tão fáceis de acessar quanto as tabelas de multiplicação.
FORÇA DE RECUPERAÇÃO
A força de recuperação indica quão fácil é lembrar de algo. Tenho certeza de que você já teve aquela
sensação de ter certeza de que sabe alguma coisa (um nome, uma música, um número de telefone, a
sintaxe da função filter() em JavaScript), mas não consegue se lembrar; a resposta está na ponta da
língua; você simplesmente não pode alcançá-lo. Essa é uma informação para a qual a capacidade de
armazenamento é alta — quando você finalmente se lembra dela, não consegue acreditar que não
conseguiu lembrar dela —, mas a capacidade de recuperação é baixa.
É geralmente aceito que a força de armazenamento só pode aumentar – pesquisas recentes indicam
que as pessoas nunca esquecem realmente as memórias4 – e que é a força de recuperação das memórias
que decai com o passar dos anos. Quando você estuda repetidamente alguma informação, você fortalece
a capacidade de armazenamento desse fato. Quando você tenta se lembrar de um fato que você sabe
que sabe, sem estudo extra, você melhora a capacidade de força de recuperação.
Todas essas opções parecem bastante semelhantes, e mesmo para um programador C++ experiente
pode ser complicado lembrar a versão correta, apesar de ter visto esse código várias vezes. Quando lhe
disserem a resposta certa, parecerá que você sabia o tempo todo: “Claro que é rit = s.rbegin(); rit !=
s.rend(); rit++!”
Portanto, o problema aqui não é a força com que o conhecimento é armazenado em seu LTM (a força
de armazenamento), mas a facilidade com que você pode encontrá-lo (a força de recuperação). Este
exemplo mostra que mesmo que você já tenha visto o código uma dúzia de vezes antes, simplesmente
ser exposto ao código pode não ser suficiente para que você consiga se lembrar dele. As informações são
armazenadas em algum lugar em seu LTM, mas não estão prontamente disponíveis quando você precisa delas.
4
“Recordação, Familiaridade e Reintegração Cortical: Uma Análise de Padrão Multivoxel”, de Jeffrey D. Johnson,
Susan GR McDuff, Michael D. Rugg e Kenneth A. Norman. Neurônio, v. 63, não. 5, 8 de setembro de 2009.
Machine Translated by Google
Um dos primeiros estudos sobre a prática de recuperação foi feito por um professor chamado
Philip Boswood Ballard, que publicou um artigo sobre o tema chamado “Obliviscence and
Reminiscence” em 1913. Ballard treinou um grupo de alunos para memorizar 16 versos do poema
“The Wreck do Hesperus”, que conta a história de um capitão vaidoso que causa a morte de sua
filha. Quando ele examinou seu desempenho de recordação, ele observou algo interessante.
Sem dizer aos alunos que eles seriam testados novamente, Ballard os fez reproduzir o poema
novamente após dois dias. Como não sabiam que outro teste estava por vir, os alunos não
estudaram o poema mais adiante. Durante o segundo teste, os alunos lembraram em média 10%
a mais do poema. Depois de mais dois dias, suas pontuações de recordação foram mais altas
novamente. Suspeitando do resultado, Ballard repetiu esses estudos várias vezes - mas
encontrou resultados semelhantes a cada vez: quando você tenta ativamente se lembrar de
informações sem estudo adicional, vai se lembrar mais do que aprendeu.
Agora que você está familiarizado com a curva de esquecimento e os efeitos da prática de
recuperação, deve ficar ainda mais claro por que simplesmente procurar uma sintaxe que você
não conhece toda vez que precisar não é uma boa ideia. Como pesquisar é tão fácil e é uma
tarefa tão comum, nossos cérebros sentem que não precisam realmente lembrar a sintaxe.
Portanto, a força de recuperação da sintaxe de programação permanece fraca.
O fato de não nos lembrarmos da sintaxe, é claro, leva a um ciclo vicioso.
Como não nos lembramos, precisamos procurá-lo. Mas, como continuamos pesquisando em vez
de tentar lembrá-lo, nunca melhoramos a força de recuperação desses conceitos de programação
- portanto, devemos continuar pesquisando-os, ad infinitum.
Então, da próxima vez que você estiver prestes a pesquisar algo no Google, pode valer a
pena primeiro tentar ativamente lembrar a sintaxe. Mesmo que não funcione dessa vez, o ato de
tentar lembrar pode fortalecer sua memória e pode ajudá-lo a lembrar da próxima vez.
Se isso não funcionar, faça um flashcard e pratique ativamente.
Antes de nos aprofundarmos no processo de elaboração e como você pode usá-lo para melhor
aprender novos conceitos de programação, precisamos examinar mais de perto como o armazenamento no
cérebro funciona.
ESQUEMA
Você viu que as memórias em seu cérebro são armazenadas em uma forma de rede, com relações com
outras memórias e fatos. As maneiras pelas quais os pensamentos e as relações entre eles são organizados
em nossas mentes são chamados de esquemas ou esquemas.
Quando você aprende novas informações, você tentará encaixar as informações em um esquema
em seu cérebro antes de armazená-lo em LTM. Informações que se ajustam melhor a um esquema existente
será mais fácil de lembrar. Por exemplo, se eu pedir para você se lembrar dos números 5, 12,
91, 54, 102 e 87 e depois liste três deles para um belo prêmio de sua escolha, que
ser difícil de fazer, porque não há “ganchos” para conectar as informações. Esta lista
será armazenado em um novo esquema: “Coisas que estou lembrando para ganhar um belo prêmio”.
No entanto, se eu pedir para você se lembrar de 1, 3, 15, 127, 63 e 31, isso pode ser mais fácil de
Faz. Se você pensar um pouco, verá que esses números se encaixam em uma categoria de números:
números que, quando transformados em uma representação binária, consistem em apenas uns. Você não só
consegue lembrar esses números com mais facilidade, mas também
também pode estar mais motivado a lembrar os números porque esta é uma
coisa para fazer. Você sabe que se você conhece o limite superior dos números em bits, isso pode
ajudar a resolver alguns problemas.
Lembre-se que quando a memória de trabalho processa informações, ela também procura
o LTM para fatos e memórias relacionados. Quando suas memórias estão conectadas a cada
outro, encontrar as memórias é mais fácil. Em outras palavras, a força de recuperação é maior para
memórias que se relacionam com outras memórias.
Quando salvamos memórias, as memórias podem até ser alteradas para se adaptarem
aos esquemas existentes. Na década de 1930, o psicólogo britânico Frederic Bartlett conduziu uma
experimento em que ele pediu às pessoas que estudassem um conto de nativos americanos chamado The
Guerra dos Fantasmas e tente relembrar a história semanas ou até meses depois .
suas descrições, Bartlett pôde ver que os participantes haviam mudado a história para
suas crenças e conhecimentos existentes. Por exemplo, alguns participantes omitiram detalhes
consideravam irrelevantes. Outros participantes tornaram a história mais “ocidental”
e de acordo com suas normas culturais; por exemplo, substituindo um arco por uma arma. Isto
experimento mostrou que as pessoas não se lembram de palavras e fatos simples, mas que
adaptar memórias para se adequarem às suas memórias e crenças existentes.
O fato de as memórias poderem ser alteradas assim que as armazenamos pode ter
lados. Duas pessoas envolvidas na mesma situação podem se lembrar dela de maneira totalmente diferente
depois, porque seus próprios pensamentos e ideias afetarão a forma como elas armazenam os dados.
recordações. Mas também podemos usar o fato de que as memórias podem ser alteradas ou salvas em nosso
vantagem ao armazenar informações conhecidas relevantes junto com as informações adicionadas.
5
Veja seu livro Remembering: A Study in Experimental and Social Psychology (Cambridge University Press, 1932).
Machine Translated by Google
Como você viu anteriormente neste capítulo, as memórias podem ser esquecidas quando a força de
recuperação - a facilidade com que você pode coletar informações - não é forte o suficiente.
O experimento de Bartlett mostra que mesmo no primeiro salvamento, quando uma memória é inicialmente
armazenada no LTM, alguns detalhes da memória podem ser alterados ou esquecidos.
Por exemplo, se eu lhe disser que James Monroe foi o quinto presidente dos Estados Unidos, você
pode se lembrar que Monroe é um ex-presidente, mas esquecer que ele era o quinto antes de guardar a
memória. O fato de você não se lembrar do número pode ser causado por vários fatores; por exemplo,
você pode pensar que é irrelevante, ou muito complexo, ou pode ter se distraído. Existem muitos fatores
que afetam o quanto é armazenado, incluindo seu estado emocional. Por exemplo, é mais provável que
você se lembre de um determinado bug que o manteve em sua mesa durante a noite há um ano do que
um bug aleatório que você corrigiu em alguns minutos hoje.
Embora você não possa mudar seu estado emocional, há muitas coisas que você pode fazer para
salvar o máximo possível de uma nova memória. Uma coisa que você pode fazer para fortalecer a
codificação inicial das memórias é chamada de elaboração. Elaboração significa pensar sobre o que você
quer lembrar, relacionando-o com memórias existentes e fazendo com que as novas memórias se
encaixem em esquemas já armazenados em seu LTM.
A elaboração pode ter sido uma das razões pelas quais os alunos do estudo de Ballard foram mais
capazes de lembrar as palavras do poema ao longo do tempo. Recordar repetidamente o poema forçava
os alunos a preencher as palavras que faltavam, cada vez as memorizando. Eles também provavelmente
conectaram partes do poema a outras coisas de que se lembravam.
Se você quiser se lembrar melhor das novas informações, é útil elaborar explicitamente as informações.
O ato de elaboração fortalece a rede de memórias relacionadas, e quando uma nova memória tem mais
conexões é mais fácil recuperá-la. Imagine que você está aprendendo um novo conceito de programação,
como compreensão de listas em Python. Uma compreensão de lista é uma maneira de criar uma lista com
base em uma lista existente. Por exemplo, se você quiser criar uma lista dos quadrados de números que
você já armazenou em uma lista chamada números, você pode fazer isso com esta compreensão de lista:
Imagine que você está aprendendo este conceito pela primeira vez. Se você quiser se ajudar a lembrá-lo
melhor, ajuda muito elaborar deliberadamente pensando em conceitos relacionados. Por exemplo, você
pode tentar pensar em conceitos relacionados em outras linguagens de programação, em conceitos
alternativos em Python ou em outras linguagens de programação, ou em como esse conceito se relaciona
com outros paradigmas.
EXERCÍCIO 3.2 Use este exercício na próxima vez que aprender um novo conceito de
programação. Responder às seguintes perguntas o ajudará a elaborar e fortalecer a nova
memória:
Machine Translated by Google
Resumo 45
ÿ Que conceitos esse novo conceito faz você pensar? Anote todos os
conceitos relacionados.
ÿ Então, para cada um dos conceitos relacionados que você pode pensar, responda a estas perguntas:
– Por que o novo conceito me faz pensar nesse conceito que já
conhecer?
– Compartilha sintaxe?
– É usado em um contexto semelhante?
Resumo
ÿ É importante saber um pouco de sintaxe de cor porque mais conhecimento de sintaxe facilitará o
agrupamento. Além disso, pesquisar a sintaxe pode interromper seu trabalho.
ÿ Você pode usar flashcards para praticar e lembrar a nova sintaxe, com um prompt
um lado e código do outro lado.
ÿ As informações em seu LTM são armazenadas como uma rede conectada de fatos relacionados.
ÿ A elaboração ativa de novas informações ajuda a fortalecer a rede de memórias à qual a nova memória
se conectará, facilitando a recuperação.
Machine Translated by Google
Como ler
código complexo
O Capítulo 1 apresentou as diferentes maneiras pelas quais o código pode ser confuso. Nós temos
visto que a confusão pode ser causada pela falta de informação, que deve ser
adquiridos e armazenados em seu STM, ou por falta de conhecimento, que exige o armazenamento
informações em seu LTM. Este capítulo cobre a terceira fonte de confusão: a falta de
do poder de processamento no cérebro.
Às vezes, o código que você está lendo é muito complexo para você entender completamente
Compreendo. Porque ler código não é uma atividade que a maioria dos programadores pratica
46
Machine Translated by Google
muitas vezes, você pode achar que não tem estratégias para lidar com a leitura de código que você não conhece.
Compreendo. Técnicas comuns como “ler de novo” e “desistir” não são úteis.
Nos capítulos anteriores, abordamos técnicas para ajudá-lo a ler melhor o código. Dentro
no capítulo 2, você aprendeu sobre técnicas para fragmentação de código mais eficaz e
capítulo 3 forneceu dicas para armazenar mais conhecimento de sintaxe em seu LTM, que também
auxilia na leitura do código. No entanto, às vezes o código é tão complexo que mesmo com muito
conhecimento de sintaxe e estratégias eficientes de agrupamento ainda é muito difícil de processar.
Este capítulo mergulha nos processos cognitivos que fundamentam o poder de processamento
do cérebro, que comumente chamamos de memória de trabalho. Vamos explorar o que é memória de trabalho e como
o código pode ser tão confuso que sobrecarrega sua memória de trabalho. Depois de abordarmos o básico, mostrarei
três técnicas para apoiar sua
memória de trabalho para que você possa processar códigos complexos com mais facilidade.
6
LET B$ = STR$ (N1 - N2 * 2) + B$
DEIXE N1 = N2
"1"
7 PRÓXIMO N1
8 IMPRIMIR B$
9 DEVOLUÇÃO
O fato de você sentir a necessidade de fazer isso significa que seu cérebro não tem a capacidade de
processar o código. Vamos comparar o código BASIC com o segundo exemplo do capítulo
1, um programa Java que calcula a representação binária de um inteiro n. Enquanto
interpretar esse código também pode exigir alguma energia mental, e é possível que a falta de familiaridade com o
funcionamento interno do método toBinaryString() possa causar
alguma confusão, é improvável que você sinta a necessidade de fazer anotações durante a leitura.
Não saber sobre o funcionamento interno de toBinaryString() pode causar confusão. Dentro
capítulos anteriores, investigamos dois dos processos cognitivos em jogo quando você lê
código complexo: STM e LTM. Para entender por que às vezes você precisa descarregar
informação, você precisa entender o terceiro processo cognitivo apresentado no capítulo 1, que ainda não
discutimos em detalhes. A memória de trabalho representa a
capacidade do cérebro de pensar, formar novas ideias e resolver problemas. Anteriormente, comparamos o STM
com a RAM de um computador e o LTM com o disco rígido. Segue
nessa analogia, a memória de trabalho é como o processador do cérebro.
A Figura 4.2 mostra um exemplo da diferença entre os dois processos: se você estiver
lembrando de um número de telefone, você usa seu STM, enquanto que, se estiver adicionando números inteiros
você usa sua memória de trabalho.
542
121
542-121-851 851
Como você viu no capítulo 2, o STM normalmente só pode conter de dois a seis itens por vez.
Mais informações podem ser processadas quando as informações são divididas em pedaços reconhecíveis,
como palavras, aberturas de xadrez ou padrões de design. Porque o trabalho
memória é o STM aplicado a um determinado problema, tem a mesma limitação.
Como o STM, a memória de trabalho só é capaz de processar de duas a seis coisas
de uma vez. No contexto da memória de trabalho, essa capacidade é conhecida como a capacidade cognitiva .
carga. Quando você está tentando resolver um problema que envolve muitos elementos que
não pode ser dividido eficientemente em pedaços, sua memória de trabalho se tornará
"sobrecarregado."
Machine Translated by Google
carga alemã Carga cognitiva criada por ter que armazenar seu pensamento no LTM
Vamos nos concentrar nos dois primeiros tipos de carga cognitiva aqui; discutimos a carga alemã em
mais profundidade em um capítulo posterior.
a=8
?
uma b=6
Figura 4.4 Essa maneira de encontrar o
comprimento do terceiro lado de um triângulo
b incorre em uma carga cognitiva externa mais alta nessa forma.
Resolver o problema não se tornou realmente mais difícil - ainda precisamos lembrar e
aplicar o teorema de Pitágoras. No entanto, nossos cérebros precisam trabalhar mais em tarefas
estranhas: conectar a ao valor 8 e b ao valor 6. Na programação, pensamos
desse tipo de carga estranha como semelhante à complexidade acidental: aspectos de um programa
que tornam um problema mais difícil do que precisa ser.
O que cria carga estranha não é o mesmo para todos os programadores. O mais
experiência que você tem usando um determinado conceito, menos carga cognitiva ele cria para você.
Por exemplo, os dois exemplos de código Python na listagem a seguir são equivalentes em termos
computacionais.
Listagem 4.2 Duas versões de um programa Python para selecionar todos os itens acima de 10
acima_dez = []
para um nos itens:
se a > 10: new_items.append(a)
Como os dois trechos de código resolvem o mesmo problema, eles compartilham o mesmo
carga cognitiva intrínseca. No entanto, se eles representam a mesma carga cognitiva estranha para
você depende de seu conhecimento prévio: se você não estiver familiarizado com as compreensões de
lista, então a carga estranha causada pelo primeiro exemplo será muito
maior do que para alguém que é experiente em usá-los.
EXERCÍCIO 4.1 Da próxima vez que você ler um código desconhecido, tente monitorar seu
própria carga cognitiva. Quando o código é difícil de processar e você sente a necessidade
para fazer anotações ou seguir a execução passo a passo, é provável que você esteja
experimentando uma alta carga cognitiva.
Quando você experimenta uma alta carga cognitiva, vale a pena examinar quais
partes do código estão criando os diferentes tipos de carga cognitiva. Você pode
use a tabela a seguir para analisar isso.
Machine Translated by Google
4.2.1 Refatoração
Uma refatoração é uma transformação de código que melhora sua estrutura interna, mas não
não altera o comportamento externo do código. Por exemplo, se um determinado bloco de código for
muito longo, você pode querer dividir a lógica em várias funções, ou se a base de código
exibe duplicação, você pode querer refatorá-lo para reunir todo o código duplicado em
um lugar para fácil reutilização. Na listagem de código Python a seguir, por exemplo, o
cálculo repetido poderia ser colocado em um método.
Listagem 4.3 Código Python que repete o mesmo cálculo duas vezes
Na maioria dos casos, a refatoração é feita para facilitar a manutenção do código resultante. Por
Por exemplo, eliminar o código duplicado significa que as alterações nesse código só precisam ser
feito em um local.
Mas o código que é mais sustentável em geral nem sempre é mais legível agora. Por
Por exemplo, considere um pedaço de código que contém muitas chamadas de método e, portanto, depende
no código espalhado por muitos lugares diferentes em um arquivo ou mesmo em vários arquivos. que
arquitetura pode ser mais sustentável porque toda a lógica tem seu próprio método.
No entanto, esse código deslocalizado também pode ser mais difícil para sua memória de trabalho porque
você terá que rolar ou procurar por definições de função em locais diferentes.
Machine Translated by Google
Portanto, às vezes você pode querer refatorar o código não para torná-lo mais sustentável a longo
prazo, mas mais legível para você naquele momento. Nós definimos
tal refatoração como uma refatoração cognitiva. Uma refatoração cognitiva é uma mudança em um código
base que não altera seu comportamento externo, semelhante a uma refatoração regular. No entanto, o
objetivo de uma refatoração cognitiva não é tornar o código mais sustentável,
mas para torná-lo mais legível para o leitor atual no momento atual.
Uma refatoração cognitiva às vezes pode envolver refatoração reversa, o que diminui
manutenibilidade, como inlining - pegando a implementação de um método e copiando
o corpo da função diretamente no site da chamada. Alguns IDEs podem realizar essa refatoração
automaticamente. O código embutido pode ser especialmente útil quando o nome de um método é
não muito revelador, como calcular() ou transform(). Ao ler uma chamada para um
método com um nome vago, você precisará gastar algum tempo na frente para entender
o que o método faz — e provavelmente levará várias exposições antes que a funcionalidade do método
seja armazenada em seu LTM.
Inline o método reduz sua carga cognitiva estranha e pode ajudá-lo
compreender o código que chama o método. Além disso, estudar o código do
método em si pode ajudá-lo a entender o código, o que pode ser mais fácil com mais
contexto. Dentro do novo contexto, você também poderá escolher um nome melhor para
o método.
O restante deste capítulo abrange técnicas que podem ajudá-lo a combater os três
possíveis fontes de confusão ao ler o código (falta de conhecimento, informação,
e poder de processamento). Se o código que você está lendo contém conceitos de programação
você não está familiarizado, você está lidando com a falta de conhecimento. Começaremos com um
técnica que pode ser útil nesses casos.
Em algumas situações, as construções desconhecidas com as quais você está trabalhando podem ser
expressa de uma forma diferente e mais familiar. Por exemplo, muitos programas modernos
Machine Translated by Google
linguagens (como Java e C#) suportam funções anônimas, geralmente chamadas de lambdas.
Lambdas são funções que não precisam receber um nome (portanto, “anônimas”).
Outro exemplo é uma compreensão de lista em Python. Lambdas e compreensões de lista são
ótimas maneiras de tornar o código mais curto e mais legível, mas muitos programadores não
estão familiarizados com eles e os lêem com menos facilidade do que leriam um loop for ou while.
Se o código que você está lendo ou escrevendo for simples e direto, lambdas ou compreensão
de listas podem não representar um problema, mas se você estiver trabalhando com código mais
complexo, essas estruturas avançadas podem sobrecarregar sua memória de trabalho. Construções
de linguagem menos familiares aumentam a carga cognitiva estranha em sua memória de trabalho,
portanto, quando você está lendo um código complexo, pode ser benéfico para sua memória de
trabalho não ter que lidar com isso.
Embora as construções de linguagem precisas que você pode querer substituir sejam, é claro,
dependentes de seu próprio conhecimento prévio, normalmente há duas razões para substituir o
código para diminuir sua carga cognitiva: primeiro porque essas construções são conhecidas por
serem confusas e, segundo, porque eles têm um equivalente claro que é mais básico. Ambas as
condições se aplicam a lambdas e compreensões de lista, portanto, esses são bons exemplos a
serem usados para demonstrar essa técnica. Pode ser útil traduzi-los em um loop for ou while para
diminuir a carga cognitiva até que você compreenda melhor o que o código está fazendo. Os
operadores ternários também são bons candidatos para tal refatoração.
LAMBDAS
O código Java na listagem a seguir é um exemplo de uma função anônima usada como parâmetro
para uma função filter() . Se você estiver familiarizado com o uso de lambdas, este código será
bastante fácil de seguir.
Listagem 4.4 Uma função filter() em Java que recebe uma função anônima como argumento
No entanto, se lambdas são novos para você, esse código pode causar muita carga cognitiva
estranha. Se você sentir que está tendo problemas com a expressão lambda, você pode
simplesmente reescrever o código para usar uma função regular temporariamente, como no exemplo a seguir.
Listagem 4.5 Uma função filter() em Java que usa uma função tradicional como argumento
public static class Toetsie implementa Predicate <Product> { private int id;
}}
LISTA DE COMPREENSÕES
Python suporta uma estrutura sintática chamada compreensão de lista, que pode criar listas com
base em outras listas. Por exemplo, você pode usar o código a seguir para criar uma lista de nomes
com base em uma lista de clientes.
Listagem 4.6 Uma compreensão de lista em Python transformando uma lista em outra lista
As compreensões de lista também podem usar filtros que as tornam um pouco mais complexas,
por exemplo, para criar uma lista de nomes de clientes com mais de 50 anos, conforme mostrado
na próxima listagem.
customer_names =
[c.first_name para c em clientes se c.idade > 50]
Embora este código possa ser fácil de ler para alguém que está acostumado a listar compreensões,
para alguém que não está (ou mesmo alguém que está, se estiver embutido em um pedaço de
código complexo), pode causar muita pressão sobre a memória de trabalho. Quando isso acontecer,
você pode transformar a compreensão da lista em um loop for para facilitar a compreensão.
Listagem 4.8 Um loop for em Python transformando uma lista em outra lista usando um filtro
customer_names = []
para c em clientes:
se c.idade > 50:
customer_names.append(c.first_name)
OPERADORES TERNIÁRIOS
Muitas linguagens suportam operadores ternários, que são uma abreviação de instruções if. Eles
normalmente têm a forma de uma condição seguida pelo resultado quando a condição for
verdadeira e, em seguida, o resultado quando a condição for falsa. Por exemplo, a listagem 4.9 é
uma linha de código JavaScript que verifica se a variável booleana isMember é verdadeira usando
um operador ternário. Se isMember for true, o operador ternário retornará $ 2,00; se não, ele
retorna $ 10,00.
Machine Translated by Google
Algumas linguagens, como Python, suportam operações ternárias em uma ordem diferente, dando
primeiro o resultado quando a condição for verdadeira, seguida pela condição e então a
resultado quando a condição for falsa. O exemplo a seguir é uma linha de código Python que
verifica se a variável booleana isMember é verdadeira usando um operador ternário. Como em
no exemplo JavaScript, se isMember for true, o operador ternário retornará $ 2,00 e se
não é, ele retorna $ 10,00.
Se você costuma ter dificuldades com, por exemplo, compreensão de listas, considere adicionar
algumas cartas dessa construção de programação para o seu baralho. Para conceitos de programação mais
avançados como esses, pode funcionar melhor ter código em ambos os lados da
flashcard em vez de uma explicação de texto - ou seja, você pode ter o código vanilla em um
lado, e por outro o código equivalente usando o conceito avançado, como um ternário ou lambda.
4.3 Auxílios de memória para usar quando sua memória de trabalho está sobrecarregada
A seção anterior introduziu uma técnica para reduzir a carga cognitiva que o código
pode criar, refatorando-o para uma forma mais familiar. No entanto, mesmo em seu estado refatorado,
código ainda pode sobrecarregar sua memória de trabalho se sua estrutura for muito complexa. Lá
são duas maneiras de código com uma estrutura complicada pode sobrecarregar a memória de trabalho.
Primeiro, você pode não saber exatamente quais partes do código você precisa ler. Isto
faz com que você leia mais do código do que o necessário, o que pode ser mais do que o seu
memória de trabalho é capaz de processar.
Segundo, com código altamente conectado, seu cérebro está tentando fazer duas coisas ao mesmo tempo.
ao mesmo tempo: entender linhas individuais de código e entender a estrutura de
o código para decidir onde continuar lendo. Por exemplo, quando você encontra um
chamada de método para a qual você não conhece a funcionalidade exata, talvez seja necessário
localize e leia o método antes de continuar lendo o código no site da chamada.
Se você já leu o mesmo pedaço de código cinco vezes seguidas sem fazer
progresso, você provavelmente não sabia em quais partes do código focar ou em que ordem.
Você pode ter entendido cada linha de código individualmente, mas não
compreensão do quadro maior. Quando você atinge os limites de sua memória de trabalho, pode usar um
auxílio de memória para ajudá-lo a se concentrar nas partes certas do código.
Auxílios de memória para usar quando sua memória de trabalho está sobrecarregada 57
Figura 4.5 Código no qual todas as variáveis são circuladas para auxiliar no entendimento
Depois de localizar todas as variáveis, concentre-se nos métodos e funções no código. Circule-os em
uma cor diferente.
Machine Translated by Google
Figura 4.6 Código no qual todas as variáveis são circuladas e vinculadas às suas outras
ocorrências para auxiliar no entendimento
onde são invocados. Concentre atenção especial em métodos com apenas uma invocação, porque
esses métodos são candidatos a serem alinhados com uma refatoração,
como explicado anteriormente neste capítulo.
5 Circule todas as instâncias de classes.
Como etapa final no exame do código, vincule instâncias da mesma classe aos seus
definição se essa definição estiver presente no código. Se a definição não estiver presente, você
pode vincular as instâncias da mesma classe umas às outras.
O padrão colorido que você criou usando as seis etapas anteriores indica o fluxo de
o código e pode ser usado como um auxílio na sua leitura. Agora você tem uma referência que pode consultar
para obter informações sobre a estrutura do código, economizando o esforço de, por exemplo,
Machine Translated by Google
Auxílios de memória para usar quando sua memória de trabalho está sobrecarregada 59
Mesmo quando o código é refatorado para a forma mais fácil possível alinhada com o seu
conhecimento e com todas as dependências marcadas, ainda pode ser confuso. Às vezes o
causa da confusão não é a estrutura do código, mas os cálculos que ele faz. Esta é uma questão de falta
de poder de processamento.
Por exemplo, vamos revisitar o código BASIC do capítulo 1 que converte o número
N em uma representação binária. Neste programa as variáveis influenciam umas às outras fortemente, por
isso é necessário um exame detalhado de seus valores para compreendê-lo. Você pode usar um
auxílio de memória, como um gráfico de dependência para código como este que executa cálculos
complicados, mas há outra ferramenta que pode ajudar com códigos pesados de cálculo: uma tabela de estados.
Uma tabela de estado se concentra nos valores das variáveis em vez da estrutura do código.
Possui colunas para cada variável e linhas para cada etapa do código. Dê outra olhada
em nosso exemplo de programa BASIC na próxima listagem. É confuso porque você não pode ver
todos os cálculos intermediários e seus efeitos nas diferentes variáveis.
Listagem 4.12 Código BASIC que converte um número N em sua representação binária
3 Adicione uma linha à tabela para cada parte distinta da execução do código.
O código que contém cálculos complexos provavelmente também conterá algumas dependências
complexas, como um loop dependendo de um cálculo ou um if complicado
declaração. As linhas na tabela de estado representam partes separadas das dependências. Por
Por exemplo, como mostrado na figura 4.7, uma linha pode representar uma iteração em um loop,
precedida pelo código de inicialização. Alternativamente, uma linha pode representar uma ramificação em um
grande if, ou simplesmente um grupo de linhas de código coerentes. Em código extremamente
complexo e conciso, uma linha na tabela pode até representar uma linha de código.
4 Execute cada parte do código e anote o valor que cada variável tem
na linha e coluna corretas.
Depois de preparar a tabela, percorra o código e calcule o novo valor de cada variável para cada
linha na tabela de estados. O processo de
o código executado mentalmente é chamado de rastreamento ou compilação cognitiva. Ao rastrear o
código usando uma tabela de estados, pode ser tentador pular algumas variáveis e apenas preencher
parte da mesa, mas tente resistir a essa tentação. Trabalhar com ele meticulosamente o ajudará a
obter uma compreensão mais profunda do código e o resultado
table irá suportar sua memória de trabalho sobrecarregada. Em uma segunda leitura do
programa você pode usar a tabela como referência, permitindo que você se concentre no
coerência do programa e não nos cálculos detalhados.
Figura 4.8 Tutor Python mostrando a diferença entre armazenar o inteiro x e seu valor
diretamente e armazenar uma fruta de lista com um ponteiro
Machine Translated by Google
Auxílios de memória para usar quando sua memória de trabalho está sobrecarregada 61
1
4.3.3 Combinando gráficos de dependência e tabelas de estado
Esta seção e a seção 4.2 descrevem duas técnicas para apoiar sua memória de trabalho
ao ler o código descarregando algumas informações sobre o código no papel: desenhando um gráfico de
dependência e criando uma tabela de estados. Essas técnicas focam em diferentes partes do código:
enquanto o gráfico de dependência chama sua atenção para como o
código é organizado, a tabela de estados captura os cálculos no código. Ao explorar um código
desconhecido, você pode usar ambos os exercícios para obter uma visão completa de seu funcionamento
interno e usar como auxiliares de memória ao ler o código após completá-los.
EXERCÍCIO 4.2 Seguindo os passos descritos nas seções anteriores, crie ambos
um gráfico de dependência e uma tabela de estado para cada um dos seguintes programas Java.
Programa 1
while (total--> 0) {
for (int i = 0; i < n - (n - k); i++)
System.out.print(a[indexes[i]]);
System.out.println();
if (decider.test(índices))
pausa;
1
Veja “The Use of Python Tutor on Programming Laboratory Session: Student Perspectives” de Oscar Karnalim
e Mewati Ayub (2017), https://kinetik.umm.ac.id/index.php/kinetik/article/view/442.
Machine Translated by Google
pausa;
}
}
}
}
}
Programa 2
}
}
}
Resumo ÿ
A carga cognitiva representa o limite do que a memória de trabalho pode processar.
Quando você experimenta muita carga cognitiva, não consegue processar o código
corretamente.
ÿ Existem dois tipos de carga cognitiva que são relevantes na programação: a carga
cognitiva intrínseca é criada pela complexidade inerente de um pedaço de código, enquanto
Machine Translated by Google
Resumo 63
ÿ Criar uma tabela de estados contendo os valores intermediários das variáveis pode auxiliar na
leitura de código que é pesado em cálculos.
Machine Translated by Google
Machine Translated by Google
Parte 2
Ao pensar em código
Na parte 2, vamos nos concentrar não tanto na leitura de código, mas em pensar sobre
código: como entender profundamente os programas e evitar bugs no pensamento.
Machine Translated by Google
Machine Translated by Google
compreensão de código
Anteriormente neste livro, discutimos o uso de flashcards e a prática repetida como técnicas para
aprender sintaxe, e abordamos estratégias para se familiarizar rapidamente com
novo código, como realçar variáveis e seus relacionamentos. Ao saber
sintaxe e entender as relações entre as variáveis é um passo importante
para entender o código, há questões mais profundas que desempenham um papel quando se pensa
sobre o código.
Quando você lê um pedaço de código desconhecido, pode ser difícil ver o que o código
está fazendo. Para usar um termo cognitivo apresentado anteriormente neste livro, você pode dizer que
67
Machine Translated by Google
ao ler um código desconhecido, sua carga cognitiva é alta. Vimos que a carga cognitiva pode ser reduzida
substancialmente aprendendo sintaxe e novos conceitos de programação e reescrevendo o código.
Uma vez que você tenha uma boa compreensão do que o código está fazendo, o próximo passo é
pense sobre o código em mãos com mais profundidade. Como foi criado? Onde você pode
adicionar um novo recurso? Quais são algumas alternativas de projeto possíveis?
Nos capítulos anteriores, falamos sobre esquemas, ou como as memórias são organizadas em
o cérebro. As memórias não são armazenadas separadamente, mas possuem links para outras memórias. Você
pode tirar proveito dessas conexões ao raciocinar sobre código porque as memórias armazenadas em seu LTM
ajudam você a criar pedaços em sua memória de trabalho que podem ajudar
você pensa em código.
Pensar em código é o tópico deste capítulo, no qual iremos
uma compreensão mais profunda do código. Abordaremos três estratégias para refletir sobre o código em um
nível mais profundo, incluindo métodos para raciocinar sobre as idéias, pensamentos e decisões de
o criador do código. Primeiro, examinaremos uma estrutura que o ajudará a raciocinar sobre
código. Em seguida, discutiremos diferentes níveis de compreensão e algumas técnicas para
indo mais fundo. Por fim, vamos nos aprofundar em algumas estratégias provenientes da leitura natural
linguagem que pode ajudar na leitura do código. Pesquisas recentes indicam que as habilidades que
necessidade de leitura de código e as habilidades que usamos para ler linguagem natural são fortemente
relacionados, o que significa que nós programadores podemos aprender muito com a linguagem natural
é lido para uma compreensão mais profunda.
Este programa contém quatro variáveis: limite superior, contador, fatores e fatores
max_prime_. No entanto, se simplesmente descrevermos este programa como tendo
quatro variáveis, não será muito útil para compreender o programa; isso é abstrato demais.
Observar os nomes das variáveis pode ajudar um pouco, mas não explica tudo. counter, por exemplo,
ainda é muito genérico. Este é um número estático de coisas ou muda no programa? Examinar os
papéis que cada uma das quatro variáveis desempenha pode ajudar.
Neste programa, o usuário é solicitado a informar um valor, que é armazenado no limite superior
da variável. Depois disso, um loop será executado até atingir esse limite superior no contador de
variáveis. Os fatores variáveis mantêm temporariamente o número de fatores primos para o valor atual
do contador. Por fim, a variável max_prime_factors representa o maior número encontrado na execução
do loop.
A estrutura de papéis de variáveis captura essa diferença no comportamento dessas variáveis. A
variável limite superior desempenha o papel de um detentor mais recente: ela armazena o limite
superior inserido mais recentemente. counter, por outro lado, é um stepper, que itera através de um
loop. max_prime_factors é o titular mais procurado; ele armazena um valor que está sendo procurado.
Os fatores variáveis são titulares mais recentes; ele armazena o número mais recente de fatores
primos. Na seção a seguir, explicarei essas funções e as outras na estrutura com mais detalhes.
Como o exemplo anterior mostra, os papéis que as variáveis desempenham são comuns. Muitos
programas têm uma variável que é um stepper ou um suporte mais procurado. Na verdade, Sajaniemi
argumenta que com apenas 11 papéis, você pode descrever quase todas as variáveis:
ÿ Valor fixo—Uma variável cujo valor não muda após a inicialização desempenha o papel de um
valor fixo. Este pode ser um valor constante se a linguagem de programação que você está
usando permite que os valores sejam fixos, ou pode ser uma variável que é inicializada uma
vez e depois não é alterada. Exemplos de variáveis de valor fixo incluem constantes matemáticas
como pi, ou dados lidos de um arquivo ou banco de dados. ÿ Stepper —Ao iterar em um loop,
sempre há uma variável percorrendo uma lista de valores. Esse é o papel do stepper, cujo valor
pode ser previsto assim que a sucessão se inicia. Isso pode ser um inteiro, como a iteração
canônica em um loop for, mas steppers mais complicados também são possíveis, como size =
size / 2 em uma pesquisa binária, onde o tamanho do array a ser pesquisado é cortado pela
metade em cada
iteração.
Machine Translated by Google
1
ÿ Bandeira —Uma variável usada para indicar que algo aconteceu ou é o caso.
Exemplos típicos são is_set, is_available ou is_error. Os sinalizadores geralmente são bool eans, mas
podem ser inteiros ou até mesmo strings.
ÿ Walker —Um walker percorre uma estrutura de dados, semelhante a um stepper. A diferença
está na forma como a estrutura de dados é percorrida. Um stepper sempre itera sobre uma lista
de valores que são conhecidos de antemão, como em um loop for em Python: for i in
intervalo(0, n). Um walker, por outro lado, é uma variável que percorre um
estrutura de uma maneira desconhecida antes do início do loop. Dependendo do
linguagem de programação, os caminhantes podem ser ponteiros ou índices inteiros. Os caminhantes podem
percorre listas, por exemplo, na pesquisa binária, mas percorre com mais frequência estruturas de dados
como uma pilha ou uma árvore. Exemplos de um andador são uma variável que está atravessando
uma lista encadeada para encontrar a posição onde um novo elemento deve ser adicionado ou um
índice de busca em uma árvore binária.
ÿ Detentor mais recente—Uma variável que contém o valor mais recente encontrado na
através de uma série de valores é um detentor mais recente. Por exemplo, pode armazenar
a última linha lida de um arquivo (line = file.readline()), ou uma cópia do
elemento de array referenciado pela última vez por um stepper (element = list[i]).
ÿ Detentor mais procurado —Muitas vezes, quando você está iterando sobre uma lista de valores, você está
fazendo isso para procurar um determinado valor. A variável que mantém esse valor, ou
o melhor valor encontrado até agora, é o que chamamos de titular mais procurado. Canônico
exemplos de um detentor mais procurado são uma variável que armazena um valor mínimo, um
valor máximo, ou o primeiro valor que atende a uma determinada condição.
ÿ Gatherer —Um coletor é uma variável que coleta dados e os agrega em um
valor. Esta pode ser uma variável que começa em zero e coleta valores durante a iteração
através de um loop, assim:
soma = 0
para i no intervalo (lista):
soma += lista[i]
Seu valor também pode, no entanto, ser calculado diretamente em linguagens funcionais ou linguagens que
englobem certos aspectos funcionais: funcional_total = soma(lista).
ÿ Container —Um container é qualquer estrutura de dados que contém vários elementos que
pode ser adicionado e removido. Exemplos de contêineres são listas, arrays, pilhas e
árvores.
ÿ Seguidor —Alguns algoritmos exigem que você acompanhe um valor anterior ou subsequente. Uma variável
neste papel é chamada de seguidor e é sempre acoplada a
outra variável. Exemplos de variáveis seguidoras são um ponteiro que aponta para um
elemento anterior em uma lista encadeada ao percorrer a lista, ou o índice inferior em uma
busca binária.
1
A estrutura de Sajaniemi especificamente chama isso de “bandeira de mão única”, mas acho que esse papel específico é muito restrito.
Machine Translated by Google
Papéis e paradigmas 71
ÿ Organizador—Às vezes, uma variável precisa ser transformada de alguma forma para
em processamento. Por exemplo, em alguns idiomas, você não pode acessar caracteres individuais em uma
string sem primeiro converter a string em uma matriz de caracteres, ou
pode querer armazenar uma versão ordenada de uma determinada lista. Esses são exemplos de organizadores,
que são variáveis que são usadas apenas para reorganizar ou armazenar valores de forma diferente. Muitas
vezes, são variáveis temporárias.
ÿ Temporária—Variáveis temporárias são variáveis que são usadas apenas brevemente e são
muitas vezes chamado de temp ou t. Estas variáveis podem ser usadas para trocar dados ou para armazenar o
resultado de uma computação que é usada várias vezes em um método ou função.
A Figura 5.1 apresenta uma visão geral das 11 funções de Sajaniemi e ajuda você a descobrir quais
papel que uma variável pode desempenhar.
sim
Valor constante? Valor fixo
não
sim Recipiente
não
Repetição?
sim
As instâncias de Dog têm dois atributos: name e age. O valor do nome do atributo
não muda após a inicialização; é um valor fixo. O atributo age se comporta de maneira semelhante ao contador
de variáveis no programa Python que vimos anteriormente: ele avança
através de uma sequência conhecida começando em 0 e aumentando a cada aniversário, de modo que,
seu papel é stepper.
Para a maioria dos programadores profissionais, os papéis no framework de Sajaniemi serão um pouco familiares
(talvez por outros nomes). Em vez de introduzir novos conceitos, o
O objetivo desta lista é fornecer a você um novo vocabulário para usar ao discutir variáveis.
Especialmente quando compartilhado entre uma equipe, a estrutura pode ser uma ótima maneira de melhorar
compreensão e comunicação sobre código.
Estar familiarizado com essas funções também pode trazer benefícios para os recém-chegados. Estudos têm
mostrou que esta estrutura pode ajudar os alunos a processar mentalmente o código-fonte e que
os alunos que usam os papéis da estrutura de variáveis superam aqueles que não usam.2 Um
razão pela qual é tão eficaz é que muitas vezes um grupo de papéis juntos caracteriza um certo
Tipo de programa. Por exemplo, um programa com um stepper e um suporte mais procurado
value é um programa de busca.
EXERCÍCIO 5.1 Este é um ótimo momento para praticar o uso dos papéis das variáveis
estrutura. Encontre algum código com o qual você não esteja familiarizado e examine o
variáveis tomando nota do seguinte para cada um:
ÿ O nome da variável
ÿ O tipo da variável
ÿ As operações nas quais a variável desempenha um papel
ÿ O papel da variável de acordo com a estrutura de papéis de variáveis de Sajaniemi
Depois de preencher a tabela, reflita sobre suas decisões sobre o papel de cada
variável. Como você determinou o papel? Qual dos outros aspectos desempenhou
parte na sua decisão? Foi influenciado pelo nome da variável, suas operações, comentários no código
ou talvez sua própria experiência com o código?
2
Veja, por exemplo, “Um experimento sobre o uso de funções de variáveis no ensino de programação introdutória” por
Jorma Sajaniemi e Marja Kuittinen (2007), www.tandfonline.com/doi/full/10.1080/08993400500056563.
Machine Translated by Google
Papéis e paradigmas 73
sobre o código, permitindo que você interaja com ele em um nível diferente. Bandeira
andador
Já fiz exercícios de código em papel com muitos programadores Titular mais recente
profissionais e, uma vez que passaram de suas primeiras inibições, todos
Titular mais procurado
acharam tremendamente valioso. De
Coletor
claro, para projetos maiores, talvez você não consiga imprimir todos
Recipiente
o código-fonte relevante, mas você pode começar com uma classe ou
Seguidor
parte do programa. Se a impressão do código não for viável
devido ao seu tamanho ou por outras razões práticas, muitos Organizador
tornar-se um forte auxiliar de memória. Para fazer a memorização deles código desconhecido. Estes são
os ícones que eu uso.
mais fácil, você pode criar um baralho de flashcards.
A Figura 5.3 mostra o exemplo de código Python anterior com os papéis das variáveis
anotado.
Ao escrever código, pode ser muito valioso colocar o nome da função na variável
nome, especialmente quando todas as pessoas que trabalham com o código estão familiarizadas com o conceito
de papéis. Embora possa tornar o nome da variável mais longo, transmite informações importantes
informações e poupa o leitor o esforço de descobrir o papel por si mesmo.
Por motivos que não são totalmente claros, a equipe do Windows também adotou a convenção, mas
apenas para tipos de dados, não semânticos. Joel Spolsky, que trabalhou no Excel antes
fundador do Stack Overflow, atribuiu a má interpretação da notação húngara
ao fato de Simonyi usar a palavra “tipo” em vez de “tipo” para explicar o papel do
o prefixo.3
3
“Making Wrong Code Look Wrong”, de Joel Sprosky, 11 de maio de 2005, www.joelonsoftware.com/2005/05/11/
fazendo-código-errado-parecer-errado/.
Machine Translated by Google
Se você olhar para o trabalho original de Simonyi, no entanto, sua explicação dos tipos aparece em
a mesma página como exemplos concretos, sem tipo, como cX para contagem. acho que é mais
provável que um pequeno grupo de pessoas, ou talvez apenas uma, simplesmente começou a usar o sistema
da maneira errada, e esse uso se espalhou. Como veremos com mais detalhes no capítulo 10,
as pessoas geralmente se apegam às convenções uma vez que estão no código. No entanto, veio a ser,
a forma errada de notação húngara foi popularizada no mundo Windows—
em grande parte pelo influente livro de Charles Petzold Programming Windows (Microsoft Press,
1998) - e então vieram as pessoas dizendo: "A notação húngara é considerada prejudicial",
E o resto é história.
No entanto, acho que ainda há muito valor nas ideias de Simonyi. Algumas das propostas defendidas
pelo Apps Húngaro são muito parecidas com os papéis na estrutura de Sajamieni. Por exemplo, Simonyi
usou o prefixo t para denotar um valor temporário, e também
proposto min e max como prefixos para os valores mínimos e máximos em uma matriz,
que são exemplos típicos dos valores mais procurados das funções do quadro de variáveis. É uma pena que
o principal benefício da notação húngara original - torná-la
mais fácil de ler o código porque menos esforço mental é necessário para raciocinar sobre o papel de um
Dissecar diferentes níveis de compreensão era o objetivo de Nancy Pennington, professora de psicologia da
Universidade do Colorado. Ela criou um modelo de dois níveis diferentes nos quais um programador pode
entender o código-fonte: conhecimento da estrutura do texto
e planejar o conhecimento.
De acordo com o modelo de Pennington, o conhecimento da estrutura do texto relaciona-se ao nível da superfície.
compreensão de partes do programa, como saber o que uma palavra-chave faz ou
conhecer o papel de uma variável. O conhecimento do plano, por outro lado, representa a compreensão do
que um programador planejou quando criou o programa ou o que eles
pretendiam alcançar. Os objetivos do programador que criou o código não são
apenas ocultos em variáveis e seus papéis, mas tornam-se mais aparentes quando examinamos
como o código é estruturado e conectado. As próximas subseções irão ensiná-lo a cavar
mais profundamente nas intenções do código.
Machine Translated by Google
O ponto focal do código é uma noção importante ao ler o código. Simplificando, você
tem que saber por onde começar a ler. Algumas estruturas e técnicas, como estruturas de injeção de
dependência, podem fragmentar pontos focais para que fiquem distantes e
difícil de ligar. Para saber por onde começar, você precisa entender como
a estrutura vincula o código.
Tal situação pode deixar um leitor do código (e muitas vezes até mesmo o escritor)
não tem certeza da estrutura real do sistema em execução, mesmo que cada linha de código seja bastante
compreensível. Este é um exemplo de uma situação em que o programador tem texto
conhecimento, mas carece de conhecimento do plano. Isso pode ser frustrante porque você tem a sensação
que você deve saber o que o código faz (já que não parece complicado), mas o
estrutura subjacente é difícil de ver.
4
Consulte “Perguntas que os programadores fazem durante as tarefas de evolução do software” de Jonathan Sillito, Gail C. Murphy e
Kris De Volder (2006), www.cs.ubc.ca/~murphy/papers/other/asking-answering-fse06.pdf.
Machine Translated by Google
Agora que você entende a diferença entre conhecimento de plano e conhecimento de texto, vamos revisitar a técnica
demonstrada no capítulo 4 como uma forma de aliviar a capacidade cognitiva.
load ao ler código complexo:
Talvez você tenha percebido que esses seis passos são uma instanciação do modelo abstrato de Sillito.
A diferença é que nas etapas do capítulo 4 não havia um ponto de entrada específico; a
modelo foi aplicado a todas as variáveis, métodos e instâncias. Quando você quer ganhar um
compreensão mais profunda de uma parte específica do código, siga estas etapas, mas para um
ponto de entrada.
Essas etapas novamente são melhor executadas imprimindo o código em papel e destacando manualmente
partes dele. Como alternativa, você pode seguir as etapas em um IDE,
onde você adiciona comentários às linhas de código relevantes. Assim como fizemos com os seis passos
No capítulo 4, vamos percorrer o processo de quatro etapas para obter o conhecimento do código do plano com um
pouco mais de detalhes:
O que você está destacando agora é chamado de fatia de código. A fatia de uma linha de
código X é definido como todas as linhas de código que se relacionam transitivamente com a linha X.
Concentrar-se em uma fatia ajuda a entender onde os dados são usados no programa.
Por exemplo, agora você pode se perguntar se existe uma determinada linha ou
método fortemente ligado ao ponto focal. Onde essas relações
ocorrer? Esses locais podem ser um ótimo ponto de partida para explorar o código em
mais profundidade. Quais partes do código são pesadas em chamadas de método? Esses também podem ser
bons pontos de foco para uma investigação mais aprofundada.
Machine Translated by Google
Na etapa final, você pode criar uma lista de conceitos presentes no código para documentar seu entendimento.
Tanto a lista de entidades resultante da etapa 3 quanto essa lista de conceitos podem ser valiosas para serem
adicionadas ao código como documentação.
EXERCÍCIO 5.2 Encontre outro pedaço de código desconhecido em sua própria base de código.
Como alternativa, você pode pesquisar algum código no GitHub. Realmente não importa qual
código você usa, mas deve ser algo com o qual você não esteja familiarizado.
Agora siga estas etapas para obter uma compreensão profunda desse código:
1 Encontre um ponto focal no código. Como você não está corrigindo um bug ou adicionando um recurso,
seu ponto de entrada no código provavelmente será o início do código — por exemplo, um método
main() .
2 Determine a fatia de código relacionada ao ponto focal, seja no papel ou no IDE.
Isso pode exigir alguma refatoração do código para aproximar o código envolvido
na fatia.
3 Com base em sua exploração na etapa 2, anote o que você aprendeu sobre o
código. Por exemplo, quais entidades e conceitos estão presentes no código e
como eles se relacionam entre si?
Machine Translated by Google
Embora ainda não se saiba muito sobre o cérebro, temos uma compreensão bastante decente
de quais partes do cérebro estão relacionadas a quais tipos de funções cognitivas.
Isso se deve principalmente ao neurologista alemão Korbinian Brodmann. Já em 1909, ele
publicou um livro, Vergleichende Lokalisationslehre der Großhirnrinde, detalhando as localizações
de 52 regiões diferentes do cérebro, agora conhecidas como áreas de Brodmann. Para cada área,
5
Consulte “Medindo a compreensão do programa: um estudo de campo em larga escala com profissionais” de Xin Xia et
al. (2017), https://ieeexplore.ieee.org/abstract/document/7997917.
6
“Organização do Conhecimento e Diferenças de Habilidades em Programadores de Computador” por Katherine B.
McKeithen et al. (1981), http://spider.sci.brooklyn.cuny.edu/~kopec/research/sdarticle11.pdf.
Machine Translated by Google
Brodmann detalhou as funções mentais que residem principalmente nessa região, como
lendo palavras ou lembrando. A quantidade de detalhes no mapa que ele produziu está aumentando
continuamente, graças a numerosos estudos nos anos seguintes.7
Por causa do trabalho de Brodmann e estudos subsequentes sobre as regiões do cérebro,
agora temos uma ideia razoável de onde as funções cognitivas “vivem” no cérebro humano.
Saber quais partes do cérebro estão associadas à leitura ou à memória de trabalho
nos ajudou a entender a essência de tarefas maiores.
Esses tipos de estudos podem ser feitos usando uma ressonância magnética funcional
(fMRI) . Uma máquina de fMRI pode detectar quais áreas de Brodmann estão ativas por
medir o fluxo sanguíneo no cérebro. Em estudos de fMRI, os participantes são comumente solicitados
para realizar uma tarefa complexa, como resolver um quebra-cabeça mental. Ao medir os aumentos de
fluxo sanguíneo para diferentes áreas de Brodmann, podemos determinar quais processos cognitivos
estão envolvidos na resolução dessa tarefa, como a memória de trabalho. Uma limitação da fMRI
máquina, no entanto, é que as pessoas não podem se mover enquanto a máquina está digitalizando. Como
tal, a gama de tarefas que os participantes podem realizar é limitada e não inclui
tarefas que envolvem fazer anotações ou produzir código.
EVIDÊNCIAS DA FMRI SOBRE O QUE O CÓDIGO FAZ NO CÉREBRO
A existência do mapa de Brodmann (e das máquinas fMRI, é claro) também deixou os cientistas curiosos
sobre programação. Que áreas cerebrais e funções cognitivas podem
estar envolvido? Em 2014, o primeiro estudo sobre programação em uma máquina de fMRI foi feito por
A professora alemã de ciência da computação Janet Siegmund.8 Os participantes foram convidados a ler
Código Java que representava algoritmos bem conhecidos, incluindo classificação ou pesquisa em um
lista e calcular a potência de dois números. Nomes de variáveis significativos no código
trechos foram substituídos por nomes ofuscados, para que os participantes gastassem
esforço para compreender o fluxo do programa em vez de adivinhar a funcionalidade do
o código baseado em nomes de variáveis.
As descobertas de Siegmund mostraram de forma confiável que a compreensão do programa ativa cinco
Áreas de Brodmann, todas localizadas no hemisfério esquerdo do cérebro: BA6, BA21, BA40,
BA44 e BA4.
O fato de as áreas de Brodmann BA6 e BA40 estarem envolvidas na programação não é
surpreendente. Essas áreas estão relacionadas à memória de trabalho (processador do cérebro) e
atenção. O envolvimento de BA21, BA44 e BA47, no entanto, pode ser um pouco mais surpreendente para
os programadores. Essas áreas estão relacionadas ao processamento de linguagem natural. Isto
encontrar é interessante porque Siegmund ofuscou todos os nomes de variáveis nos programas.
Isso sugere que, embora os nomes das variáveis estivessem ofuscados, os participantes estavam lendo
outros elementos do código (por exemplo, palavras-chave) e tentando extrair significado deles, assim como
fazemos ao ler palavras em um natural.
texto do idioma.
7
Se você estiver interessado, você pode visitar www.cognitiveatlas.org para um mapa recente.
8
Veja “Entendendo os cérebros dos programadores com fMRI” por Janet Siegmund et al. (2014), www.frontiersin
.org/10.3389/conf.fninf.2014.18.00040/event_abstract.
Machine Translated by Google
Vimos que os exames de ressonância magnética mostraram que áreas do cérebro relacionadas à memória
de trabalho e ao processamento de linguagem estão envolvidas na programação. Isso implica que pessoas
com maior capacidade de memória de trabalho e melhores habilidades de linguagem natural serão melhores
programadores?
Pesquisas recentes lançam mais luz sobre a questão de quais habilidades cognitivas desempenham um
papel na programação. A professora associada Chantel Prat da Universidade de Washington liderou um
estudo sobre a conexão entre habilidades cognitivas e programação que avaliou o desempenho de seus
participantes (36 alunos que fizeram um curso de Python na Code Academy) em diversas áreas, incluindo
matemática, linguagem , e raciocínio, bem como capacidade de programação.9 Os testes que Prat usou para
medir as habilidades cognitivas de não programação dos participantes deste estudo eram comumente usados
e conhecidos por testar essas habilidades de forma confiável. Por exemplo, para habilidades matemáticas,
uma pergunta de exemplo diz: “Se cinco máquinas levarem 5 minutos para fazer cinco widgets, quanto tempo
levaria 100 máquinas para fazer 100 widgets?” O teste de raciocínio fluido assemelhava-se a um teste de QI;
os alunos, por exemplo, tinham que terminar uma sequência de imagens abstratas.
Para a capacidade de programação, os pesquisadores analisaram três fatores: as pontuações dos alunos
nos testes da Code Academy; um projeto final em que os alunos tiveram que criar um jogo de Pedra, Papel,
Tesoura; e um exame de múltipla escolha. Especialistas em Python criaram o exame e o esquema de notas
para o projeto final.
Como os pesquisadores tiveram acesso a pontuações de habilidades de programação e pontuações de
outras habilidades cognitivas para cada aluno, eles foram capazes de criar um modelo preditivo para ver quais
habilidades cognitivas previam a capacidade de programação. O que Prat e seus colegas descobriram pode
ser surpreendente para alguns programadores. A numeracia — o conhecimento e as habilidades que as
pessoas precisam para aplicar a matemática — teve apenas um efeito preditivo menor, prevendo apenas 2%
da variação entre os participantes. As habilidades de linguagem foram um melhor preditor, respondendo por
17% da variância. Isso é interessante porque nós, como campo, normalmente enfatizamos o fato de que as
habilidades matemáticas são importantes, e muitos programadores que conheço insistem que são ruins em
aprender linguagens naturais. O melhor preditor para todos os três testes foi a capacidade de memória de
trabalho e habilidades de raciocínio, respondendo por 34% da variação entre os participantes.
Neste estudo, os pesquisadores não apenas mediram as habilidades cognitivas dos 36 participantes,
mas também a atividade cerebral durante os testes, usando um dispositivo de eletroencefalografia (EEG). Ao
contrário de uma máquina de fMRI, este é um dispositivo relativamente simples que mede a atividade cerebral
com eletrodos colocados na cabeça. Os dados de EEG foram levados em consideração ao examinar as três
tarefas de programação.
Para a taxa de aprendizado – ou seja, a rapidez com que os alunos passaram pelo curso da Code
Academy – a habilidade no idioma foi um fator particularmente importante. A taxa de aprendizado e outras
habilidades de programação estavam correlacionadas, então não era como se os alunos rápidos fossem simplesmente
9
“Relacionando a Aptidão de Linguagem Natural às Diferenças Individuais na Aprendizagem de Linguagens de Programação” por Chantal S. Prat
et al. (2020), www.nature.com/articles/s41598-020-60661-8.
Machine Translated by Google
correndo pelo curso sem entender nada. Claro, o fator subjacente aqui pode ser que os alunos que leem
bem aprendem muito em geral, enquanto os alunos que lutam com a leitura não aprendem tão rápido ou
facilmente, independentemente do
domínio da programação que foram ensinados neste estudo.
Para a precisão da programação, medida pelo desempenho na tarefa Pedra, Papel e Tesoura, as
habilidades cognitivas gerais (incluindo memória de trabalho e raciocínio) eram as mais importantes. Para
conhecimento declarativo, medido por um teste de múltipla escolha, EEG
atividade também foi um fator importante. Conforme mostrado na figura 5.4, os resultados deste estudo
parecem indicar que o quão bem você pode aprender uma linguagem de programação é previsto por
sua habilidade em aprender linguagens naturais.
Figura 5.4 Os resultados do estudo de Prat, que mostram que as habilidades numéricas (em azul claro) são apenas um
preditor menor da capacidade de programação. A aptidão da linguagem (em rosa) é um preditor muito mais forte,
especialmente da rapidez com que se pode aprender uma linguagem de programação. Fonte: www.nature.com/articles/
s41598-020-60661-8.pdf . Fonte: Chantal S. Prat et al. (2020).
Este é um resultado que pode ser um tanto inesperado para muitos programadores. Computador
a ciência é muitas vezes vista como um STEM (ciência, tecnologia, engenharia e matemática)
campo e agrupados com essas disciplinas em universidades (incluindo a minha). Na cultura
de programação, as habilidades matemáticas são procuradas como habilidades úteis ou mesmo necessárias.
Essas novas descobertas podem exigir que atualizemos nosso pensamento sobre o que prevê a capacidade
de programação.
Antes de mergulharmos na leitura do código, vamos refletir sobre como lemos um texto (não-ficção),
como um jornal. O que você faz quando lê um artigo de jornal?
Machine Translated by Google
Existem muitas estratégias que as pessoas costumam usar ao ler o texto. Por exemplo,
você pode escanear o texto antes de lê-lo em profundidade para determinar se vale a pena seu tempo.
Você também pode olhar conscientemente para as imagens que aparecem ao lado do texto enquanto lê
para ajudar a apoiar sua compreensão do texto e seu contexto ou fazer anotações para resumir o que você está lendo
ou para destacar as partes mais importantes. Digitalizando texto
e olhar para as imagens que acompanham são estratégias de compreensão do texto. Muitos desses
as estratégias são ensinadas e praticadas ativamente na escola, então é provável que elas sejam automáticas e você
as use sem pensar.
Vamos nos aprofundar em suas habilidades de leitura de código em breve, mas primeiro vamos ver o que
pesquisas científicas descobriram sobre como as pessoas lêem código.
EXERCÍCIO 5.3 Pense em uma ocasião em que você estava lendo um texto de não ficção. O que
estratégias você empregou antes, durante e depois da leitura do texto?
Quando os pesquisadores querem entender o que as pessoas olham, eles usam rastreadores oculares. Olho
rastreadores são dispositivos que podem ser usados para determinar onde em uma tela ou página as pessoas
estão focando sua atenção. Eles são usados extensivamente em pesquisas de marketing para determinar quais tipos
de anúncios chamam a atenção das pessoas por mais tempo. Os rastreadores oculares podem ser
dispositivos físicos e eram usados já na década de 1920, quando ainda ocupavam toda uma
sala. Os rastreadores oculares modernos são muito menores. Eles podem trabalhar com hardware, como um Mic
rosoft Kinect rastreamento de profundidade, ou até mesmo ser totalmente baseado em software e rastrear o usuário
olhar com reconhecimento de imagem.
Os rastreadores oculares permitiram que os pesquisadores entendessem melhor como as pessoas leem o código.
Por exemplo, uma equipe de pesquisadores do Instituto de Ciência e Tecnologia de Nara,
liderado pelo professor Hidetake Uwano, observou que os programadores escaneiam o código para obter um
ideia do que o programa faz.10 Eles descobriram que nos primeiros 30% do tempo gasto
revisando o código, os participantes visualizaram mais de 70% das linhas. Fazendo isso
tipo de varredura rápida é um comportamento comum ao ler a linguagem natural para obter uma visão geral da
estrutura do texto, e parece que as pessoas transferem essa estratégia para
código de leitura.
Para comparar como os desenvolvedores leem código e como as pessoas leem linguagem natural, Teresa
Busjahn, pesquisadora da Freie Universität Berlin, liderou um estudo envolvendo 14 programadores novatos e 6
especialistas.11 Busjahn e seus colegas estudaram pela primeira vez a diferença
entre a leitura de texto e a leitura em linguagem natural. Ela descobriu que o código é lido menos
linearmente do que a linguagem natural: programadores iniciantes seguiram um caminho linear no texto
com aproximadamente 80% de seus movimentos oculares, enquanto eles lêem linearmente em 75% dos
10
Consulte “Analisando o desempenho individual da revisão do código-fonte usando o movimento dos olhos dos revisores” de Hidetake
Uwano et ai. (2006), www.cs.kent.edu/~jmaletic/cs69995-PC/papers/Uwano06.pdf.
11
Ver “Eye Movements in Code Reading: Relaxing the Linear Order” de Teresa Busjahn et al. (2015), https://
ieeexplore.ieee.org/document/7181454.
Machine Translated by Google
movimentos oculares para o código. Nos casos em que os programadores novatos não liam lin
antes, eles geralmente seguiam a pilha de chamadas em vez de ler de cima para baixo.
Busjahn não apenas comparou código a texto, ela também comparou programadores novatos
com programadores experientes. A comparação das práticas de leitura de código dos novatos com
as dos especialistas revelou uma diferença nas práticas de leitura de código dos dois grupos: os
novatos leem mais linearmente e seguem a pilha de chamadas com mais frequência do que os
programadores especialistas. Aprender a seguir a pilha de chamadas enquanto lê o código,
aparentemente, é uma prática que vem com a experiência.
Como há semelhanças cognitivas entre a leitura de código e a leitura de texto, é possível que as
estratégias de leitura de linguagem natural também sejam úteis na leitura de código. Esta seção
explora cada uma das sete estratégias conhecidas para leitura de texto no contexto de leitura de
código.
12 “Os sete hábitos dos leitores altamente eficazes”, por Kathy Ann Mills, 2008, https://www.researchgate.net/
publication/27474121_The_Seven_Habits_of_Highly_Effective_Readers .
Machine Translated by Google
EXERCÍCIO 5.4 Estude um trecho de código desconhecido por um período fixo de tempo
(digamos, 5 ou 10 minutos, dependendo da duração). Depois de estudar o código por
esse período fixo de tempo, tente responder às seguintes perguntas concretas sobre ele:
ÿ Qual foi o primeiro elemento (variável, classe, conceito de programação e assim por diante)
que chamou sua
atenção? ÿ Por que isso?
ÿ Qual foi a segunda coisa que você notou? ÿ
Por quê? ÿ Essas duas coisas (variáveis,
classes, conceitos de programação) estão relacionadas? ÿ Quais conceitos estão
presentes no código? Você conhece todos eles? ÿ Quais elementos sintáticos estão
presentes no código? Você conhece todos eles? ÿ Quais conceitos de domínio estão
presentes no código? Você conhece todos eles?
5.5.2 Monitoramento
Ao ler o código, é importante acompanhar o que você está lendo e se você o entende. Mantenha
uma nota mental não apenas do que você entende, mas também do que você acha confuso.
Uma boa estratégia é imprimir o código e marcar as linhas que você entende e as que te
confundem. Você pode fazer isso com ícones como os que você usou para marcar os papéis das
variáveis.
A Figura 5.5 mostra uma parte do código JavaScript que anotei dessa maneira — uso uma
marca de verificação para o código que entendo e um ponto de interrogação para linhas ou
partes de linhas que me confundem. Monitorar sua compreensão dessa forma o ajudará quando
você ler o código pela segunda vez, pois poderá se concentrar nas linhas confusas com mais
profundidade.
Anotações marcando o que o confunde podem ser muito úteis para monitorar sua própria
compreensão, mas também podem ajudar na busca de ajuda. Se você puder comunicar
claramente quais partes do código são confusas, você pode pedir ao autor uma explicação sobre
certas linhas. Isso será mais eficaz do que se você disser: “Não tenho ideia do que isso faz”.
Machine Translated by Google
O QUE É UMA LINHA DE CÓDIGO IMPORTANTE? Você pode se perguntar o que é importante
linha de código é, e essa é uma ótima pergunta! Muitas vezes fiz o exercício de
marcando importantes linhas de código com uma equipe de desenvolvimento, onde cada equipe
membro marca individualmente o que eles acham que são as linhas mais importantes e
a equipe compara as notas depois.
Machine Translated by Google
Não é incomum que as pessoas em uma equipe discordem sobre quais linhas são
importante. Algumas pessoas argumentam que as linhas onde os cálculos mais intensivos
acontecem são as linhas mais importantes, enquanto outras pessoas selecionam uma
import para uma biblioteca relevante ou um comentário explicativo. Pessoas
com experiência em diferentes linguagens de programação ou domínios podem
têm ideias diferentes sobre a importância de determinadas linhas de código e
isso está ok. Não pense nisso como um desacordo a ser resolvido, mas uma oportunidade
aprender.
O bom de fazer este exercício com sua equipe é que não apenas
ensiná-lo sobre o código, mas também sobre você e seus colegas de equipe (prioridades,
experiência e assim por diante).
Um exercício para isso é percorrer o código linha por linha e criar uma lista de todos os
nomes de identificadores (variáveis, classes, métodos, funções). Isso é algo que você pode fazer
mesmo quando você está totalmente confuso sobre o que o código faz. Pode parecer um pouco estranho
analisar o código tão mecanicamente, mas ler todos os identificadores ajudará seu trabalho
memória. Depois de se concentrar nos nomes, você pode pesquisar em seu LTM por
em formação. A informação encontrada suporta a memória de trabalho para processar
o código com mais facilidade.
Depois de criar a lista de identificadores, você pode usá-la para obter uma
compreensão do código. Por exemplo, você pode dividir os nomes das variáveis em dois
diferentes categorias: nomes de variáveis que estão relacionadas ao domínio do código, como
como Cliente ou Pacote, e nomes de variáveis relacionados à programação
conceitos, como Árvore ou Lista. Alguns nomes de variáveis combinam ambas as categorias, como
CustomerList ou FactorySet. Outros não podem ser entendidos sem contexto, o que
significa que você precisará gastar mais esforço para investigar seu significado - por exemplo,
tentando decidir qual papel eles desempenham usando a estrutura de Sajaniemi, discutida anteriormente
neste capítulo.
EXERCÍCIO 5.6 Selecione uma parte do código-fonte e crie meticulosamente uma lista de
todos os nomes de variáveis presentes no programa.
Machine Translated by Google
ÿ Existem nomes que são ambíguos quando vistos sem contexto? ÿ Que significado
podem ter nomes ambíguos nesta base de código?
5.5.5 Visualizando
Nos capítulos anteriores, você viu várias técnicas para visualizar código para obter uma visão mais profunda
compreensão, incluindo a criação de uma tabela de estados e o rastreamento do fluxo de um pedaço de código.
Existem várias outras estratégias de visualização que podem ser usadas para entender
código. Uma técnica que pode ser útil para códigos muito complexos, dos quais uma análise mais profunda
compreensão é necessária é listar todas as operações nas quais as variáveis estão envolvidas.
TABELAS DE OPERAÇÃO
Ao trabalhar com código desconhecido, às vezes pode ser difícil prever como o
os valores das variáveis mudarão à medida que o código for executado. Quando o código é muito difícil de
processar imediatamente, pode ser útil criar uma tabela de operações. Por exemplo, o seguinte
O trecho de código JavaScript pode ser difícil de entender se você não souber que uma função zip mescla duas
listas.
Listagem de código JavaScript 5.1 que compacta listas como e bs usando uma determinada função f
Nesse caso, pode ajudar a inspecionar as variáveis, métodos e funções e determinar em quais operações eles
estão envolvidos. Por exemplo, f é aplicado a as[i] e
Machine Translated by Google
bs[i], então é uma função. Inspecionando as e bs, vemos que eles estão sendo
indexados, então essas variáveis devem ser listas ou dicionários. Depois de determinar
os tipos de variáveis em um código complexo por meio de suas operações, pode ser
mais fácil determinar suas funções.
Depois de criar esta tabela, leia o código novamente. O preenchimento da tabela ajudou você a
obter uma compreensão mais profunda dos papéis das variáveis e do significado do problema
como um todo?
5.5.6 Questionamento
Fazer perguntas a si mesmo enquanto lê o código o ajudará a entender os objetivos e a funcionalidade do
código. Nas seções anteriores, você viu vários exemplos de perguntas que você pode fazer sobre código.
Algumas perguntas mais valiosas são as seguintes:
ÿ Quais são os cinco conceitos mais centrais do código? Podem ser nomes de identificadores,
temas, classes ou informações encontradas nos comentários. ÿ Que estratégias você utilizou
para identificar os conceitos centrais? Por exemplo, você olhou para nomes de métodos,
documentação ou nomes de variáveis ou se baseou em seu conhecimento prévio de um
sistema?
ÿ Quais são os cinco conceitos de ciência da computação mais centrais no código? Podem ser
algoritmos, estruturas de dados, suposições ou técnicas. ÿ O que você pode determinar sobre
as decisões tomadas pelo(s) criador(es) do código – por exemplo, a decisão de implementar uma
determinada versão de um algoritmo, usar um determinado padrão de projeto ou usar uma
determinada biblioteca ou API? ÿ Em que suposições essas decisões se baseiam? ÿ Quais
são os benefícios dessas decisões?
Essas perguntas são mais profundas do que o conhecimento da estrutura do texto e podem ajudá-lo a chegar
a um plano para entender o código.
Machine Translated by Google
EXERCÍCIO 5.8 Resuma um trecho de código preenchendo a tabela a seguir. Claro, você
pode adicionar mais informações ao resumo do que sugeri neste exercício.
Origem
Resumo ÿ
Ao ler um código desconhecido, descobrir quais funções as variáveis desempenham, como
stepper ou valor mais procurado, pode ajudar a aprofundar sua compreensão. ÿ Quando
se trata de entender o código, existe uma diferença entre o conhecimento da estrutura do
texto, que significa conhecer os conceitos sintáticos usados no código, e o conhecimento
do plano, que significa entender as intenções do criador do código. ÿ Existem muitas
semelhanças entre ler código e ler linguagem natural, e sua capacidade de aprender uma
linguagem natural pode ser um preditor de sua capacidade de aprender a programar.
ÿ Estratégias que são comumente usadas para auxiliar na compreensão profunda de textos em
linguagem natural, como visualização e resumo, também podem ser aplicadas para obter uma
compreensão mais profunda do código.
Machine Translated by Google
Melhorar na resolução
problemas de programação
Nos capítulos anteriores, você aprendeu sobre diferentes processos cognitivos ativos em
o cérebro ao programar. Exploramos como as informações são armazenadas brevemente no
STM durante a leitura do código e como as informações são recuperadas do LTM quando ele
precisa ser aplicado. Também discutimos a memória de trabalho, que está ativa quando
91
Machine Translated by Google
Usar modelos explícitos de código ao resolver um problema tem dois benefícios. Primeiro, os modelos
podem ajudar a comunicar informações sobre programas a outras pessoas. Se eu criei um
diagrama de estado, posso mostrar a outra pessoa todos os valores intermediários das variáveis para
Machine Translated by Google
N N2 B$ N1
1 LET N2 = ABS (INT (N))
2 LET B$ =
"" Iniciar 77 7
3 PARA N1 = N2 PARA 0 PASSO 0 Ciclo 1 3 31
4 LET N2 = INT (N1 / 2)
5
Ciclo 2
LET B$ = STR$ (N1 - N2 * 2) + B$
6 DEIXE N1 = N2
7 PRÓXIMO N1
8 IMPRIMIR B$
9 DEVOLUÇÃO
Figura 6.1 Um programa BASIC que converte o número N em uma representação binária. Você pode usar
um auxílio de memória como a tabela de estado parcial mostrada aqui para ajudá-lo a entender como o programa funciona.
ajudá-los a entender como o código funciona. Isso é especialmente útil com sistemas maiores. Por exemplo, se
olharmos juntos para um diagrama de arquitetura de código, posso apontar
classes e os relacionamentos entre elas e objetos que de outra forma seriam
invisível ou oculto no código.
Um segundo benefício dos modelos é que eles podem ajudá-lo a resolver problemas. Quando você é
perto do limite de itens que você pode processar de uma só vez, fazer um modelo pode ser uma maneira útil
para diminuir a carga cognitiva. Assim como uma criança pode somar 3+5 usando uma reta numérica (uma espécie de
modelo) em vez de fazer o cálculo em sua cabeça, os programadores podem mapear
a arquitetura de um sistema em um quadro branco porque é muito difícil manter todos os elementos de uma grande
base de código na memória de trabalho.
Os modelos podem ser uma grande ajuda na resolução de problemas porque ajudam o LTM a identificar
memórias relevantes. Muitas vezes, os modelos têm restrições; por exemplo, apenas um diagrama de estado
mostra os valores das variáveis e um diagrama entidade-relacionamento mostra apenas classes e
seus relacionamentos. Concentrar-se em uma determinada parte de um problema pode apoiar seu pensamento
sobre uma solução, e essas restrições forçam você a fazer exatamente isso. Pensar na linha numérica ao somar
ajuda a colocar o foco na atividade de contar, e uma entidade
diagrama de relacionamento força você a pensar sobre quais entidades ou classes seu sistema
consistem e como se relacionam entre si.
Nem todos os modelos que podemos usar para pensar sobre problemas são iguais, no entanto. Como programadores,
sabemos a importância da representação e seus efeitos na resolução de problemas. Por exemplo, dividir um número
por dois é trivial quando convertemos o
número para uma representação binária - nós simplesmente precisamos deslocar os bits para a direita por um.
Embora este seja um exemplo relativamente simples, existem vários problemas em que a representação influencia a
estratégia de solução.
A importância da representação pode ser bem ilustrada com mais profundidade pelo seguinte problema relativo
a um pássaro e dois trens. Um pássaro está sentado em um trem prestes a
deixar Cambridge, Inglaterra, em direção a Londres. Assim que esse trem parte, um segundo trem
parte de Londres, a 50 milhas de distância. O pássaro decola e voa em direção ao segundo
Machine Translated by Google
trem a uma velocidade de 75 milhas por hora. Ambos os trens estão viajando a 50 milhas por hora.
Quando o pássaro chega ao segundo trem, ele se vira e voa de volta para o primeiro, e continua
fazendo isso até que os dois trens se encontrem. A que distância o pássaro voou quando os trens
se encontraram?
A primeira intuição de muitas pessoas é pensar nos trens e como o pássaro se move
entre eles (figura 6.2).
Figura 6.2 Modelando a distância que um pássaro percorre entre dois trens em movimento, a
partir da visão do pássaro. Isso leva a uma solução correta, mas muito complexa, envolvendo
o cálculo das localizações de ambos os trens.
Modelar a trajetória do pássaro é uma solução correta, mas envolve um conjunto complicado de
equações que a maioria das pessoas preferiria evitar. Uma solução mais fácil se concentra no
próprio pássaro e é o seguinte. Os trens se encontrarão no meio entre Londres e Cambridge após
30 minutos. Nesse ponto, ambos os trens viajaram 25 milhas.
Como o pássaro voa a uma velocidade de 75 milhas por hora, depois de meia hora, o pássaro
voou 37,5 milhas. Esta é uma boa ilustração do fato de que a maneira como você pensa sobre um
problema pode afetar fortemente a maneira como você o resolve e o esforço necessário para fazê-lo.
Na programação, também trabalhamos com diferentes representações de problemas. Algumas
linguagens de programação limitam o número de representações possíveis, o que pode ser útil e
prejudicial na resolução de problemas. Por exemplo, uma linguagem como APL é perfeita para
modelar soluções que envolvem matrizes, mas pode ser difícil de usar para problemas que
precisam de uma representação diferente. Java, por outro lado, tem a opção de criar classes para
representar todos os tipos de problemas, então você pode usar Java para problemas envolvendo
matrizes, mas precisará fazer o trabalho extra de criar uma classe de matrizes. Em Java, é mais
provável que você busque uma solução envolvendo dois loops for aninhados, porque eles são
incorporados e comumente usados.
Modelos mentais 95
também são modelos que você pode usar ao pensar em um problema que não é feito
explícito fora do seu cérebro. São os chamados modelos mentais.
Na seção anterior você viu que a representação que você usa para resolver um problema
influencia como você pensa sobre o problema. O mesmo vale para os modelos mentais:
alguns apoiam o pensamento melhor do que outros. Nesta seção, exploraremos o que mental
modelos são e como você pode usá-los explicitamente ao resolver problemas.
Um exemplo de modelo mental que usamos para código é pensar em atravessar uma árvore.
No código e no computador, é claro, não há uma árvore real que atravessamos; lá
são apenas valores na memória que pensamos como uma estrutura de árvore. Este modelo nos ajuda a
raciocinar sobre o código. É mais fácil pensar nos “filhos desse nó” do que
pense sobre “os elementos aos quais esse elemento se refere”.
Os modelos mentais foram inicialmente descritos pelo filósofo escocês Kenneth Craik em
seu livro de 1943 The Nature of Explanation. Craik descreveu os modelos mentais como
“modelos em escala” de fenômenos na natureza. De acordo com Craik, as pessoas usam modelos mentais
para prever, raciocinar e explicar o mundo ao seu redor.
A definição de modelo mental que eu mais gosto é esta: um modelo mental criado
uma abstração em sua memória de trabalho que você pode usar para raciocinar sobre o
problema em mãos.
Ao interagir com computadores, criamos muitos tipos de modelos mentais. Por
Por exemplo, ao pensar em um sistema de arquivos, você pode pensar em um grupo de arquivos em uma pasta
juntos. Claro, quando você pensa sobre isso um pouco mais profundamente, você sabe que há
não são realmente arquivos ou pastas em seu disco rígido; um disco rígido contém apenas zeros e
uns. Mas quando pensamos nesses zeros e uns, pensamos neles como sendo organizados nessas estruturas.
EXERCÍCIO 6.1 Considere um trecho de código que você usou nos últimos dias. O que
modelos mentais desse código que você usou durante a programação? Será que aqueles
modelos mentais dizem respeito ao computador ou execução de código ou outros aspectos da
programação?
Machine Translated by Google
Tabela 6.1 Principais características dos modelos mentais, combinados com exemplos em programação
Característica Exemplo
Os modelos mentais são incompletos. Um modelo mental não Pensar em uma variável como uma caixa que contém um valor
precisa ser um modelo completo do sistema-alvo, assim como um não suporta adequadamente o pensamento sobre reatribuição. O
modelo em escala simplifica o objeto físico que modela de segundo valor caberá na caixa com o primeiro valor? Ou o primeiro
algumas maneiras. Um modelo mental incompleto pode ser útil valor será empurrado para fora?
para seu titular se abstrair os detalhes irrelevantes.
Os modelos mentais são instáveis. Os modelos mentais não Quando aprendemos a programar, é útil pensar em uma variável
precisam permanecer os mesmos para sempre; na verdade, como uma caixa que contém um valor. No entanto, depois de um
eles muitas vezes mudam durante o período em que estão em tempo, percebemos que uma variável não pode conter mais de um
uso. Por exemplo, se você criar um modelo mental de eletricidade valor, então uma etiqueta de nome se torna uma analogia melhor.
como o fluxo de água, poderá imaginá-lo inicialmente como um rio
reto, mas como um rio que se alarga e se estreita quando aprende
mais sobre como a eletricidade flui. Os detentores de um modelo
mental também podem esquecer partes do modelo quando não
se envolvem suficientemente com ele.
Vários modelos mentais podem coexistir, mesmo que sejam Você pode pensar em uma variável como uma caixa contendo
inconsistentes entre si. Os novatos em particular muitas vezes um valor. Alternativamente, você pode pensar em uma variável como
têm modelos mentais “localmente coerentes, mas globalmente uma tag de nome que você anexa a um valor.
inconsistentes”, que tendem a estar intimamente ligados aos Ambos os modelos mentais podem existir ao mesmo tempo e podem
detalhes do caso particular que estão considerando.
ter benefícios em diferentes situações.
Modelos mentais podem ser “estranhos” e até mesmo parecer Você já pediu a um computador para fazer algo, como “Por favor,
superstições. Muitas vezes as pessoas acreditam em coisas que trabalhe desta vez?” Mesmo sabendo que um computador não é um
realmente não fazem sentido. ser senciente e não pode ouvi-lo, você ainda pode ter um modelo
mental de um computador como uma entidade que pode decidir agir
a seu favor.
As pessoas são frugais ao usar modelos mentais. Por exemplo, ao depurar, muitos programadores preferem fazer
Como o cérebro consome muita energia, as pessoas normalmente pequenas alterações em seu código (ajustes) e executá-lo novamente
preferem fazer um trabalho físico extra se isso economizar esforço para ver se o bug foi corrigido, em vez de gastar energia para criar
mental. um bom modelo mental do problema.
a Gentner, Dedre. (2002). Psicologia dos modelos mentais. Em NJ Smelser & PB Bates (Eds.), Enciclopédia
Internacional das Ciências Sociais e Comportamentais (pp. 9683-9687), Elsevier.
Machine Translated by Google
Modelos mentais 97
Ao aprender a programar, as pessoas geralmente aprendem novos modelos mentais gradualmente. Por
Por exemplo, inicialmente você pode pensar em um documento no disco rígido como uma folha de papel
física real com palavras armazenadas em algum lugar, enquanto mais tarde você aprende que
o disco rígido só pode armazenar zeros e uns. Ou você pode primeiro pensar em uma variável e
seu valor como nome e número de telefone em um catálogo de endereços, atualize-o
modelo quando você aprender mais sobre como funciona a memória do computador. Você pode
pense que quando você aprende como algo funciona com mais profundidade, o velho modelo mental
“errado” é removido de seu cérebro e substituído por um melhor. No entanto, em capítulos anteriores vimos
que não é provável que essa informação desapareça
completamente do LTM. Isso significa que há sempre o risco de você recorrer
modelos mentais incorretos ou incompletos que você aprendeu anteriormente. Múltiplos modelos mentais
podem permanecer ativos simultaneamente, e os limites entre os modelos não são
sempre claro. Então, especialmente em uma situação de alta carga cognitiva, você pode de repente
usar um modelo antigo.
Como exemplo de modelos mentais concorrentes, considere este enigma: o que acontece com
um boneco de neve se você o vestir com um suéter bonito e quente? Será que vai derreter mais rápido ou mais lento do que
seria sem um suéter?
Seu primeiro pensamento pode ser que o boneco de neve derreterá mais rápido, já que seu cérebro
imediatamente busca o modelo mental de um suéter sendo uma coisa que fornece
cordialidade. Mas após uma análise mais aprofundada, você concluirá que um suéter não
dá calor, mas ajuda a manter o calor do nosso corpo. Porque o suéter isola,
ele manterá o frio que o boneco de neve perde, então o boneco de neve realmente derreterá
mais lento, não mais rápido.
EXERCÍCIO 6.2 Pense em dois modelos mentais que você conhece para um único conceito de
programação, como variáveis, loops, armazenamento de arquivos ou gerenciamento de memória.
Quais são as semelhanças e diferenças entre as duas mentes?
modelos?
O primeiro livro sobre modelos mentais que saiu em 1983 foi de Philip Johnson-Laird, um
professor de psicologia em Princeton. Johnson-Laird argumentou que os modelos mentais são
usados enquanto raciocinam e, portanto, vivem na memória de trabalho. Em seu livro, ele descreve uma
estudo onde ele e um colega investigaram o uso desses modelos. Os participantes foram
conjuntos de sentenças que descrevem uma mesa: por exemplo, “A colher está para o
direita do garfo” e “O prato está à direita da faca”. Após ouvir as descrições, os participantes foram solicitados a
realizar algumas tarefas não relacionadas. Em seguida, foram dadas
quatro descrições de arranjos de mesa e perguntou qual das descrições era mais semelhante à que havia
recebido.
Das descrições que os participantes puderam escolher, duas foram completamente
falso, um era o verdadeiro que eles tinham ouvido, e um era uma descrição que poderia ser
inferido do layout. Por exemplo, de “A faca está à esquerda do garfo” e
“O garfo está à esquerda do prato” pode-se inferir que o prato está à direita da faca
sem ser dito explicitamente. Os participantes foram então solicitados a classificar as quatro descrições,
começando com aquele que melhor representa a descrição que eles receberam.
Eles geralmente classificavam tanto as descrições reais quanto as inferidas mais altas do que as descrições.
dois falsos, dos quais os pesquisadores concluíram que os participantes fizeram uma
modelo em suas mentes da configuração da mesa, que eles usaram para selecionar a resposta certa.
O que podemos aprender com o trabalho de Johnson-Laird para melhorar o pensamento sobre programação
é que ter um modelo abstrato de código é útil: ele permite que você raciocine sobre
o modelo em si, em vez de confiar na referência ao código, o que seria menos
eficiente.
Mais adiante neste capítulo, exploraremos como criar deliberadamente modelos mentais quando
raciocínio sobre o código. Mas primeiro precisamos discutir um aspecto do estudo de Johnson-Laird
ainda não cobrimos. Seu experimento teve uma reviravolta interessante!
Machine Translated by Google
Modelos mentais 99
carga, para que você possa se concentrar na criação de um modelo mental maior com mais facilidade.
Em segundo lugar, esses modelos menores podem ser os blocos de construção de um modelo mental
maior. Por exemplo, um gráfico de dependência pode apontar para algumas linhas de código fortemente
conectadas que podem desempenhar um papel importante na formação de um modelo mental.
3 Responda a perguntas sobre o sistema e use as respostas para refinar seu modelo.
Agora você pode tentar responder a perguntas sobre o sistema com o qual está trabalhando usando
o modelo mental que construiu nas etapas 1 e 2 e verificá-las no código. Quais são as perguntas certas
depende do sistema em questão, mas algumas perguntas genéricas que geralmente funcionam são
as seguintes: a Quais são os elementos mais importantes (classes, objetos, páginas) do sistema? Eles
estão presentes no modelo?
Por exemplo, uma pessoa pode armazenar um modelo mental de como os líquidos fluem, que é usado ao
despejar leite em um copo. Como o modelo é genérico, as pessoas saberão que, ao despejar a massa de
panqueca em uma tigela, o líquido pode se comportar um pouco diferente do leite, porque é mais espesso,
mas ainda se encaixa no modelo mental de como o líquido flui.
Como isso se aplica à programação? Na programação, você pode armazenar uma representação abstrata
de como a travessia de árvore funciona: há uma raiz na qual você inicia e, em seguida, você pode explorar a
largura primeiro, observando todos os filhos de um determinado nó, ou a profundidade primeiro, onde você
escolha um filho e explore seus filhos até que você não possa prosseguir. Quando você encontra um programa
que trabalha com árvores, pode invocar o modelo mental genérico de árvores.
Machine Translated by Google
Uma maneira que abordamos de expandir o conhecimento armazenado em seu LTM é o uso
de flashcards. Os flashcards que discutimos no capítulo 3 tinham um conceito de programação
de um lado e o código correspondente do outro. Se você deseja armazenar mais modelos mentais
para usar ao raciocinar sobre código, também pode usar flashcards. No entanto, eles conterão
informações diferentes agora. O objetivo desta segunda forma de flashcards não é ampliar seu
conhecimento de conceitos sintáticos, mas ampliar seu vocabulário de modelos mentais ou
maneiras de pensar sobre código. Coloque o nome de um modelo mental em um lado do cartão
(o prompt) e uma breve explicação ou visualização do modelo mental do outro lado.
Quais modelos mentais você usará para pensar sobre código depende em parte do domínio,
da linguagem de programação e da arquitetura do código. No entanto, algumas coisas geralmente
valem a pena usar:
ÿ Estruturas de dados, como gráficos direcionados e não direcionados e diferentes formas de
listas ÿ Padrões de projeto, como o padrão observador ÿ Padrões arquitetônicos, como Model–
View–Controller ÿ Diagramas, como diagramas de relacionamento de entidade ou diagramas de
sequência ÿ Ferramentas de modelagem, como diagramas de estado ou redes de Petri
Há duas maneiras de usar um conjunto de flashcards de modelos mentais. Você pode usá-los
para testar seus conhecimentos, da mesma forma que os cartões de sintaxe: leia um prompt e
verifique se você conhece a explicação correspondente. Conforme explicado anteriormente,
sempre que você encontrar um padrão com o qual não está familiarizado, poderá adicionar uma
carta ao seu baralho de modelos mentais.
Uma segunda maneira de usar o baralho é ao ler o código com o qual você está lutando.
Percorra seu baralho de modelos mentais e decida para cada um se ele pode se aplicar ao código
em questão.
Por exemplo, se você escolher um cartão sobre árvores, pergunte a si mesmo: “Posso pensar
nesse código na forma de uma árvore?” Se esse padrão se aplicar, você pode começar a criar
um modelo inicial baseado neste cartão. Para uma árvore, isso significa decidir quais nós, folhas
e arestas estariam presentes no modelo e o que eles podem representar.
Machine Translated by Google
EXERCÍCIO 6.3 Crie um baralho inicial de flashcards com modelos mentais que
pode ser útil para o código em que você está trabalhando. Escreva o nome do mental
modelo de um lado como um prompt e uma explicação do modelo do outro
lateral. Anexe a explicação com perguntas a serem feitas ao aplicar este
modelo. Por exemplo, para uma árvore, você modelará nós, folhas e arestas, de modo que um
A pergunta inicial poderia ser “Que pedaços de código podem ser representados como folhas?”
Da mesma forma, para usar o modelo mental de uma tabela de estados, você precisará criar uma lista
de variáveis, então uma pergunta inicial poderia ser "Quais são as variáveis?"
Você também pode fazer este exercício juntos como uma equipe e aprender uns com os outros
modelos. Ter um vocabulário compartilhado de modelos mentais pode facilitar muito a comunicação
sobre código.
1
Para uma visão geral, veja “Toward a Unified Theory of Reasoning” de Johnson-Laird e Khemlani, https://www.
.sciencedirect.com/science/article/pii/B9780124071872000010.
Machine Translated by Google
linguagem de programação, ainda que incompleta, como acabamos de ver. Máquinas nocionais,
portanto, diferem dos modelos mentais de um programador, o que pode estar errado
ou inconsistente.
A maneira mais clara que encontrei para entender a diferença entre uma máquina nocional e um
modelo mental é que uma máquina nocional é uma explicação de como um computador funciona.
Quando você internalizou a máquina nocional e pode usá-la com facilidade, a máquina nocional se
torna seu modelo mental. Quanto mais você aprende sobre uma linguagem de programação, mais
próximo seu modelo mental fica da máquina nocional.
A ideia da máquina nocional foi concebida por Ben du Boulay, professor da Universidade de Sussex,
enquanto trabalhava no Logo na década de 1970. Logo era uma linguagem de programação
educacional projetada por Seymour Papert e Cynthia Solomon. Foi a primeira linguagem a introduzir
a tartaruga: uma entidade que pode desenhar linhas e ser controlada por código. O nome vem da
palavra grega logos, que significa palavra ou pensamento.
Machine Translated by Google
du Boulay usou pela primeira vez o termo “máquina nocional” para descrever sua estratégia para ensinar
Logo a crianças e professores. Ele o descreveu como “o modelo idealizado do computador implícito nas
construções da linguagem de programação”. du Boulay's
as explicações incluíam visualizações feitas à mão, mas usavam principalmente analogias.
Por exemplo, du Boulay usou um operário de fábrica como analogia para o modelo de execução da
linguagem. O trabalhador é capaz de executar comandos e funções, e
tem ouvidos com os quais pode ouvir valores de parâmetros, uma boca que fala saídas e
mãos que realizam as ações descritas pelo código. Esta representação de conceitos de programação começou
simples, mas foi gradualmente construída para explicar a totalidade de
a linguagem Logo, incluindo comandos internos, procedimentos e funções definidos pelo usuário, chamadas de
subprocedimentos e recursão.
Como você viu, máquinas fictícias são projetadas para explicar o funcionamento de um
máquina executando código e, portanto, eles também compartilham algumas características com
máquinas. Por exemplo, como uma máquina física, uma máquina nocional tem a noção de
"Estado." Quando pensamos em uma variável como uma caixa, essa caixa virtual e nocional pode estar vazia ou
tem um valor “dentro” dele.
Existem também outras formas de máquinas fictícias que não estão tão ligadas ao hardware.
Você provavelmente usa representações abstratas da máquina que é executada quando você lê
e escrever código. Por exemplo, quando pensamos em como os cálculos funcionam em linguagens de
programação, muitas vezes comparamos o funcionamento do computador ao de um matemático. Considere
esta expressão em Java como um exemplo:
Quando você tem que prever o valor de fahrenheit, você provavelmente usará uma forma de substituição: você
substitui a variável celsius na segunda linha por seu valor, 10 .
passo, você pode adicionar mentalmente colchetes para indicar a precedência do operador:
Você já viu alguns exemplos de máquinas fictícias agora. Alguns operam no nível da linguagem de
programação e abstraem todos os detalhes da máquina subjacente, como a máquina nocional de
substituição.
Outras máquinas nocionais, como representar uma pilha como uma pilha física de papéis, são
mais representativas de como a máquina física executa o programa. Ao usar máquinas nocionais
como uma forma de explicar e entender os conceitos de programação, pode ser útil pensar
propositalmente em quais detalhes a máquina nocional esconde e expõe. A Figura 6.4 mostra uma
visão geral de quatro níveis diferentes de abstração nos quais uma máquina nocional
pode operar, incluindo um exemplo por nível. Por exemplo, “variáveis como caixas” desempenha um
papel no nível da linguagem de programação e no nível do compilador/intérprete, mas abstrai detalhes
sobre o código compilado e o sistema operacional.
Figura 6.4 Diferentes níveis em que uma máquina nocional pode usar abstrações.
Por exemplo, a máquina nocional “cálculo como substituição” que vimos abstrai tudo além da
linguagem de programação, enquanto representa threads como a colaboração humana se
concentra no funcionamento do sistema operacional.
Pode ser importante perceber quais detalhes você está ignorando ao raciocinar sobre o código.
Embora abstrair detalhes seja uma ótima maneira de obter uma compreensão de código de alto nível,
algumas formas de pensar sobre código podem abstrair detalhes relevantes.
EXERCÍCIO 6.4 Liste três exemplos de máquinas fictícias e os níveis em que operam.
Preencha a tabela a seguir listando máquinas nocionais e selecionando seu nível de
abstração.
EXERCÍCIO 6.5 Liste mais três exemplos de linguagem que usamos sobre programação
que indicam o uso de uma certa máquina nocional e levam a uma certa
modelo mental.
25
print_square(5) print(quadrado(5))
Figura 6.6 Duas máquinas nocionais para funções. À esquerda está uma máquina que suporta apenas parâmetros de
entrada e à direita está um modelo estendido que também incorpora parâmetros de saída.
2
“Thinking Out of the Box: Comparing Variables in Programming Education”, 2018, https://dl.acm.org/doi/
10.1145/3265757.3265765 .
Machine Translated by Google
um rótulo, como a temperatura ou a idade de uma pessoa. A outra metade, o grupo “caixa”, recebeu
uma aula em que explicamos as variáveis como sendo caixas, como um cofrinho ou uma caixa de
sapatos. Usamos consistentemente a mesma metáfora em ambas as lições; por exemplo, usamos a
frase “x contém 5?” para o grupo de caixas e “x é 5?” para o grupo de rótulos.
Após esta lição introdutória, os participantes foram testados em sua compreensão de programação.
Isso incluiu perguntas simples sobre variáveis com uma variável, mas também incluímos perguntas
em que uma variável foi definida duas vezes para investigar se os participantes entenderam que uma
variável pode conter apenas um valor.
Nossos resultados mostraram claramente que ambas as metáforas para variáveis têm seus
benefícios e desvantagens. Para questões simples relacionadas a variáveis com uma atribuição, o
grupo caixa se saiu melhor. Assumimos que pensar em uma caixa é uma coisa fácil de fazer, porque
as pessoas colocam coisas em caixas o tempo todo. Portanto, visualizar o conceito de uma caixa para
armazenamento de uma variável pode ajudá-los a entender a ideia. No entanto, quando analisamos
quantos participantes pensavam que uma variável poderia conter dois valores, descobrimos que as
pessoas do grupo da caixa provavelmente sofreriam com esse equívoco.
A conclusão importante de nosso estudo é que você deve ter cuidado ao descrever conceitos de
programação e o funcionamento correspondente do computador em termos de objetos e operações
no mundo real. Embora essas metáforas possam ser valiosas, elas também podem criar confusão,
especialmente porque modelos mentais antigos podem permanecer no LTM e, ocasionalmente,
aparecer na memória de trabalho.
EXERCÍCIO 6.6 Pense em uma máquina fictícia que você costuma usar ao raciocinar
sobre código ou ao explicar código. Quais são algumas das desvantagens ou limitações
dos modelos mentais que essa máquina nocional cria?
Esquemas são maneiras pelas quais o LTM armazena informações. Por exemplo, a ideia de uma
caixa é muito provavelmente um conceito com o qual as pessoas têm fortes associações. Colocar algo
em uma caixa, recuperá-lo mais tarde e abri-lo apenas para ver o que está dentro são operações
prováveis com as quais as pessoas estão familiarizadas. Como tal, pensar em uma variável como uma
caixa não causa carga cognitiva extra. Se disséssemos “Uma variável é como um monociclo”, isso
seria muito menos útil, precisamente porque a maioria das pessoas não tem um modelo mental forte
de todas as operações que um monociclo suporta.
O que você pode presumir que as pessoas saberão não é fixo no tempo e no lugar, é claro.
Ao explicar um conceito, portanto, é importante escolher uma comparação com a qual a pessoa para
quem você está explicando esteja familiarizada. Por exemplo, ao explicar a funcionalidade de um
computador para crianças na zona rural da Índia, alguns educadores usaram
Machine Translated by Google
Resumo 109
elefantes como computadores e seus treinadores como programadores, pois esse é um princípio
familiar para as crianças.
Resumo ÿ
Como você representa um problema pode influenciar fortemente a maneira como você pensa sobre ele.
Por exemplo, pensar nos clientes como uma lista em vez de uma coleção pode influenciar
como você armazena e analisa os objetos do cliente.
ÿ Modelos mentais são representações mentais que formamos enquanto pensamos em problemas.
As pessoas podem ter vários modelos mentais que podem competir entre si. ÿ Máquinas
nocionais são versões abstratas de como um computador real funciona que são usadas para
explicar conceitos de programação e raciocinar sobre programação.
Equívocos:
Erros no pensamento
Nos últimos capítulos, abordamos técnicas para pensar sobre código, como
criando visualizações, usando frameworks para suportar a memória de trabalho e
usando modelos mentais para ajudar a resolver problemas de código. Não importa quão úteis
sejam as técnicas que usamos para apoiar nossos cérebros, às vezes cometemos erros em
pensando em código.
110
Machine Translated by Google
Por que aprender uma segunda linguagem de programação é mais fácil do que aprender a primeira 111
O foco deste capítulo são os bugs. Às vezes, os bugs são o resultado de desleixo, por
Por exemplo, quando você esquece de fechar um arquivo ou comete um erro de digitação em um nome de arquivo. Mais frequentemente,
no entanto, os bugs são o resultado de um erro de pensamento. Você pode não saber que um arquivo
precisa ser fechado após o uso, ou você pode supor que a linguagem de programação
fecha o arquivo para você automaticamente.
Neste capítulo, primeiro exploraremos o tópico de aprender programação múltipla
línguas. Existem muitas fontes possíveis de suposições errôneas quando você aprende
uma nova linguagem, sendo que diferentes linguagens de programação têm convenções diferentes para vários
conceitos. Python, por exemplo, fechará um arquivo após um bloco começando com open() sem exigir uma
instrução file.close() explícita , mas em C você
sempre tem que usar fclose(). Na primeira parte deste capítulo, mostrarei como melhor
use seu conhecimento existente para aprender novas linguagens de programação, bem como
evitar frustrações e erros resultantes de diferenças entre idiomas.
A segunda parte do capítulo ensina sobre suposições errôneas sobre
código. Abordaremos vários equívocos específicos de programação e nos aprofundaremos
as origens dos equívocos. Estar ciente de quais equívocos você pode ter
sobre o código irá ajudá-lo a detectar erros mais cedo e até mesmo evitar alguns.
Conforme mostrado na figura 7.1, quando seu LTM é pesquisado, as informações relacionadas ao
novas informações aprendidas podem ser encontradas. Se existirem informações relacionadas com o
novas informações, são retransmitidas para a memória de trabalho. Essas informações podem incluir
memórias procedurais, esquemas, planos ou memórias episódicas.
Machine Translated by Google
Memória de curto
Memória de
prazo longo prazo
Novo
Novo Relacionado
em formação
em formação
SM Trabalhando
memória
Novo
Relacionado
Figura 7.1 Quando você aprende uma nova informação, ela é processada primeiro pela memória sensorial (SM) e
depois pelo STM. As novas informações são posteriormente enviadas para a memória de trabalho, onde você
pode pensar sobre elas. Ao mesmo tempo, o LTM é pesquisado por informações relacionadas. Se forem
encontradas informações relacionadas, elas também são enviadas para a memória de trabalho para apoiar o pensamento sobre as novas inform
Por exemplo, quando você já conhece Java e está aprendendo sobre métodos em Python,
você pode ser lembrado de métodos em Java. Isso irá ajudá-lo a aprender o Python
métodos mais rapidamente, embora funcionem um pouco diferente dos métodos em Java.
No capítulo 3, falamos sobre o uso da elaboração ao aprender um novo conceito.
Elaboração é a prática de relacionar explicitamente novas informações com coisas que você já
conhecer. A razão pela qual a elaboração funciona bem é que pesquisar explicitamente em seu LTM por
informações relacionadas aumentam as chances de serem encontradas informações relevantes que
pode ajudá-lo a realizar a tarefa em mãos. Portanto, a elaboração pode ajudar a aumentar
transferência durante o aprendizado.
EXERCÍCIO 7.1 Pense em um novo conceito de programação ou biblioteca que você aprendeu
recentemente. Quais conceitos que você já conhecia ajudaram você a aprender o novo conceito?
A segunda maneira pela qual o conhecimento armazenado em seu LTM pode apoiar o aprendizado é chamado de transferência
de aprendizagem. A transferência de aprendizado acontece quando você pode aplicar coisas que já sabe
em situações totalmente desconhecidas. Quando as pessoas estão falando sobre ciência cognitiva e
usar o termo “transferência”, eles quase sempre significam transferência de aprendizado.
Às vezes, a transferência de aprendizado acontece sem que você pense nisso. Por
Por exemplo, quando você compra um novo par de calças, não precisa pensar em como
feche seu botão. Você apenas sabe como fazê-lo, mesmo que essas calças em particular e seus
botão não lhe são familiares. Da mesma forma, quando você compra um novo laptop, você sabe como é
O teclado funciona sem pensar, mesmo que você nunca tenha operado esse tipo específico de laptop antes.
A transferência de aprendizado também pode acontecer conscientemente, como quando
você aprende uma nova linguagem de programação. Se você está aprendendo JavaScript e já
conhece Python, você pode pensar explicitamente: “Eu sei que preciso recuar o corpo de um loop
em Python; isso seria verdade para JavaScript também?”
Machine Translated by Google
Por que aprender uma segunda linguagem de programação é mais fácil do que aprender a primeira 113
ÿ Domínio — Quão bem você dominou a tarefa para a qual o conhecimento já está armazenado em
seu LTM. Quanto melhor você conhecer uma tarefa, maior a probabilidade de aplicá-la em outro
domínio. Por exemplo, um programador especialista em Java provavelmente se beneficiará mais
de seu conhecimento prévio ao aprender Python do que um programador Java iniciante. Como
vimos nos capítulos anteriores, um programador especialista tem um arsenal maior de estratégias,
blocos e modelos mentais que podem ser aplicados a problemas em qualquer linguagem de
programação diferente.
ÿ Semelhança — Semelhanças entre as duas tarefas. Por exemplo, se você estiver implementando
um algoritmo que já conhece em uma linguagem de programação desconhecida, será mais fácil
do que implementar um novo algoritmo em uma nova linguagem de programação.
ÿ Contexto — Quão semelhantes são os ambientes. Não é apenas a semelhança entre as tarefas que
importa, mas também o contexto em que você as está executando.
Por exemplo, a transferência entre duas linguagens de programação é mais provável se você
programar essas linguagens diferentes no mesmo IDE, o que é um forte argumento para usar um
IDE para vários idiomas. No entanto, o contexto se estende além do computador; também pode
importar se você está sentado no mesmo escritório com as mesmas pessoas. Quanto mais
semelhanças houver, mais provável é que o conhecimento seja transferido. ÿ Atributos críticos—
Quão claro está para você qual conhecimento pode ser benéfico. Se alguém apontar que conhecer
JavaScript pode ser benéfico para aprender Python, é mais provável que você procure ativamente por
semelhanças. Portanto, pode ser importante procurar ativamente por semelhanças e refletir sobre
elas antes de aprender uma nova linguagem de programação ou estrutura. Que conhecimento
existente poderia ajudar com a nova tarefa?
ÿ Associação—Com que intensidade você sente que as tarefas são semelhantes. Por exemplo, Java
e JavaScript soam semelhantes, embora conceitualmente as linguagens não sejam tão parecidas.
Portanto, pode existir uma associação mais forte em seu LTM entre Java e JavaScript do que entre
Python e Scala. Sua memória episódica,
Machine Translated by Google
que armazena memórias de coisas que você experimentou, também pode desempenhar um papel. Por
exemplo, se você aprendeu Java e C# na mesma sala de aula, pode conectá-los mais fortemente do que se
os tivesse aprendido em configurações diferentes. ÿ Emoções—Como você se sente em relação à tarefa.
Suas emoções também podem desempenhar um papel na probabilidade de transferência. Por exemplo, se você
achar prazeroso trabalhar com árvores binárias e uma nova tarefa o lembrar disso, você pode tentar aplicar
estratégias semelhantes à nova tarefa de forma mais ativa.
Anteriormente, você aprendeu que, se dois domínios forem mais semelhantes, isso afetará a quantidade de
transferência. Observar a distância entre os domínios é outra maneira de dividir diferentes formas de transferência. A
transferência próxima ocorre quando o conhecimento é transferido de domínios que são vistos como próximos, como
cálculo e álgebra ou C# e Java.
Falamos de transferência distante quando as habilidades são transferidas entre domínios muito diferentes, como
latim e lógica, ou Java e Prolog. Como a semelhança é um fator que influencia a transferência, a transferência para
longe é muito menos provável de acontecer do que a transferência para perto.
EXERCÍCIO 7.2 Pense em algumas situações em que você passou por uma transferência.
Que forma de transferência ocorreu? Preencha a tabela a seguir para orientar seu pensamento.
Machine Translated by Google
Por que aprender uma segunda linguagem de programação é mais fácil do que aprender a primeira 115
Quando ocorre uma transferência positiva, você não precisa criar um novo modelo mental
do princípio; em vez disso, seu cérebro recebe ajuda na construção de um modelo mental de um novo
situação, baseando-a em modelos mentais que o LTM já mantém para outros domínios. Por
Por exemplo, quando você conhece Java, você já tem um modelo mental de um loop e sabe
que um loop tem uma variável de contador, um corpo e uma condição de parada. Em quase todos os novos
linguagem de programação que você encontrar, os loops também terão esses aspectos, então você sabe
o que procurar. Isso ajuda você a criar um novo modelo mental. No entanto, como você pode
experimentaram, a transferência nem sempre é positiva. Quando o conhecimento existente impede
você de aprender algo novo, chamamos isso de transferência negativa. Edsger W. Dijkstra,
Professor holandês de ciência da computação e criador do algoritmo de Dijkstra, disse
que ensinar BASIC deveria ser proibido porque “aleija a mente”.
Embora eu não acredite de forma alguma que os cérebros possam ser arruinados para sempre aprendendo um certo
linguagem de programação, há alguma verdade neste ditado porque os erros podem ser
causados por suposições erradas sobre o código. Suposições erradas podem, por sua vez, ser causadas
por transferência negativa. Por exemplo, em Java, as variáveis não podem ser usadas sem inicialização. Um
programador Java experiente pode supor que em Python também, todas as variáveis
deve ser inicializado e que o compilador irá avisá-los se eles esquecerem. Isso pode causar
confusão e levar a bugs.
Mesmo em linguagens muito semelhantes, como Java e C#, há riscos de transferência negativa
porque os modelos mentais de ambas as línguas são semelhantes, mas não idênticos. Por exemplo, Java tem
um conceito chamado exceções verificadas, que são exceções verificadas em tempo de compilação. Se você não
encapsular essas exceções em um bloco try-catch , o código falhará em
compilar. As exceções verificadas são um recurso de linguagem específico do Java, portanto, as pessoas que vêm
do C# podem não perceber que são diferentes do que estão acostumados. Não apenas faça
eles têm o modelo mental errado, mas acham que têm o certo!
Machine Translated by Google
Esquecer de inicializar uma variável ou manipular uma exceção são relativamente pequenos
erros que podem ser facilmente corrigidos. Mas há exemplos de transferência negativa que correm
Deeper. Por exemplo, muitas pessoas têm dificuldade em aprender uma linguagem funcional
como F# quando já estão familiarizados com linguagens orientadas a objetos porque as funções existem em
ambos os paradigmas, mas funcionam de forma diferente.
EXERCÍCIO 7.3 Pense em uma situação em que você fez uma suposição incorreta
sobre um conceito de linguagem em uma linguagem de programação. Poderia ser isso
atribuída à transferência negativa de uma língua para outra?
No entanto, esta seção mostrou que a transferência distante, digamos, de algo como SQL para
JavaScript, não é tão provável, e você provavelmente precisará aprender muito
sintaxe, bem como novas estratégias para recuperar seu nível de especialista em uma nova linguagem de
programação. As práticas também podem diferir; por exemplo, muito do que você sabe sobre reutilização
e a abstração em JavaScript deve ser considerada de maneira bastante diferente em SQL.
Prestar atenção deliberada às semelhanças e diferenças facilitará a tarefa de
aprendendo um novo idioma.
EXERCÍCIO 7.4 Considere uma linguagem de programação que você está aprendendo ou uma que você
quero aprender. Compare-o com o(s) idioma(s) que você já conhece. O que é semelhante
e o que é diferente?
Sintaxe
Tipo de sistema
Conceitos de programação
Tempo de execução
Ambiente de programação/
IDE
Ambiente/práticas de teste
Pense em uma situação em que você criou um bug, por exemplo, esquecendo de
inicialize uma instância da maneira correta, chamando a função errada ou com um erro off-by one em uma
lista. Situações em que ocorrem bugs podem ser causadas por um simples deslize do
lembre-se de onde você acidentalmente esqueceu algum código, selecionou o método errado ou fez um
erro ao calcular um valor limite.
No entanto, os bugs também podem ter uma razão subjacente mais profunda, onde você cometeu um erro
suposição sobre o código em mãos. Talvez você tenha contado com o fato de que a instância
seria inicializado em outro lugar no código, ou talvez você tivesse certeza de que era o correto
método ou assumiu que a estrutura de dados impediria você de acessar elementos
Machine Translated by Google
fora de seus limites. Quando você tem certeza de que seu código funcionará, mas ainda falha, é
provável que você esteja sofrendo de um equívoco.
Na conversa normal, a palavra “equívoco” é frequentemente usada como sinônimo de erro ou
confusão, mas a definição formal é um pouco diferente. Para que uma crença seja um equívoco, ela
deve ÿ ser defeituosa, ÿ ser mantida de forma consistente, em diferentes situações, e ÿ ser mantida
com confiança.
Existem muitos equívocos comuns. Por exemplo, muitas pessoas pensam que as sementes de pimenta
são a parte mais picante. As sementes de pimentão não são nada picantes!
Este é um exemplo de um equívoco porque
2 Se as pessoas acreditam que as sementes de um tipo de pimenta são picantes, elas vão acreditar
sementes de todos os tipos de pimenta são picantes, e
3 Eles acreditam que é verdade e agem de acordo, por exemplo, removendo as sementes das pimentas antes de
cozinhá-las.
O equívoco “sementes de pimentas são picantes” é o resultado de uma lenda urbana que as pessoas
simplesmente repetiram umas às outras, mas a transferência negativa muitas vezes desempenha um
papel na criação de equívocos. Por exemplo, muitas pessoas acreditam que a carne selando vai “selar
os sucos” porque as superfícies externas de outras formas de alimentos, como ovos, solidificam
quando aquecidas. Essas pessoas supõem que o calor sempre cria um “escudo” sólido, que será
impenetrável pela umidade presa no interior. O conhecimento de um tipo de alimento é incorretamente
transferido para outro tipo de alimento, levando a um equívoco – de fato, a carne selada leva a uma
maior perda líquida de umidade.
Equívocos comumente ocorrem na programação também. Novos programadores algumas vezes
assumem que uma variável, como temperatura, pode conter apenas um valor que não pode ser
alterado. Embora essa suposição possa parecer absurda para um programador experiente, há razões
pelas quais assumir que uma variável pode conter apenas um valor é sensato. Por exemplo, essa
suposição pode ser transferida do conhecimento prévio de matemática, onde as variáveis de fato não
mudam dentro do escopo de uma prova matemática ou
exercício.
Outra fonte desse equívoco está no domínio da própria programação.
Já vi alunos com exposição prévia a arquivos e sistemas de arquivos transferirem incorretamente
crenças sobre arquivos para variáveis. Como os sistemas operacionais normalmente permitem que
você crie apenas um arquivo com um determinado nome (em uma pasta), os alunos podem assumir
incorretamente que a variável temperature já está em uso e não pode conter um segundo valor, assim
como um nome de arquivo só pode ser usado para um arquivo.
mente sobre eles. Muitas vezes, não é suficiente apontar a falha em seu pensamento.
Em vez disso, para mudar um equívoco, a maneira de pensar defeituosa precisa ser substituída
com uma nova forma de pensar. Ou seja, não basta dizer a um programador iniciante que um
variável pode ser alterada; eles precisam obter uma nova compreensão da variável como um
conceito.
O processo de substituir um equívoco baseado em uma linguagem de programação que você
já sabe com o modelo mental certo para o novo idioma que você está aprendendo é
chamada mudança conceitual. Nesse paradigma, uma concepção existente é fundamentalmente
alterado, substituído ou assimilado pelo novo conhecimento. É essa mudança no conhecimento, mais do que a
adição de novos conhecimentos a um esquema já existente, que
distingue a mudança conceitual de outros tipos de aprendizagem.
O fato de que o conhecimento que você já aprendeu precisa ser mudado em sua
O LTM torna o aprendizado de mudança conceitual mais difícil do que o aprendizado regular. É por isso que
equívocos podem durar muito tempo; simplesmente receber informações sobre por que seu pensamento está errado
muitas vezes não ajuda ou não ajuda o suficiente.
Portanto, você terá que gastar muita energia quando aprender uma nova linguagem de programação
“desaprendendo” o conhecimento existente sobre linguagens de programação anteriores.
Por exemplo, se você está aprendendo Python quando já conhece Java, terá que
desaprender alguma sintaxe, assim você deve sempre definir os tipos de variáveis. Você também vai
tem que desaprender algumas práticas, como confiar nos tipos de variáveis para tomar decisões
em código. Mesmo que não seja difícil lembrar o simples fato de que Python é tipado dinamicamente, aprender a
pensar sobre tipos enquanto você está programando pode levar mais tempo porque requer mudança conceitual.
provavelmente era que ele iria derreter mais rápido, afinal, colocar um suéter faz
um mais quente, certo? Bem, não para um boneco de neve. Ao usar um suéter, o boneco de neve é
isolado, prendendo no frio e retardando o processo de fusão.
O que provavelmente aconteceu é que seu cérebro ativou instantaneamente uma concepção existente:
suéteres deixam você mais quente. Essa concepção, que é correta nas pessoas (de sangue quente), foi então
erroneamente transferida para a situação do boneco de neve. Isso não é porque
você não é uma pessoa inteligente!
Foi por muito tempo assumido que quando as pessoas aprendem como as coisas funcionam, velhas, incorretas
as noções são apagadas definitivamente de suas memórias e substituídas por noções melhores e mais corretas.
Sabendo o que sabemos sobre o cérebro, isso é improvável. As memórias são
agora assumido para não ser esquecido ou substituído; em vez disso, nossa recuperação deles diminui
hora extra. No entanto, as velhas memórias de maneiras erradas de pensar ainda estão lá, e
caminhos para eles podem ser acionados, mesmo se preferirmos que não sejam.
A pesquisa mostrou que as pessoas muitas vezes recorrem a noções antigas, mesmo que possam
também trabalhar com sucesso com os corretos. O trabalho de Igal Galili e Varda Bar da
Machine Translated by Google
a Universidade Hebraica de Jerusalém mostrou que os alunos podiam trabalhar bem com
mecânica em exercícios familiares, mas regrediam a um raciocínio mais básico, mas errado,
para questões mais complicadas.1 Isso ilustra que várias noções podem estar presentes na
memória ao mesmo tempo, como no boneco de neve exemplo: podemos ter a ideia de que
um suéter significa calor em nossos cérebros, assim como a noção de que um suéter isola e,
portanto, mantém o frio. Essas ideias competem umas com as outras quando temos que
decidir se um suéter aquece um boneco de neve, então temos que suprimir ativamente a
ideia mais antiga de que suéter é igual a calor para chegar à conclusão certa. Isso
provavelmente aconteceu quando você pensou no quebra-cabeça e experimentou um
momento de “espere um minuto” em que raciocinou em vez de reagir intuitivamente.
Não sabemos exatamente como o cérebro decide qual dos conceitos armazenados usar,
mas sabemos que a inibição desempenha um papel. Normalmente associamos a inibição a
um sentimento de autoconsciência, contenção ou timidez. No entanto, pesquisas recentes
começaram a indicar que, quando mecanismos de controle inibitórios estão ativos, uma
concepção incorreta pode perder a concorrência com uma concepção correta.
EXERCÍCIO 7.5 Pense em uma situação em que você teve um conceito errado sobre um
conceito em uma determinada linguagem de programação. Por exemplo, eu assumi por um
tempo embaraçosamente longo que todas as linguagens funcionais usavam avaliação
preguiçosa e que todas as linguagens preguiçosas deveriam ser funcionais porque eu só
conhecia uma linguagem preguiçosa e funcional: Haskell. Que equívoco você manteve por
muito tempo, e qual foi sua origem?
1
Veja “Movimento Implica Força: Onde Esperar Vestígios do Equívoco?” por Igal Gaili e Varda Bar, 1992,
https://www.tandfonline.com/doi/abs/10.1080/0950069920140107.
2
Consulte a tabela A-1 (pp. 3593–68) de “Visual Program Simulation in Introductory Programming Education”,
http://lib.tkk.fi/Diss/2012/isbn9789526046266/isbn9789526046266.pdf.
Machine Translated by Google
def quadrado(número):
retornar número * número
número = 12
print(quadrado(número))
Machine Translated by Google
Esse código também ocorre na vida real. Por exemplo, quando você usa a extração
funcionalidade de método em um IDE, a maioria dos IDEs replicará o nome da variável tanto
ao definir e chamar a função. Portanto, o código real é preenchido com esse padrão
também, o que pode ser o motivo pelo qual os educadores o usam. Este equívoco é interessante
exemplo de um equívoco que transfere dentro de uma linguagem de programação
em vez de ser influenciado pelo conhecimento prévio de matemática ou inglês. Algumas vezes, quando
entendemos um conceito específico de uma linguagem de programação, o
conhecimento que adquirimos sobre essa parte não se transfere nem mesmo para outros conceitos
dentro da mesma língua.
Resumo 123
Especialmente para programadores experientes (ou especialistas em qualquer coisa), pode ser
difícil perceber que o erro é seu, então sempre verifique as suposições que você faz sobre o código
executando-o ou fazendo uso de um conjunto de testes. Se você tem certeza de que um determinado
valor nunca pode ser inferior a zero, por que não adicionar um teste para verificá-lo? Esse teste pode
não apenas ajudá-lo a detectar se sua suposição está errada, mas também serve como documentação
do fato de que o valor de fato sempre será positivo. O teste pode comunicar essa informação a você no
futuro, o que é importante porque, como vimos, os equívocos raramente desaparecem e sempre podem
ressurgir, mesmo quando aprendemos o modelo correto.
Como isso sugere, a documentação é uma terceira maneira de combater equívocos sobre um
determinado método, função ou estrutura de dados dentro de uma base de código. Se você descobriu
um equívoco, além de adicionar um teste, você também pode querer adicionar documentação em locais
relevantes para evitar que você e outras pessoas caiam na mesma armadilha.
Resumo ÿ O
conhecimento que você já armazenou em seu LTM pode ser transferido para novas situações.
Às vezes, o conhecimento existente ajuda você a aprender mais rápido ou a realizar novas
tarefas melhor. Isso é chamado de transferência positiva.
ÿ A transferência de conhecimento de um domínio para outro também pode ser negativa, o que
acontece quando o conhecimento existente interfere no aprendizado de coisas novas ou na
execução de novas tarefas. ÿ Você pode usar a transferência positiva para aprender coisas
novas de forma mais eficaz pesquisando ativamente informações relacionadas em seu LTM (por
exemplo, por elaboração, conforme abordado anteriormente neste livro).
ÿ Você pode ter equívocos, que ocorrem quando você tem certeza de que está certo, mas na
verdade está errado. ÿ Os equívocos nem sempre são resolvidos simplesmente percebendo ou
sendo informado de que você está errado. Para que os equívocos sejam corrigidos, você precisa de
um novo modelo mental para substituir o antigo modelo errado.
ÿ Mesmo que você tenha aprendido um modelo correto, sempre existe o risco de você voltar a usar
o equívoco. ÿ Use testes e documentação em uma base de código para ajudar a evitar equívocos.
Machine Translated by Google
Machine Translated by Google
Parte 3
Dentro
partes 1 e 2, examinamos os papéis da memória de curto prazo, da
memória de longo prazo e da memória de trabalho ao ler e pensar sobre código.
Na parte 3, movemos nossa atenção para escrever código melhor: como escrever código
que seja compreensível e evite nomes vagos e cheiros de código. Também discutiremos
como melhorar suas habilidades em escrever código para problemas complexos.
Machine Translated by Google
Machine Translated by Google
127
Machine Translated by Google
Este capítulo tem como objetivo estudar como nomear melhor as coisas no código, como variáveis, classes,
e métodos. Como agora sabemos bastante sobre como o cérebro processa o código,
pode entender mais profundamente por que a nomenclatura é tão importante para a compreensão do código.
Bons nomes ajudam a ativar seu LTM para encontrar informações relevantes que você já conhece
sobre o domínio do código. Nomes ruins, por outro lado, podem fazer com que você faça
suposições sobre o código, levando a equívocos.
Embora nomear seja importante, também é muito difícil. Os nomes são frequentemente criados enquanto
modelar uma solução ou resolver um problema. Durante tal atividade, é provável que você esteja
experimentando uma alta carga cognitiva: sua memória de trabalho está em plena capacidade para criar um
modelo mental e usar o modelo mental para raciocinar. Nessa situação, pensar
um bom nome de variável pode simplesmente causar muita carga cognitiva, que seu cérebro
quer prevenir. Como tal, faz sentido, de uma perspectiva cognitiva, escolher um
nome ou um nome de espaço reservado para não exceder a capacidade da memória de trabalho.
Este capítulo cobre a importância, mas também a dificuldade de nomear em profundidade.
Uma vez que tenhamos coberto os conceitos básicos de nomenclatura e processamento cognitivo, ampliaremos
no efeito de nomes em dois aspectos diferentes da programação. Vamos estudar primeiro
que tipos de nomes tornam o código mais fácil de compreender. Em segundo lugar, veremos o
efeito de nomes ruins na ocorrência de bugs. Fechamos o capítulo com concreto
orientações para a criação de grandes nomes.
A primeira razão pela qual os nomes de variáveis importam é que na maioria das bases de código
uma grande parte do que você lerá serão nomes. Por exemplo, no código-fonte do Eclipse, que
consiste em cerca de dois milhões de linhas de código, 33% dos tokens e 72% dos caracteres são
dedicados a identificadores.1
Além de sua ocorrência frequente no código, os programadores falam muito sobre nomes. Miltiadis
Allamanis, agora pesquisador da Microsoft Research em Cambridge, investigou com que frequência
os nomes de identificadores são mencionados nas revisões de código. Para esse fim, Allamanis
analisou mais de 170 comentários com mais de 1.000 comentários neles. Ele descobriu que uma em
cada quatro revisões de código continha observações relacionadas à nomenclatura e que as
observações sobre nomes de identificadores ocorreram em 9%.
OS NOMES SÃO A FORMA DE DOCUMENTAÇÃO MAIS ACESSÍVEL
Embora a documentação formal do código possa conter muito mais informações básicas, os nomes
servem como um tipo importante de documentação porque estão disponíveis dentro da base de
código. Como vimos nos capítulos anteriores, reunir informações de diferentes lugares pode aumentar
a carga cognitiva. Como tal, ter que navegar fora de sua base de código para ler a documentação é
um ato que os programadores tentam evitar. Portanto, a “documentação” mais lida serão comentários
no código e nomes.
Nos capítulos anteriores, discutimos beacons, partes do código que ajudam um leitor desconhecido a
desvendar o significado do código. Os nomes de variáveis são sinalizadores importantes que ajudam
os leitores a entender o código além dos comentários.
Escolher um bom nome é importante. Muitos pesquisadores diferentes tentaram definir o que torna
um nome de variável bom ou ruim, e todos eles têm perspectivas diferentes sobre essa questão. Mas
antes de olharmos para essas diferentes perspectivas, vamos ativar seu LTM e analisar sua opinião
sobre os nomes das variáveis com um exercício.
EXERCÍCIO 8.1 O que define um bom nome de identificador na sua opinião? Você consegue
pensar em um exemplo de um bom nome?
O que define um nome ruim? Isso é simplesmente o oposto de um bom nome, ou você
consegue pensar em propriedades que caracterizam nomes ruins que você já viu? Você
conhece um exemplo de um nome ruim de sua prática?
1
Florian Deißenbock e Markus Pizka, “Concise and Consistent Naming,” https://www.cqse.eu/fileadmin/
content/news/publications/2005-concise-and-consistent-naming.pdf.
Machine Translated by Google
Agora que você pensou sobre o que faz um nome ser um bom nome, vamos dar uma olhada em três
diferentes perspectivas sobre boas práticas de nomeação por pesquisadores que estudam nomeação.
Algumas pessoas acreditam que existem várias regras baseadas na sintaxe de nomes que devem ser
segure. Por exemplo, Simon Butler, professor associado sênior da Open University em
o Reino Unido, criou uma lista de problemas com nomes de variáveis, conforme mostrado na tabela 8.1.
Anomalia de maiúsculas Os identificadores devem usar letras maiúsculas apropriadas. contador de páginas
Ordem de declaração do A menos que haja motivos claros para não fazê-lo, CardValue = {ACE, OITO,
identificador de enumeração os tipos de enumeração devem ser declarados em CINCO, QUATRO, JACK, REI...}
ordem alfabética.
Nome de identificador longo Nomes de identificador longos devem ser evitados sempre page_counter_
que possível. convertido_e_
valor_normalizado
Embora a lista de Butler contenha diferentes tipos de regras, a maioria é sintática. Por exemplo,
a regra “External underscores” afirma que os nomes não devem começar ou terminar com um
sublinhado. As regras de Butler também implicam na proibição dos sistemas de notação húngara, que
prescreveria nomes de variáveis como strName para uma variável que representa um nome
armazenado como uma string.
Embora as regras sobre a formação precisa de nomes de variáveis possam parecer um pouco mesquinhas,
nos capítulos anteriores vimos que informações desnecessárias no código podem causar carga cognitiva estranha e
distrair a compreensão do código, por isso é sensato também
têm regras sintáticas como as da tabela 8.1.
Muitas linguagens de programação, é claro, têm convenções sobre como formatar
nomes de variáveis, como PEP8 em Python, que prescreve o caso de cobra para nomes de variáveis,
e a convenção de nomenclatura Java, que afirma que os nomes das variáveis devem ser camel case.
Outra perspectiva sobre uma boa nomenclatura é a consistência. Allamanis, cujo trabalho em código
revisões e nomenclatura que abordamos no início do capítulo, também pensou em boas
nomes. Ele afirma que o aspecto mais importante de um bom esquema de nomenclatura é semelhante
execução em uma base de código.
A objeção contra práticas inconsistentes de nomenclatura se encaixa com o que sabemos sobre ciência
cognitiva. Se a mesma palavra for usada para objetos semelhantes em uma base de código, ela será
mais fácil para o cérebro encontrar informações relacionadas relevantes armazenadas no LTM. Simão
concorda parcialmente com a visão de Allamanis; sua lista contém uma regra dizendo que os nomes devem
não use letras maiúsculas de forma inconsistente.
EXERCÍCIO 8.2 Selecione um trecho de código no qual você trabalhou recentemente. Faça uma lista
de todos os nomes de variáveis que ocorrem nesse pedaço de código. Agora reflita sobre o
qualidade desses nomes, dadas as três perspectivas delineadas. são os nomes
sintaticamente claro? Eles consistem em palavras? Eles são usados de forma consistente em
sua base de código?
Para responder a essas perguntas, Lawrie analisou um total de 186 versões de 78 bases de código
escrito em C++, C, Fortran e Java. Juntas, essas versões tinham mais de 48 milhões de linhas
de código e durou três décadas. O conjunto analisado por Lawrie continha tanto código proprietário quanto projetos
de código aberto, incluindo bases de código conhecidas como Apache,
Eclipse, mysql e gcc e samba.
Para analisar a qualidade dos nomes de identificadores, Lawrie investiga dois aspectos das práticas de
nomenclatura. Primeiro, ela verificou se os nomes dividiam palavras dentro de nomes, por exemplo, usando
sublinhados entre as palavras ou usando maiúsculas. Lawrie argumenta que os nomes
que as palavras divididas são mais fáceis de entender. Em segundo lugar, ela olhou se as palavras
que ocorrem dentro de nomes de variáveis ocorrem no dicionário, seguindo a regra de Butler que
nomes devem consistir em palavras.
Como Lawrie estudou diferentes versões da mesma base de código ao longo do tempo, ela
poderia analisar como as práticas de nomenclatura mudam ao longo do tempo. Ela olhou como nomear
a qualidade mudou ao longo do tempo em todas as 78 bases de código combinadas e descobriu que o código
moderno usa identificadores que consistem em palavras de dicionário em nomes mais antigos
código, usando mais palavras do dicionário e mais comumente dividindo as palavras
dentro dos nomes das variáveis. Lawrie atribui essas práticas aprimoradas de nomenclatura ao amadurecimento da
programação como disciplina. O tamanho da base de código não mostrou correlação com
qualidade, então bases de código maiores não fazem melhor (ou pior) quando se trata de qualidade
de nomes de identificadores.
Lawrie não apenas comparou as bases de código mais antigas com as mais novas; ela também olhou
versões anteriores da mesma base de código para ver se as práticas de nomenclatura mudaram ao longo do tempo
dentro de uma base de código. Ela descobriu que dentro de uma única base de código, a nomenclatura não melhora
à medida que o código envelhece. Lawrie tira uma conclusão importante e acionável aqui, dizendo que “a qualidade
do identificador ocorre no início do desenvolvimento de um programa”. Então, quando você
iniciar um novo projeto, você pode querer ter um cuidado extra na escolha de bons nomes,
porque a maneira como você cria nomes nos estágios iniciais de um projeto provavelmente será
a forma como os nomes serão criados para sempre.
A pesquisa sobre o uso de testes no GitHub encontrou um fenômeno semelhante: novos contribuidores
para um repositório, muitas vezes analisam os testes existentes e os modificam em vez de ler o
diretrizes do projeto.3 Quando um repositório tem testes implementados, os novos contribuidores se sentem
obrigados a também adicionar testes e, como tal, cumprir a forma como o projeto está organizado.
2
Dawn Lawrie, Henry Field e David Binkley, “Quantifying Identifier Quality: AN. Análise de Tendências”,
http://www.cs.loyola.edu/~lawrie/papers/lawrieJese07.pdf.
3
Raphael Pham et al., “Criando uma compreensão compartilhada da cultura de teste em um site de codificação social”, 2013,
http://etc.leif.me/papers/Pham2013.pdf.
Machine Translated by Google
Até agora neste capítulo, vimos duas perspectivas diferentes sobre a nomenclatura, como mostrado em
tabela 8.2.
investigador Perspectiva
A perspectiva de Butler é que podemos seguir várias diretrizes principalmente sintáticas para escolher
os nomes certos. Allamanis, por outro lado, não prescreve regras ou diretrizes fixas
na qualidade dos nomes, mas assume a posição de que a base de código deve liderar e
que consistente e ruim é melhor do que bom, mas inconsistente. Seria ótimo se houvesse
era uma maneira clara de nomear identificadores em código, mas o fato de que mesmo pesquisadores
opiniões variadas sublinha que o que é um bom nome está nos olhos de quem vê.
Tabela 8.3 Diferentes perspectivas sobre nomeação e sua conexão com a cognição
A perspectiva de Butler também se encaixa com o que sabemos sobre processamento cognitivo. Ele
promove o uso de nomes que sejam sintaticamente semelhantes, por exemplo, não permitindo sublinhados
iniciais e usando maiúsculas consistentes. Nomes semelhantes também podem
diminuir sua carga cognitiva ao ler os nomes porque a informação relevante é
apresentado da mesma forma todas as vezes. Limite de Butler de quatro palavras em um identificador
Machine Translated by Google
name, embora aparentemente um pouco aleatório, se encaixa no limite da memória de trabalho, que
agora é estimada entre dois e seis pedaços.
Em seu artigo sobre Naturalize, os autores compartilham uma linda história de quando
usaram o Naturalize para gerar um pull request para Junit. Este pull request não foi aceito,
pois de acordo com os desenvolvedores, a mudança proposta não era consistente com a
base de código. Naturalize poderia então indicá-los para todos os outros locais onde essa
convenção foi violada, causando a sugestão.
Sua própria convenção foi quebrada com tanta frequência que, para Naturalize, parecia
que a versão errada era mais natural!
Memória de
Memória de
curto prazo
longo prazo
lista
Nome da variável de
Conhecimento
em em
name_ctr_list nomes nomes g, nomes
contadores , de
contadores e listas e
listas
SM Memória de
trabalho
name_ctr_list
Conhecimento
em em
nomes g, nomes
contadores , de
contadores e listas e
listas
Mas nomear, é claro, é mais do que selecionar a sintaxe correta para um nome de variável. O
as palavras que escolhemos também importam, especialmente de uma perspectiva cognitiva. Vimos anteriormente em
o livro que ao pensar em código, a memória de trabalho processa dois tipos de
em formação. Isso é ilustrado na figura 8.1. Primeiro, o nome da variável é processado por
memória sensorial e, em seguida, é enviado para o STM. Sabemos que o STM é limitado em tamanho
e, portanto, tenta fazer com que os nomes das variáveis separem as palavras. Quanto mais sistemática
nomes são formatados, mais provável é que o STM possa identificar partes individuais.
Por exemplo, em um nome como nmcntravg, pode ser necessário um esforço considerável para encontrar e
entender os componentes. Alternativamente, em um nome como name_counter_average é
muito mais fácil ver o que o nome diz respeito. Apesar de ter aproximadamente o dobro de personagens, requer uma
fração do esforço mental quando você o lê.
Ao processar nomes de variáveis, não é apenas o STM que fornece informações
para a memória de trabalho. A memória de trabalho também recebe informações do seu LTM
depois de ter sido pesquisado por fatos relacionados. Para este segundo processo cognitivo, a escolha
de palavras no nome do identificador é importante. Usar o conceito de domínio correto para um nome ou classe de
variável pode ajudar o leitor de código a encontrar informações relevantes em seu LTM.
j
Domínio
Programação Convenções
conhecimento conceitos
n m
envio Mapa de hash
Figura 8.2 Três tipos de conhecimento armazenados em seu LTM podem ocorrer
em nomes de variáveis e podem ajudá-lo a entender os nomes: conhecimento de
domínio (como cliente ou remessa), conceitos de programação (como lista, árvore ou
mapa de hash) e convenções (por exemplo , i e j provavelmente serão contadores de loop e n e m serão dimensões).
Machine Translated by Google
Considerar como um nome de variável suportará tanto o STM quanto o LTM de um futuro leitor pode ser de grande
ajuda na escolha de nomes.
EXERCÍCIO 8.3 Selecione um trecho de código-fonte não muito familiar. Não deve ser
totalmente desconhecido, por exemplo, pode ser algum código em que você trabalhou há algum
tempo, ou um pedaço de código escrito por outra pessoa dentro de uma base de código em que
você também trabalha.
Percorra o código e anote uma lista de todos os nomes de identificadores no código: nomes de variáveis,
nomes de métodos e nomes de classes. Para cada um dos nomes, reflita sobre como o nome suporta seu
processamento cognitivo:
Portanto, a codificação não é um bom momento para pensar em nomes e melhorar sua qualidade. É melhor refletir
sobre a qualidade de nomenclatura fora do processo de codificação.
As revisões de código podem ser um bom momento para refletir sobre a qualidade do código de seus nomes de
identificadores. O Exercício 8.4 pode servir como uma lista de verificação para nomes a serem usados, que você pode
usar em revisões de código para direcionar a atenção especificamente para nomes no código.
EXERCÍCIO 8.4 Antes de iniciar a revisão do código, liste mecanicamente todos os nomes de
identificadores presentes no código alterado. Liste esses nomes fora do código, por exemplo,
em um quadro branco ou em um documento separado. Para cada um dos nomes de identificador,
responda a estas perguntas:
ÿ Sem saber nada sobre o código, está claro o que o nome significa? Por exemplo, você sabe o
significado das palavras em que esse nome consiste?
ÿ Algum dos nomes é ambíguo ou pouco claro? ÿ Os
nomes usam abreviações que podem confundir? ÿ Quais nomes são
semelhantes? Esses nomes semelhantes também se referem a objetos semelhantes em
o código?
Machine Translated by Google
4
Dawn Lawrie et al., “Effective Identifier Names for Comprehension and Memory”, 2007, https://www.research
gate.net/publication/220245890_Effective_identifier_names_for_comprehension_and_memory.
Machine Translated by Google
CUIDADO COM PREFIXOS E SUFIXOS Lawrie aconselha ter cuidado com o uso
de convenções de nomenclatura que envolvem prefixar ou sufixar um identificador.
Os resultados de Beniamini mostraram que diferentes linguagens de programação têm convenções bastante
diferentes para o uso de nomes de variáveis de uma única letra. Por exemplo, para Perl,
os três nomes de uma única letra mais usados são, em ordem, v, i e j, enquanto para JavaScript
os nomes de uma única letra mais comuns são i, e e d. A Figura 8.3 mostra o uso de todos os 26
letras nas cinco linguagens de programação diferentes Beniamini analisadas.
Beniamini não olhou apenas para a ocorrência de nomes de variáveis de uma única letra; ele
também estava interessado nas associações que os programadores têm para as letras. Para uma letra como eu,
a maioria dos programadores pensará em um iterador de loop, e x e y provavelmente serão coordenadas
em um avião, mas e as outras letras, como b, f, s ou t? Essas letras estão associadas a um significado comum
por muitos programadores? Conhecer as suposições que as pessoas fazem sobre nomes de variáveis pode
ajudá-lo a evitar equívocos e
também ajudam você a entender as maneiras como os outros estão confusos sobre o código.
Para obter uma compreensão dos tipos que os programadores associam às variáveis
nomes, Beniamini fez uma pesquisa com 96 programadores experientes, onde perguntou
para listar um ou mais tipos aos quais eles associariam variáveis de uma única letra. Como você puder
veja na figura 8.4, há pouco consenso sobre os tipos da maioria das letras. Exceções notáveis
são s, que é majoritariamente votado para ser uma string, c, que é majoritariamente um caractere, e i, j, k e n,
que são inteiros. Mas para todas as outras cartas, quase tudo vale.
Machine Translated by Google
Perl Javascript
aBCDeFGHIJKLMNopqRSTUVWxyZ aBCDeFGHIJKLMNopqRSTUVWxyZ
PHP C
aBCDeFGHIJKLMNopqRSTUVWxyZ aBCDeFGHIJKLMNopqRSTUVWxyZ
Java
outro
flutuador
boleano
90 array
char
Respostas 80 string
inteiro
70
60
50
40
30
20
10
0
aBCDeFGHIJKLMNopqRSTUVWxyZ
Figura 8.4 Tipos associados a variáveis de uma única letra
Machine Translated by Google
EXERCÍCIO 8.5 Escreva os tipos que você esperaria para todas as 26 letras
nomes de variáveis na tabela a seguir. Em seguida, compare as notas com sua equipe
membros. Existem cartas em que suas suposições diferem? Você pode encontrar
lugares em sua base de código onde essas letras são usadas como variáveis?
aj s
bk t
cl u
dmv
en w
Raposa
gp y
hq z
eu r
5
Dave Binkley, “To Camel Case or Under_Score”, 2009, https://ieeexplore.ieee.org/abstract/document/
5090039.
Machine Translated by Google
os dois estilos de identificador afetam a velocidade e a precisão com que as pessoas podem se adaptar
programas. No estudo de Binkley, participaram 135 pessoas, tanto programadores quanto não programadores.
Os participantes do estudo viram primeiro uma frase descrevendo o
variável, por exemplo, “Estende uma lista para uma tabela”. Após o estudo da sentença, os participantes
receberam quatro opções de múltipla escolha, das quais uma representava a sentença. Exemplos de opções
que os participantes podem escolher são
extendListAsTable, expandAliasTable, expandAliasTitle ou expandAliasTable.
Os resultados do estudo de Binkley mostram que o uso do camel case leva a uma maior precisão entre
programadores e não programadores. O modelo descobre que existe
uma chance 51,5% maior de selecionar a opção certa para identificadores escritos no
estilo de caixa de camelo. Mas há um custo para essa maior precisão: velocidade. Levou os participantes
meio segundo a mais para encontrar identificadores escritos em caixa de camelo.
6
Simon Butler, “Relating Identifier Naming Flaws and Code Quality: An Empirical Study”, 2009, http://
oro.open.ac.uk/17007/1/butler09wcreshort_latest.pdf.
Machine Translated by Google
Butler criou uma ferramenta para extrair nomes de variáveis do código Java e também detectar violações das
diretrizes de nomenclatura. Usando sua ferramenta, Butler localizou lugares na caixa oito
bases onde ocorreram estilos de nomenclatura ruins. Conforme explicado na seção 8.1, Butler procurou
tanto em questões estruturais de nomenclatura, como dois sublinhados consecutivos, quanto na
componentes dos nomes, como se eles ocorrem em um dicionário.
Butler então comparou os locais no código com um estilo de nomenclatura ruim com o
localizações de bugs, como encontrado com FindBugs, uma ferramenta que localiza possíveis locais de bugs
usando análise estática. Curiosamente, o estudo de Butler encontrou associações estatisticamente significativas
entre problemas de nomenclatura e qualidade do código. As descobertas de Butler sugerem que um mau
estilo de nomenclatura pode apontar para código que provavelmente está errado em oposição ao código que é
apenas difícil de ler, entender e manter.
Claro, a correlação entre os locais dos bugs e os locais dos nomes ruins
não implica necessariamente causalidade. Pode ser que tanto os bugs quanto os nomes ruins sejam
o resultado do código escrito por um programador novato ou desleixado. Também pode ser o caso
que o local onde os bugs ocorrem é onde os problemas complexos estão sendo resolvidos.
Esses problemas complexos podem estar relacionados aos erros de nomenclatura de outras maneiras. Como já
discutimos antes, talvez a carga cognitiva do programador tenha sido muito alta ao criar o código porque eles
estavam resolvendo um problema difícil. Pode ser também o caso de
o domínio do código é complexo e, portanto, criar um bom nome é
difícil, e a complexidade de pegar o nome certo levou à confusão e, portanto, a um bug.
Portanto, embora resolver problemas de nomenclatura não resolva ou impeça necessariamente
bugs, inspecionando sua base de código para encontrar locais onde ocorrem práticas ruins de nomenclatura
pode ajudá-lo a encontrar lugares onde o código pode ser melhorado e os bugs podem ser evitados. Essa é uma
razão extra para ir à caça de nomes ruins no código: melhorar
nomes podem resultar indiretamente em menos bugs, ou pelo menos tempos de correção mais curtos porque melhor
nomes tornarão o código mais fácil de compreender.
7
Dror G. Fietelson, “Como os desenvolvedores escolhem nomes”, https://www.cs.huji.ac.il/~feit/papers/Names20TSE.pdf.
Machine Translated by Google
receber por mês, os nomes da tabela 8.4 foram todos escolhidos. Os nomes também são normalizados,
então “max” pode ser “max” ou “maximum” e “benefício” pode ser “benefícios”.
A tabela lista os nomes em ordem do mais para o menos escolhido.
max_benefit
max_benefit_per_month
max_benefit_num
max_monthly_benefit
benefícios
benefícios_por_mês
max_num_of_benefit
max_month_benefit
núm_máximo_benefício
max_number_of_benefit
max_benefit_amount
max_acc_benefit
max_allowed_benefit
limite_benefício_mensal
Observar esses moldes nos ajuda a entender por que a chance de dois desenvolvedores escolherem o
mesmo nome de variável no estudo de Feitenson era tão baixa. A infinidade de nomes diferentes no
experimento foi principalmente de desenvolvedores usando diferentes moldes de nomes.
Embora todos esses nomes representem conceitualmente o mesmo valor, há muitas diferenças de
estilo. Os desenvolvedores no estudo de Feitelson não trabalharam todos na mesma base de código, mas
mesmo dentro da mesma base de código é provável que esses diferentes moldes ocorram.
Saber o que sabemos agora sobre carga cognitiva e LTM, usando diferentes moldes dentro de uma base
de código não é uma boa ideia.
Primeiro, em termos de carga cognitiva, procurar o conceito relevante no nome da variável (neste
caso, benefício) e em locais diferentes dentro do nome da variável adiciona carga cognitiva desnecessária
e estranha. A energia mental que você gastará procurando o conceito certo não pode ser gasta na
compreensão dos nomes. Anteriormente neste capítulo, vimos que as pessoas podem ser treinadas para
reconhecer variáveis em um determinado estilo, como camel case ou snake case. Embora nenhum estudo
tenha sido feito sobre moldes de nomes, as pessoas provavelmente ficarão melhores em reconhecer
variáveis escritas em um determinado molde quando as usarem com frequência.
Em segundo lugar, se os nomes das variáveis forem semelhantes, usar o mesmo molde provavelmente
tornará mais fácil para o seu LTM encontrar informações relacionadas. Por exemplo, max_benefit_amount
Machine Translated by Google
pode lembrá-lo do código que você escreveu antes para calcular o valor máximo de juros se essa
variável tiver o nome de max_interest_amount. Seu LTM terá mais dificuldade em lembrar um código
semelhante quando a variável envolvida for chamada de interest_maximum, mesmo que o cálculo seja
semelhante.
Como moldes semelhantes suportam melhor sua memória de trabalho e seu LTM, é aconselhável
concordar com um número limitado de moldes diferentes para usar em cada base de código.
Quando você inicia um projeto, concordar com os moldes pode ser um bom passo nessa direção.
Em uma base de código existente, você pode começar criando ou extraindo uma lista de nomes de
variáveis existentes para a base de código, ver quais moldes já estão em uso e decidir sobre esses
moldes daqui para frente.
EXERCÍCIO 8.6 Crie uma lista de nomes de variáveis e funções/métodos em parte de sua
base de código. Isso pode ser uma classe, todo o código em um arquivo ou todo o código
relacionado a um determinado recurso. Para cada um dos nomes, verifique qual molde eles
seguem usando a tabela a seguir. Nos nomes da tabela, X representa uma quantidade ou
valor como juros de IVA e Y representa um determinado filtro na quantidade, como por mês,
para um determinado cliente.
Discuta os resultados com sua equipe. Quais moldes são comumente usados? Algumas
variáveis podem ser escritas usando um molde diferente para alcançar mais consistência
em sua base de código?
max_X
max_X_per_Y
max_X_num
X_per_Y
max_num_of_X
max_Y_X
X_max_num
max_number_of_X
max_X_amount
max_acc_X
max_allowed_X
Y_X_limit
max_X
De outros
Machine Translated by Google
Vimos que os programadores costumam usar muitos moldes de nomes diferentes para os mesmos objetos,
enquanto o uso de moldes semelhantes pode ajudar na compreensão. Com base nessas descobertas,
Feitelson projetou um modelo de três etapas para ajudar os desenvolvedores a escolher nomes melhores:
Vamos explorar essas três etapas com mais detalhes. A primeira etapa, selecionar os conceitos a serem
incluídos no nome, é muito específica do domínio, e a decisão sobre quais dimensões incluir pode ser a
decisão mais importante na nomenclatura. O principal a se levar em consideração na hora de escolher quais
partes incluir no nome é, segundo Feitelson, a intenção do nome, que deve representar quais informações o
objeto contém e para que serve. Quando você sentir a necessidade de escrever um comentário para explicar
o nome, ou quando encontrar um comentário próximo a um nome no código que você usa, as palavras do
comentário provavelmente devem ser incluídas no nome da variável. Em alguns casos, pode ser importante
incluir também uma indicação de que tipo de informação se trata, por exemplo, que um comprimento está na
dimensão horizontal ou vertical, que um peso está armazenado em quilos ou que um buffer contém entrada
do usuário e, portanto, deve ser considerado inseguro. Às vezes, podemos até usar um novo nome quando
os dados são convertidos.
Por exemplo, após a validação da entrada, ela pode ser armazenada em outra variável com um nome
indicando que é segura.
O segundo passo do modelo de Feitelson é escolher as palavras para representar cada conceito. Muitas
vezes, escolher as palavras certas é simples, com uma palavra específica sendo a escolha óbvia porque é
usada no domínio do código ou foi usada em
a base de código. No entanto, em seus experimentos, Feitelson observou que também havia muitos casos
em que, para pelo menos uma das palavras, muitas opções diferentes eram sugeridas pelos participantes.
Essa diversidade pode causar problemas quando os desenvolvedores ficam confusos sobre se sinônimos
significam a mesma coisa ou representam diferenças sutis. Um léxico de projeto, no qual todas as definições
importantes são anotadas e as alternativas para sinônimos são registradas, pode ajudar os programadores
a selecionar nomes de forma consistente.
Feitelson observa que as etapas de seu modelo não precisam necessariamente ser executadas em
ordem. Às vezes você pode pensar em palavras para usar em um nome de variável sem considerar os
conceitos que elas representam. Nesses casos, é importante ainda considerar os conceitos.
A terceira etapa do modelo de Feitelson é construir um nome com as palavras escolhidas, que se
resume a selecionar um dos moldes de nomeação. Como explicamos, ao escolher um molde, o alinhamento
com sua base de código pode ser importante. Nomes consistentes tornarão mais fácil para os outros localizar
os elementos importantes dentro do nome e relacionar o nome a outros nomes. Uma segunda consideração
que Feitelson aconselha é usar
moldes para que se ajustem à linguagem natural em que as variáveis são definidas. Por exemplo,
Machine Translated by Google
em uma frase em inglês, você diria “o número máximo de pontos” em vez de “o máximo de pontos”. Portanto,
você pode preferir max_points sobre points_max.
Outra maneira de fazer com que nomes de variáveis pareçam mais naturais é adicionar uma preposição, como
como em indexOf ou elementAt.
Depois de definir o modelo de três etapas, Feitelson realizou um segundo experimento com 100 novos
participantes.
Os pesquisadores explicaram o modelo aos novos participantes, que receberam um exemplo. Após a
explicação, os participantes receberam os mesmos nomes dos participantes do estudo original de Feitelson.
Dois juízes externos foram então solicitados a comparar pares de dois nomes: um do primeiro estudo, onde os
participantes não conheciam o modelo, e um decorrente do segundo experimento, no qual os participantes
foram treinados no uso do modelo. Os juízes não sabiam qual nome veio de qual estudo.
As escolhas dos juízes mostraram que os nomes selecionados pelos sujeitos usando o modelo foram
vistos como superiores aos nomes escolhidos no experimento original na proporção de dois para um.
Assim, usar essas três etapas leva a nomes melhores.
Resumo
ÿ Existem diferentes perspectivas sobre o que faz um bom nome, desde regras sintáticas,
como o uso de camel case, até ênfase na consistência dentro de uma base de código.
ÿ Sem outras diferenças, as variáveis camel case são mais fáceis de lembrar do que as
variáveis escritas em snake case, mas as pessoas são mais rápidas em identificar variáveis
ao usar snake case. ÿ Locais no código onde ocorrem nomes incorretos também são mais
propensos a conter bugs,
mas isso não significa necessariamente que haja uma causação.
ÿ Existem muitos moldes de nomes diferentes usados para moldar nomes de variáveis, mas
limitar-se a um número menor de moldes provavelmente ajudará na compreensão. ÿ A
aplicação do modelo de três etapas de Feitelson (quais conceitos usar em um nome, quais
palavras usar para esses conceitos e como combiná-los) leva a nomes de maior qualidade.
Machine Translated by Google
Como programador profissional, tenho certeza de que você já viu código fácil de ler,
assim como o código que você teve que gastar muito esforço para entender. O código de razão pode
ser difícil de entender é algo que abordamos nos capítulos anteriores ao discutir
STM, LTM e memória de trabalho: você está experimentando muita carga cognitiva.
A carga cognitiva acontece quando sua memória de trabalho fica muito cheia e seu
cérebro não pode processar corretamente mais. Nos capítulos anteriores, focamos em como
leia o código. Vimos que às vezes você precisa obter mais conhecimento de syn tax, conceitos ou
conhecimento de domínio para ler o código com maior facilidade.
147
Machine Translated by Google
9.1 Por que código com cheiro de código cria muita carga cognitiva
Neste capítulo, examinamos como escrever código que não seja confuso para os outros; em outros
palavras, código que não impõe muita carga cognitiva para o leitor. O primeiro trabalho de estrutura que usamos para
estudar por que o código pode ser confuso é a ideia de cheiros de código: partes do código
que não estão estruturados idealmente. (Os cheiros de código foram cunhados por Martin Fowler em seu 1999
livro Refatoração: Melhorando o design do código existente [Addison-Wesley Professional].)
Exemplos de cheiros de código são métodos muito longos ou instruções switch excessivamente complexas.
Você já deve estar familiarizado com os cheiros de código. Se não estiver, a próxima subseção
fornece uma breve visão geral. Após a breve recapitulação dos cheiros de código, mergulhamos nos cheiros de código
e sua conexão com os processos cognitivos, especialmente a carga cognitiva. Simplesmente dizendo
“Essa classe é muito grande” é útil, mas às vezes não é útil o suficiente porque nós
querem entender exatamente quão grande é “grande demais” e de quais fatores isso depende.
Fowler descreve um catálogo de diferentes cheiros de código, combinados com estratégias para aliviar
cheiros de código, que ele chamou de refatorações. Exemplos de cheiros de código são métodos muito longos,
classes que tentam fazer muito ao mesmo tempo e instruções de comutação excessivamente complexas. Como
vimos anteriormente neste livro, o termo “refactoring” tornou-se
um pouco separado dos cheiros de código em que uma refatoração agora também pode indicar um
melhoria para codificar em um sentido mais genérico, além de aliviar um cheiro de código. Por
Por exemplo, a mudança de um loop em uma compreensão de lista é vista pela maioria das pessoas como uma
refatoração, mesmo que um loop não contenha necessariamente um cheiro de código.
O livro de Fowler descreve um catálogo de 22 cheiros de código, que resumimos na tabela
9.1. Embora Fowler não faça essa distinção em seu livro, os 22 cheiros de código podem ser
dividido em diferentes níveis, também indicados na tabela 9.1 Alguns code smells dizem respeito a um
método único, como o método longo, enquanto outros pertencem a uma base de código inteira, como
comentários. A próxima subseção mergulha no cheiro de código em cada um dos níveis.
Machine Translated by Google
Por que o código com cheiro de código cria muita carga cognitiva 149
Tabela 9.1 Visão geral dos cheiros de Fowler e os níveis aos quais eles pertencem
Método longo Um método não deve consistir em muitas linhas de código realizando cálculos diferentes. Nível do método
Longa lista de parâmetros Um método não deve ter muitos parâmetros. Nível do método
Alternar declarações O código não deve conter instruções switch grandes; polimorfismo poderia ser usado para Nível do método
facilitar o código.
Classes alternativas com Não deve haver duas classes diferentes que pareçam diferentes à primeira vista, mas que Nível da turma
interfaces diferentes tenham campos e métodos semelhantes.
Obsessão primitiva Evite o uso excessivo de tipos primitivos em uma classe. Nível da turma
Classe de biblioteca Os métodos não devem ser adicionados a classes aleatórias em vez de a uma classe de Nível da turma
incompleta biblioteca.
Turma grande Uma classe não deve ter muitos métodos e campos, deixando claro qual abstração a classe Nível da turma
fornece.
Classe preguiçosa Uma classe não deveria estar fazendo muito pouco para justificar sua existência. Nível da turma
Classe de dados As classes não devem conter apenas dados; eles devem conter métodos também. Nível da turma
Campo temporário As classes não devem conter campos temporários desnecessários. Nível da turma
Agrupamentos de dados Os dados frequentemente usados em combinação pertencem juntos e devem ser Nível da turma
Mudança divergente Geralmente, as alterações de código devem ser locais, de preferência para uma classe. Nível de base de código
Se você tiver que fazer muitas alterações diferentes em lugares diferentes, isso indica uma
estrutura ruim no código.
Inveja do recurso Quando muitos métodos da classe A são referenciados da classe B, eles pertencem a B e Nível de base de código
Intimidade inadequada As aulas não devem ser conectadas extensivamente a outras classes. Nível de base de código
Código duplicado ou clones O mesmo código ou código muito semelhante não deve ocorrer em vários locais diferentes Nível de base de código
Comentários Os comentários devem descrever por que o código está lá, não o que ele faz. Nível de base de código
Cadeias de mensagens Evite cadeias de mensagens que são longas cadeias de chamadas de mensagens, Nível de base de código
homem intermediário Se uma classe está delegando muita responsabilidade, ela deveria existir? Nível de base de código
Herança paralela Quando você faz uma subclasse de uma classe, você precisa fazer uma subclasse de outra. Nível de base de código
Isso indica que a funcionalidade de ambas as classes pode pertencer a uma classe.
Machine Translated by Google
Tabela 9.1 Visão geral dos cheiros de Fowler e os níveis aos quais eles pertencem (continuação)
Legado recusado Quando as classes herdam um comportamento que não usam, a herança pode não Nível de base de código
ser necessária.
Cirurgia de espingarda Geralmente, as alterações de código devem ser locais para uma classe. Se você tiver Nível de base de código
que fazer muitas alterações diferentes em lugares diferentes, isso indica uma estrutura
ruim no código.
Generalidade especulativa Não adicione código a uma base de código “apenas por precaução”; apenas adicione recursos que Nível de base de código
são necessários.
Além dos cheiros de código que existem no nível do método, existem cheiros de código no
nível de classe. Um exemplo é uma classe grande, às vezes também chamada de classe de Deus. Uma grande classe é
uma classe que tem tanta funcionalidade que não é mais uma abstração significativa. Deus
as classes normalmente não são criadas de uma só vez, mas ocorrem ao longo do tempo. Primeiro você pode criar um
classe para lidar com a exibição da conta de um cliente, que pode conter métodos relacionados
para marcar bem as informações do cliente, como print_name_and_title() ou
show_date_of_birth(). Lentamente, a funcionalidade da classe é expandida com alguns
método que também realiza alguns cálculos simples como determine_age(). Hora extra,
são adicionados métodos que não consideram um cliente individual, mas também podem listar todos
clientes de um determinado representante, e assim por diante. Em algum momento, a classe não mais
representa a lógica relacionada a um cliente, mas contém lógica para todos os tipos de processos
dentro do aplicativo e, portanto, torna-se uma classe de Deus.
Da mesma forma, uma classe pode ter poucos métodos e campos para ser uma abstração significativa, que
Fowler chama de classe preguiçosa. Uma classe preguiçosa também pode ser criada ao longo do tempo, quando
funcionalidade é movida para outras classes, ou a classe lenta pode ter sido criada como um
stub, destinado a ser estendido, o que nunca aconteceu.
Os cheiros de código não ocorrem apenas no nível de métodos ou classes individuais; uma base de código
como um todo também pode ter cheiros. Por exemplo, quando uma base de código contém
código em lugares diferentes, a base de código tem o cheiro de código duplicado, também chamado de código
clones. Um exemplo de código clonado é mostrado na figura 9.1. Outro exemplo de código
Machine Translated by Google
Por que o código com cheiro de código cria muita carga cognitiva 151
cheiro em uma base de código é quando ela contém vários métodos que passam continuamente cada
outra informação. Esse fenômeno é chamado de cadeia de mensagens.
A presença de um cheiro de código não implica necessariamente que o código tenha um erro. No entanto, sabe-
se que códigos com cheiros são mais propensos a conter erros. Foutse Khomh,
professor de engenharia de software na Polytechnique Montréal, no Canadá, estudou o
codebase do Eclipse, um conhecido IDE para Java e outras linguagens. Khomh inspecionado
diferentes versões da base de código do Eclipse e analisamos como os cheiros de código impactaram
erros. Ele descobriu que as classes de Deus eram um contribuinte significativo para a propensão ao erro em
todas as versões do Eclipse analisadas, e os métodos de Deus foram contribuintes significativos para
erros no Eclipse 2.1.1, 2
Khomh olhou não apenas para o impacto dos cheiros de código nos erros, mas também para as mudanças
Tendência. Ele descobriu que o código contendo cheiros também é mais provável de mudar no
futuro do que o código sem cheiro. A classe grande e os cheiros do método longo mostraram ter
um impacto significativamente negativo na propensão à mudança: classes que sofrem desses cheiros
são mais propensos a mudar do que classes sem em mais de 75% das versões do Eclipse.3
EXERCÍCIO 9.1 Pense no código que você editou ou corrigiu recentemente e que foi muito difícil de
Compreendo. Isso estava relacionado a um cheiro de código? Em que nível esses códigos
cheiros ocorrem?
Agora que cobrimos os cheiros de código em detalhes, vamos olhar para os aspectos cognitivos mais profundos.
questões ligadas a eles. Se você quiser evitar escrever código que contenha cheiros de código,
é importante entender por que eles são prejudiciais à compreensão. Vamos, portanto, explorar a conexão dos
cheiros de código aos processos cognitivos no cérebro e
especialmente à carga cognitiva.
1
Wei Le e Raed Shatnawi, “Um Estudo Empírico dos Maus Cheiros e Probabilidade de Erro de Classe na Evolução
do Sistema Orientado a Objetos Post Release, Journal of Systems and Software, vol. 80, não. 11, 2007, pp. 1120-1128,
http://dx.doi.org/10.1016/j.jss.2006.10.018.
2 Aloisio S. Cairo et al., “O impacto dos cheiros de código em bugs de software: uma revisão sistemática da literatura”, 2018,
https://www.mdpi.com/2078-2489/9/11/273.
3
Foutse Khomh et al., “Um Estudo Exploratório do Impacto dos Antipadrões na Alterabilidade do Software,” http://
www.ptidej.net/publications/documents/Research+report+Antipatterns+Changeability+April09.doc.pdf.
Machine Translated by Google
Os cheiros de código de Fowler são baseados em trabalhos anteriores e em sua experiência pessoal na
escrita de código. Embora Fowler não faça a conexão, muitos cheiros de código podem
estar relacionado com as funções cognitivas do cérebro. Com base no que sabemos sobre o trabalho
memory e LTM, podemos interpretar o efeito do código que contém cheiros de código.
Nos capítulos anteriores, vimos diferentes formas de confusão relacionadas a diferentes processos
cognitivos. Da mesma forma, diferentes cheiros de código têm origem em diferentes
formas de processos cognitivos, que iremos esboçar.
Sabendo o que sabemos sobre a memória de trabalho, podemos entender por que
listas de parâmetros e instruções switch complexas são difíceis de ler: ambos os cheiros são
relacionado a uma memória de trabalho sobrecarregada. Na parte 1 deste livro, explicamos que
a capacidade da memória de trabalho é tão baixa quanto 6, então faz sentido que um parâmetro
lista de mais de seis parâmetros será demais para as pessoas lembrarem.
Durante a leitura do código, será impossível manter todos os parâmetros na memória de trabalho. Como tal,
o método será mais difícil de entender.
Há, é claro, algumas nuances aqui. Nem todos os parâmetros individuais serão necessariamente tratados
como pedaços separados quando você os ler. Por exemplo, considere um
método com uma assinatura como a da próxima listagem.
Listar a assinatura do método Java 9.1 usa duas coordenadas x e duas y como parâmetros
Seu cérebro provavelmente tratará isso como dois em vez de quatro pedaços: uma origem de pedaço com um
coordenada x e y e um destino de pedaço com suas coordenadas. O limitado
número de parâmetros é, portanto, dependente do contexto e depende do conhecimento prévio
você tem sobre elementos no código. No entanto, longas listas de parâmetros são mais prováveis
sobrecarregar a memória de trabalho. O mesmo vale para instruções switch complexas.
Os clones de código, ou o cheiro de duplicação, ocorrem quando uma base de código tem muito código com
pequenas diferenças.
Com o que sabemos agora sobre a memória de trabalho, podemos entender por que
clones são considerados um cheiro de código. Considere os dois métodos anteriores, mostrados novamente
na figura 9.2. Quando você vê uma chamada de função muito semelhante a foo(), como goo(), sua memória
de trabalho pode coletar informações sobre foo() do armazenamento de longo prazo. É como o seu
a memória de trabalho está lhe dizendo “Isso pode ser útil”. Em seguida, você pode inspecionar
a implementação de goo() em si. Basta olhar para ele, e fortalecido por sua
conhecimento prévio de foo(), é provável que você pense “Ah, isso é foo()”.
Assim, seu cérebro irá agrupar o método goo(), com suas pequenas diferenças de foo(),
em uma categoria com foo(), assim como algumas variantes diferentes da abertura siciliana são todas
agrupadas em “siciliano” na mente de um jogador de xadrez. Como tal, o equívoco de que goo() é um foo()
pode nascer, mesmo que retorne um valor diferente.
Vimos anteriormente neste livro que tais equívocos podem permanecer em sua mente por
muito tempo. Você pode precisar de várias exposições ao fato de que goo() não é o mesmo que
foo() para realmente perceber seu erro.
EXERCÍCIO 9.2 Reveja o código malcheiroso que você examinou no exercício 9.1. Que
processos cognitivos estiveram envolvidos na compreensão errônea do código?
um único objeto, como um método getCustomers que retorna um booleano. Enquanto isso pode
ser um comportamento um tanto sensato em um caso em que o método está verificando se há
são clientes, também pode ser confuso.
Arnaoudova descreve seis categorias de antipadrões linguísticos, resumidas na tabela 9.2.
Identificadores cujos nomes dizem que eles contêm mais do que a entidade contém
Identificadores cujos nomes dizem que contêm menos do que a entidade contém
4
http://www.veneraarnaoudova.ca/linguistic-anti-pattern-detector-lapd/.
Machine Translated by Google
Embora todos possamos intuitivamente adivinhar que os antipadrões linguísticos são confusos e,
portanto, podem causar uma carga cognitiva mais alta, a ciência também confirma isso como fato.
Mas antes que possamos mergulhar nos efeitos dos antipadrões linguísticos na carga cognitiva,
devemos primeiro entender como a carga cognitiva pode ser medida.
Ao medir a carga cognitiva, os cientistas costumam usar a Escala Paas, projetada pelo psicólogo
holandês Fred Paas, que agora é professor da Universidade Erasmus em Rotterdam, mostrado na
tabela 9.3.
A Escala de Paas tem recebido algumas críticas nos últimos anos por ser um questionário
relativamente pequeno, composto por apenas uma questão. Também não está claro se os
participantes podem distinguir de forma confiável entre, por exemplo, ter uma carga muito alta e uma
carga muito, muito alta.
Apesar de suas deficiências, a escala de Paas é comumente usada. Nos capítulos anteriores,
abordamos estratégias para leitura de código e exercícios para praticar. Ao se envolver na tarefa de
ler um código desconhecido, a Escala Paas pode ser usada para ajudá-lo a refletir sobre o código e
seu relacionamento com o código.
Tabela 9.3 Na Escala Paas, os participantes são solicitados a auto-avaliar a carga cognitiva nesta escala de 9 pontos.
EXERCÍCIO 9.3 Escolha um código desconhecido e use a Escala de Paas para avaliar o
esforço cognitivo que você teve que fazer para entender esse código. Em seguida, reflita
também sobre por que esse código causou uma certa quantidade de carga cognitiva. Este
exercício pode ajudá-lo a entender quais tipos de código são difíceis de ler.
Machine Translated by Google
Além de métricas baseadas na percepção dos participantes, pesquisas mais recentes também
utiliza cada vez mais a biometria. Ao medir a reação do corpo a uma determinada tarefa,
é possível estimar a carga cognitiva sendo causada por qualquer que seja a pessoa
fazendo naquele momento.
Um exemplo de medição biométrica é o rastreamento ocular. Com um rastreador ocular nós
pode determinar como as pessoas estão concentradas. Isso pode ser feito, por exemplo, examinando a
taxa de piscadas de uma pessoa, que é uma medida de quantas vezes você pisca. Vários estudos
mostraram que o comportamento de piscar não é estável, mas pode diferir com base no que você está
fazendo no momento. Vários estudos também descobriram que a carga cognitiva afeta o piscar; quanto
mais difícil for uma tarefa, menos você piscará. Uma segunda métrica relacionada ao olho que pode
prever a carga cognitiva é a pupila. Estudos mostraram que tarefas mais difíceis induzem
mais carga cognitiva medida pelo tamanho da pupila.5
A hipótese atual de por que piscar se correlaciona com a carga cognitiva é que pode
indicar o quanto seu cérebro tenta maximizar sua exposição à tarefa difícil e
assim tenta obter tantos estímulos visuais quanto possível. Por razões semelhantes, alunos maiores
ocorrem ao realizar tarefas complicadas, pois com uma pupila maior, o olho pode
absorver mais informações, que é o que o cérebro procura quando está envolvido em uma tarefa difícil.
Além das medidas com base nos olhos, a pele também pode nos dizer as
carga de uma pessoa. A temperatura da pele e a presença de suor também são indicadores de carga
cognitiva.
Embora esses métodos biométricos de medição da carga cognitiva possam parecer legais,
pesquisas mostraram que eles geralmente se correlacionam com a escala Paas, então mesmo que você
pode considerar usar seu rastreador de fitness para decidir sobre a legibilidade do seu código,
simplesmente usar o exercício 9.3 provavelmente funcionará.
5
Shamsi T. Iqbal et. al., “Task-Evolved Pupillary Response to Mental Workload in Human-Computer Interaction”, 2004, https://interruptions.net/literature/
Iqbal-CHI04-p1477-iqbal.pdf.
Machine Translated by Google
No capítulo 5, discutimos o scanner fMRI como um método para medir que tipo de atividades o
cérebro está realizando. As máquinas de fMRI são precisas em sua medição, mas também têm
grandes limitações. Como os participantes precisam ficar parados, eles não podem escrever
código enquanto estão na máquina. Até mesmo a leitura de código é um pouco artificial, pois só
pode ser exibida em uma tela pequena. O fato de as pessoas não poderem se mover na
máquina também limita muito suas possíveis interações com o código; por exemplo, eles não
seriam capazes de percorrer o código, clicar em identificadores para navegar até suas definições
no código ou pesquisar palavras-chave ou identificadores usando ctrl-f. Devido a essas limitações
de fMRI, medições cerebrais alternativas também são usadas.
ELETROENCEFALOGRAMA
6
Takao Nakagawa et al., “Quantificando a carga de trabalho mental dos programadores durante a compreensão do
programa com base na medição do fluxo sanguíneo cerebral: um experimento controlado”, https://posl.ait.kyushu-u.ac.jp/
~kamei/publications/Nakagawa_ICSENier2014. pdf.
Machine Translated by Google
programa mais complicado, a configuração foi aleatória. Isso significava que alguns participantes
viram pela primeira vez a versão original enquanto outros viram pela primeira vez o programa modificado.
Os resultados do estudo de Nakagawa mostraram que para 8 dos 10 participantes, o
fluxo sanguíneo oxigenado enquanto estudava o complicado programa era maior do que quando
lendo os programas originais. Este resultado sugere que a carga cognitiva durante a programação pode ser
quantificada usando a medição do fluxo sanguíneo cerebral com um fNIRS
dispositivo.
Os pesquisadores fazem mais algumas adaptações ao código, de modo que tinham quatro
variantes dos trechos de código:
4 Sem problemas
Os participantes foram divididos em quatro grupos, e cada um dos grupos leu os bichinhos de estimação em
uma ordem diferente para descartar quaisquer efeitos de aprendizagem.
Os pesquisadores usaram um rastreador ocular para detectar onde no código as pessoas estavam lendo,
e os participantes também usaram uma máquina fNIRS para detectar a carga cognitiva. O
resultados do estudo mostraram primeiro que as partes do código onde os antipadrões linguísticos
ocorreram foram inspecionados muito mais do que o resto do código, como indicado pelo olho
resultados do rastreador.
Além disso, os resultados do dispositivo fNIRS indicam que a presença de antipadrões linguísticos no
código-fonte aumenta significativamente a média oxigenada
fluxo sanguíneo que um participante experimenta (ou seja, a carga cognitiva que é induzida por
o trecho é maior).
O que também foi interessante neste estudo em particular foi que alguns dos trechos continham
antipadrões estruturais que os pesquisadores adicionaram ao código. O código foi
formatado de forma contrária aos padrões convencionais de formatação Java; por
Por exemplo, os colchetes de abertura e fechamento não estavam em suas linhas e não eram recuados
devidamente. Eles também aumentaram a complexidade do código, por exemplo, adicionando
rotações.
Machine Translated by Google
Resumo 159
Resumo
ÿ Odores de código, como métodos longos, indicam problemas estruturais com o código. Lá
são diferentes razões cognitivas pelas quais os cheiros de código causam uma carga cognitiva mais alta.
O código duplicado, por exemplo, torna mais difícil o código em pedaços corretamente, enquanto
longas listas de parâmetros são pesadas em sua memória de trabalho.
Ficando melhor em
resolvendo problemas complexos
Nos últimos capítulos, analisamos principalmente o que você não deve fazer ao codificar
e porque. Investigamos o efeito de nomes ruins no capítulo 8 e o impacto de
código cheira a sua capacidade de entender código no capítulo 9.
No início do livro, no capítulo 6, discutimos diferentes estratégias para apoiar
sua memória de trabalho ao resolver problemas de programação. Este capítulo novamente
abrange técnicas para ajudá-lo na resolução de problemas, mas concentra-se no fortalecimento
seu LTM.
160
Machine Translated by Google
Vamos primeiro investigar o que significa resolver um problema. Depois de explorar o problema
resolvendo em profundidade, mergulhamos em como melhorar nisso. Ao final deste capítulo, você
conhecerá duas técnicas para melhorar suas habilidades de programação e resolução de problemas.
A primeira técnica que abordaremos é a automatização (ou seja, ser capaz de realizar pequenas tarefas
sem pensar neles). Isso é útil porque quanto menos tempo você gasta para descobrir pequenas coisas, mais fácil
é resolver problemas difíceis. Em seguida, exploraremos a resolução de problemas a partir do código escrito de
outras pessoas como um meio de melhorar suas próprias habilidades de resolução de problemas.
O estado da meta, que é o que queremos alcançar. Quando atingimos o estado objetivo,
o problema é considerado resolvido.
ÿ O estado inicial de onde o problema deve ser resolvido ÿ As
regras que ditam como podemos alcançar o estado objetivo a partir do estado inicial
Por exemplo, considere jogar jogo da velha. Nessa situação, o estado inicial é um vazio
campo, seu estado desejado é três cruzes seguidas, e as regras são que você pode colocar um
cruzar em qualquer campo vazio no tabuleiro. Quando você adiciona uma caixa de pesquisa a um site existente,
seu estado inicial é a base de código existente e seu estado desejado pode ser
passando em testes de unidade ou um usuário satisfeito. As regras do problema na programação muitas vezes
vêm na forma de restrições, como implementar o recurso em JavaScript ou não
quebrando testes adicionais ao implementar este novo recurso.
Todos os passos que poderíamos considerar ao resolver um programa são chamados de estado do problema
espaço. Ao jogar jogo da velha, todos os campos possíveis são o espaço de estados. Para pequenos problemas
como jogo da velha, todo o espaço de estados pode ser visualizado. A Figura 10.1 mostra um pequeno
parte do espaço de estados de um jogo da velha.
Por exemplo, ao adicionar um botão a um site, todos os programas JavaScript possíveis
são o espaço de estados. Cabe ao solucionador de problemas fazer os movimentos certos ou adicionar o
linhas de código certas para atingir a meta inicial. Em outras palavras, a resolução de problemas significa
percorrendo o espaço de estados da maneira ideal, alcançando o estado objetivo em poucos passos
possível.
Machine Translated by Google
Linha de x: 0
Linha de x: 1
EXERCÍCIO 10.1 Examine algo que você criou com código nos últimos
dias. Quais eram os aspectos desse problema?
1 Entendendo o problema
2 Elaborando um
plano 3 Realizando o plano
No entanto, apesar da popularidade das abordagens genéricas, a pesquisa tem consistentemente
mostraram que a resolução de problemas não é uma habilidade genérica nem um processo cognitivo. Lá
são duas razões pelas quais os métodos genéricos de resolução de problemas não funcionam tão bem, e ambos
têm a ver com o papel do LTM.
Machine Translated by Google
Primeiro, quando você resolve um problema, leva em consideração o conhecimento sobre o estado
do objetivo desejado e as regras pelas quais temos que jogar. O problema em si influenciará as
soluções que podemos projetar. Vamos considerar a influência do conhecimento prévio nas soluções
com um exemplo de programação. Suponha que você tenha que implementar um código para
detectar se uma determinada string de entrada s é um palíndromo. Pedimos que você escreva o
código para este problema em Java, APL e BASIC. Vamos investigar os passos de Polya e ver como
eles se sairiam durante a programação.
ÿ Compreender o problema.
Assumimos que para você, sendo um programador, entender o problema não é um
problema. Você provavelmente será capaz de formular alguns casos de teste sólidos para
verificar seu código também. ÿ Elabore um plano (traduza).
A segunda etapa do método de Polya é mais difícil de fazer. Seu plano depende muito
dos recursos da linguagem de programação na qual você implementará a solução.
A linguagem em questão, por exemplo, tem um método ou função inversa? Nesse caso,
você pode simplesmente verificar se a string s é igual a reverse(s). Você deve saber que em
Java você pode usar StringBuilder, que tem uma função reverse() , mas esse método está
disponível para BASIC e APL?
Não ter acesso às informações, aos blocos de construção da solução, dificulta a criação de
um plano.
ÿ Executar o plano (resolver).
A terceira etapa do método de Polya também é influenciada por sua compreensão da
linguagem de programação em questão. Posso dizer que o APL realmente tem uma função
reverse() (o BASIC não tem), mas você viu um pouco do APL agora e sabe que não será
chamado reverse(), porque todas as palavras-chave do APL são operadores, portanto, a
execução do plano ainda é um desafio.
Há uma segunda razão pela qual os métodos genéricos de solução de problemas geralmente ficam
aquém que também se relaciona com o funcionamento do LTM. No capítulo 3, explicamos que as
memórias no LTM são armazenadas como uma rede em relação umas às outras. Anteriormente
neste capítulo, também abordamos o fato de que, enquanto pensa em um problema, o cérebro
recupera informações do LTM, que podem ser relevantes para o problema em questão.
O uso de uma técnica genérica de solução de problemas como a de Polya “desenhe um plano”
cria um problema cognitivo. Você pode ter muitas estratégias úteis armazenadas em seu LTM, que o
cérebro tenta recuperar ao resolver o problema. Quando tentamos resolver o problema de forma
genérica, no entanto, as estratégias relevantes podem não ser encontradas. Conforme descrevemos
no capítulo 3, o LTM precisa de pistas para recuperar as memórias certas. Quanto mais específica a
pista, maior a probabilidade de encontrarmos a memória certa. Por exemplo, quando você tem que
realizar a divisão da cauda, é improvável que pensar em um plano dê ao seu LTM pistas suficientes
Machine Translated by Google
para encontrar a abordagem armazenada na memória. Pensar em coisas como divisão de cauda ou subtração de
múltiplos do divisor tem maior probabilidade de resultar no plano certo.
Como abordamos no capítulo 7, transferência de conhecimento de um domínio, como xadrez,
para outro domínio, como a matemática, é improvável de acontecer. Da mesma forma, a transferência de
o domínio muito genérico da solução de problemas de volta para outros domínios não é muito provável.
Vimos que a resolução de problemas não é um processo cognitivo. Isso nos traz a isso
pergunta: Em vez disso, como devemos treinar para a resolução de problemas? Para explorar isso em mais
profundidade, precisamos mergulhar ainda mais fundo em como o cérebro pensa. No início do livro, nós
explicou que os pensamentos são formados na memória de trabalho. Vimos também que seu
a memória de trabalho não forma pensamentos sozinha, mas opera em forte colaboração
que tanto o LTM quanto o STM.
Quando você pensa em um determinado problema, digamos que implemente um botão de classificação em
uma aplicação web, sua memória de trabalho tomará as decisões sobre o que você implementa. No entanto, antes
que sua memória de trabalho possa tomar tal decisão, ela precisa
fazer duas coisas. A primeira é obter informações do STM sobre o contexto do problema, como os requisitos para
o botão ou o código existente que você acabou de ler.
Ao mesmo tempo, o LTM é pesquisado por conhecimento prévio relevante. Memórias relevantes que você
pode ter, como como implementar classificação ou informações
sobre a base de código, também são enviados para a memória de trabalho. Para entender o problema
resolvendo melhor, precisamos explorar esse segundo processo de busca do LTM.
Mais adiante neste capítulo, exploraremos duas técnicas para fortalecer sua capacidade de resolução de problemas.
Habilidades. Antes disso, no entanto, precisamos explorar diferentes tipos de memórias que as pessoas têm.
e qual o papel que desempenham na resolução de problemas. Compreender as diferentes formas de
memórias são importantes porque tipos diferentes são criados de maneiras diferentes,
O LTM pode armazenar diferentes tipos de memória, que estão descritos na figura 10.2.
Primeiro, há a memória procedural (às vezes chamada de implícita) , que é a memória para
habilidades motoras ou habilidades que você não está conscientemente ciente. As memórias implícitas são, por
por exemplo, saber amarrar cadarços ou andar de bicicleta.
O segundo tipo de memória que desempenha um papel na resolução de um problema é a declarativa
(às vezes chamado de memória explícita) . Há fatos que você pode lembrar, e você também
sei que você os conhece, como o fato de Barack Obama ter sido o 44º presidente da
nos Estados Unidos, ou que a maneira de escrever um loop for em Java é para (i = 0; i < n; i++).
Conforme mostrado na figura 10.2, as memórias declarativas podem ser subdivididas em duas categorias:
memória episódica e memória semântica. Memórias episódicas são o que muitas vezes
queremos dizer quando usamos coloquialmente a palavra “memória”. Essas são memórias de experiências, como
ir ao acampamento de verão quando você tinha 14 anos, conhecer seu cônjuge pela primeira vez.
tempo, ou passar três horas perseguindo um bug apenas para descobrir que havia um erro em um
teste de unidade.
Machine Translated by Google
Recordações
Declarativo
Processual
ou
ou Figura 10.2 Existem diferentes
explícito tipos de memórias. A memória
implícito
procedural (ou implícita) mostra
como fazer algo. A memória
declarativa (explícita) consiste em
Semântica memórias das quais estamos
Episódico explicitamente cientes. A memória
declarativa é dividida em coisas que
você experimentou e que são
a2 + b2 = c2 armazenadas na memória episódica
e fatos que você conhece que são armazenados na me
A memória episódica é usada quando você se lembra de como resolveu um problema no passado.
Quando você tem que resolver um problema envolvendo hierarquia, por exemplo, você pode
lembre-se que você usou uma árvore no passado. A pesquisa mostra que os especialistas confiam especialmente
fortemente na memória episódica ao resolver problemas. De certa forma, os especialistas recriam,
em vez de resolver problemas familiares. Isso significa que, em vez de encontrar uma nova solução, eles confiam
em soluções que funcionaram anteriormente para problemas semelhantes. No capítulo 10, vamos mergulhar
mais fundo em como podemos fortalecer memórias episódicas para nos tornarmos
melhores solucionadores de problemas.
colocar automaticamente um ponto de interrupção em uma linha onde você suspeita que um bug possa estar presente.
Às vezes, o que chamamos de intuição, de fato, acontece quando você resolve um problema semelhante ao
um que você já resolveu antes; você só sabe o que fazer sem realmente saber como fazer
isto.
Recordações
Processual Declarativo
ctrl-c
ou ou
ctrl-v
implícito explícito
Episódico Semântica
DESAPRENDIZADO
Embora tenhamos visto que a memória implícita pode ajudá-lo a executar tarefas conhecidas rapidamente,
ter memórias implícitas também pode ser prejudicial. No capítulo 7, discutimos a ideia de
transferência negativa: saber algo prejudica aprender outra coisa. Tendo muito
a memória implícita também pode prejudicar sua flexibilidade. Por exemplo, uma vez que você aprendeu
como tocar no teclado Qwerty, aprender a usar um teclado Dvorak será
mais difícil do que se você nunca tivesse aprendido Qwerty. Isso se deve em parte ao fato de você
têm um grande conjunto de memórias implícitas de como fazer as coisas.
Você pode ter experimentado a dificuldade de desaprender a memória implícita, também,
se você já aprendeu uma segunda linguagem de programação com uma sintaxe bem diferente
da primeira língua que você aprendeu. Por exemplo, ao passar de C# ou Java para
Python, é provável que você inconscientemente digite chaves em torno de blocos ou funções por um tempo. Eu
pessoalmente mudei de C# para Python anos atrás, e ainda
digite foreach em vez de for ao iterar sobre uma lista, que é devido à memória implícita
Eu construí ao programar C# e isso ainda está fortemente presente.
EXERCÍCIO 10.2 Na próxima vez que você escrever código, tente monitorar ativamente o
memórias que você usa.
Use a tabela a seguir para refletir sobre que tipo de programas ou problemas ativam qual tipo de memória.
Pode ser interessante fazer este exercício algumas
vezes para programas diferentes. Se você fizer o exercício algumas vezes e seguir
seu progresso por um tempo, é provável que você veja que, para linguagens ou projetos de programação
menos familiares, você confia mais na memória semântica,
enquanto para situações mais familiares, você usa mais procedimentos e episódios
recordações.
Machine Translated by Google
Por exemplo, uma vez que você pode fatorar equações com grande facilidade, você pode olhar para um
equação como a que segue e ver imediatamente que a resposta é (x + y). Se vocês
não automatizou a fatoração, este problema será muito mais difícil de fazer, se não
impossível.
x2 y2+ 2xy
+
----------------------------------
()x+
Assim, a automatização das habilidades de programação é a chave para poder resolver problemas maiores e mais
problemas complexos. Mas como você chega ao ponto de ter habilidades automatizadas?
Para saber como automatizar habilidades, primeiro precisamos explorar como fortalecer seu
memórias de programação implícitas. No início do capítulo, discutimos o fato de que
às vezes, memórias implícitas podem atrapalhar quando se muda para uma nova programação
linguagem, por exemplo, quando você continua inserindo chaves em um problema Python.
Embora você possa pensar que é um pequeno erro, isso causa alguma carga cognitiva. Como
abordada no capítulo 9, a carga cognitiva indica o quanto seu cérebro está ocupado ou cheio. Quando você
experimentar muita carga cognitiva, o pensamento pode se tornar muito difícil. A coisa interessante sobre
memórias implícitas é que quando você treinou memórias implícitas
bem o suficiente, seu cérebro quase não precisa de energia para usá-los. Por exemplo, quando
você sabe andar de bicicleta, ou como tocar o tipo, você pode fazê-lo sem nenhum esforço. Porque
Machine Translated by Google
essas tarefas não criam quase nenhuma carga cognitiva, você pode andar de bicicleta e também comer um sorvete ou
dirigir um carro enquanto fala.
FASE COGNITIVA
Primeiro, alguém que está aprendendo algo novo está na fase cognitiva. Nesta fase,
uma nova informação precisa ser dividida em partes menores e você tem que pensar explicitamente sobre a
tarefa em mãos.
Por exemplo, quando você aprendeu a indexar uma lista baseada em zero, provavelmente
precisava gastar energia para acompanhar esse índice, conforme mostrado na parte mais à esquerda da figura
10.4. Na fase cognitiva, os esquemas são formados ou atualizados. Por exemplo, quando
você aprendeu a indexar uma lista começando do zero, você já tinha um esquema salvo no seu cérebro
para contar fora da programação. Esse esquema ditava que a contagem começa em um
e precisava ser adaptado para incluir também a possibilidade de começar do zero.
Figura 10.4 As três fases de armazenamento de informações: fase cognitiva, fase associativa
e fase autônoma
Machine Translated by Google
FASE ASSOCIATIVA
A fase associativa é a próxima. Nesta fase, você precisa repetir ativamente as novas informações até que surjam
padrões de resposta. Você vê um colchete de abertura e fica nervoso se não vê o de fechamento. Você também
pode perceber que digitar os dois colchetes
é uma ótima estratégia para não esquecer o fechamento. Em outras palavras, ações efetivas são
ações lembradas e ineficazes são descartadas.
Quanto mais difícil é uma tarefa, mais tempo leva para completar a fase associativa. Mais fácil
fatos ou tarefas são lembrados mais rapidamente. No exemplo da contagem em zero, após
um tempo você pode perceber que você pode simplesmente pensar no elemento que deseja recuperar
e subtraia 1 dele para chegar ao índice correto.
FASE AUTÔNOMA
Finalmente, chega-se à fase autônoma (também chamada de fase processual), onde
a habilidade é aperfeiçoada. Por exemplo, você atingiu a fase autônoma quando
index em uma lista e você sempre faz isso corretamente, não importa o contexto, o tipo de dados ou
as operações da lista. Quando você vê uma lista e uma operação de lista, agora você pode dizer a sua
número imediatamente, sem depender de contar ou pensar explicitamente.
Quando você atinge a fase autônoma, dizemos que você automatizou a habilidade.
Você pode executar a tarefa sem nenhum esforço, e realizar a habilidade não irá adicionar
a carga cognitiva que um problema representa.
Para experimentar o poder da automatização, veja o exercício 10.3. Se você é um programador Java
experiente, provavelmente pode preencher os espaços em branco no código sem
a pensar nisso. O padrão de um loop for é tão conhecido que você pode completá-lo sem pensar nos limites,
mesmo em uma situação um pouco mais incomum, como um
laço reverso.
EXERCÍCIO 10.3 Conclua esses programas Java completando as partes que faltam
sobre __ o mais rápido possível:
10.3.2 Por que a automatização vai fazer você programar mais rápido
Ao criar um grande repositório de técnicas (os céticos podem chamá-las de truques), podemos
criar uma caixa de ferramentas cada vez maior de novas técnicas. Gordon Logan, psicólogo americano, argumenta
que a automatização é feita recuperando memórias da parte episódica do LTM, na qual também são armazenadas
memórias regulares sobre a vida cotidiana.
A execução de uma tarefa como fatorar uma equação ou ler uma carta cria uma nova memória, que é uma
instância de uma memória sobre a tarefa. Porque cada memória é vista como
uma instância de uma classe abstrata, como a classe “memórias sobre fatoração”, a teoria é
chamada de teoria da instância.
Quando você é confrontado com uma tarefa semelhante, em vez de raciocinar sobre o
tarefa — como alguém que não tem memórias de instância suficientes pode fazer — você pode lembrar como fez
antes e aplicar o mesmo método. De acordo com Logan, a automatização está completa quando você confia
totalmente na memória episódica e não usa nenhum
raciocínio em tudo. Este desempenho automático de tarefas é rápido e sem esforço porque
recuperação da memória é mais rápida do que pensar ativamente na tarefa em mãos e pode ser
feito com pouca ou nenhuma atenção consciente. Se você automatizou totalmente uma tarefa, você
também não sente necessidade de voltar e verificar seu trabalho, o que você pode ficar tentado a fazer
ao completar uma tarefa pelo raciocínio.
Para muitas das habilidades que você precisa para programação e resolução de problemas, você tem
provavelmente atingiu a fase autônoma até agora. Escrevendo um loop for, indexando em uma lista,
ou criar uma classe são habilidades que você provavelmente automatizou. Assim, essas tarefas
não adicionar à sua carga cognitiva durante a programação.
Dependendo de sua experiência e habilidades, no entanto, há tarefas que você provavelmente
ainda lutando. Como mencionei antes, eu pessoalmente lutei com o loop for
quando comecei a aprender Python. Não que eu não conseguisse lembrar a sintaxe do for; eu
tinha até usado flashcards para praticar a sintaxe! No entanto, muitas vezes quando eu tentei digitar
as palavras, pois cada uma saiu de meus dedos e não para. Minhas memórias implícitas
precisava ser religado. Antes de mergulharmos em técnicas para religar suas memórias,
seria uma ótima idéia diagnosticar suas próprias habilidades para que você saiba onde melhorar.
Assim como com os flashcards, a repetição espaçada é a chave para o aprendizado. Reserve algum tempo
todos os dias para praticar e continuar até que você possa executar as tarefas de forma consistente sem qualquer
esforço. Eu quero enfatizar novamente como essa técnica é incomum na programação, então pode parecer
estranho, mas continue tentando. É realmente como levantamento de peso; cada
a repetição torna você um pouco mais forte.
O professor australiano John Sweller, que também introduziu a ideia de carga cognitiva,
coberto no capítulo 4, pesquisou extensivamente a importância de
estratégias para habilidades de resolução de problemas.
Sweller ensinou matemática às crianças fazendo com que elas resolvessem equações de álgebra, mas
logo ele ficou frustrado com o quão pouco eles estavam aprendendo trabalhando apenas em problemas tradicionais
de álgebra. Nesse ponto, Sweller se interessou em experimentar
a forma como a resolução de problemas é ensinada. Para obter mais informações, Sweller realizou uma série de
experimentos na década de 1980. Nesses experimentos, Sweller estudou 20 alunos do nono ano
(crianças de 14 a 15 anos) em uma escola secundária australiana. Ele dividiu os alunos em dois
grupos, que foram solicitados a resolver equações típicas de álgebra, como “a = 7 – 4a;
resolver para um.”
a = b – ca
a + ca = b
a = 7 - 4a
a (1 + c) = b
O que é uma?
a=b
1+c
sem mais ajuda, assim como você provavelmente também resolveu equações de álgebra em alta
escola. O Grupo 1 recebeu as equações de álgebra, mas também recebeu exemplos trabalhados das
equações, que explicam como resolver os problemas em detalhes. Você pode pensar em trabalhar
exemplos como receitas que descrevem em detalhes os passos necessários para resolver as equações.
Depois que os dois grupos de crianças terminaram as equações, Sweller e Cooper compararam o
desempenho dos alunos. Provavelmente não é uma surpresa que o
o primeiro grupo se saiu melhor; afinal, eles têm receitas para resolver os problemas. O grupo 1 fez
espetacularmente melhor; eles resolveram as equações cinco vezes mais rápido que o grupo 2.
Sweller e Cooper também testaram o desempenho de ambos os grupos em diferentes problemas porque
é isso que faz com que algumas pessoas relutem em ensinar com essas receitas; a
ideia de que as crianças só poderão seguir cegamente as receitas e não
nada. E aqui está o kicker: as crianças do grupo 1 também tiveram melhor desempenho em diferentes
problemas, para os quais poderiam ser usadas regras de cálculo (que estavam presentes no
receita), como subtrair o mesmo valor de ambos os lados de uma equação ou dividir
ambos os lados da equação pelo mesmo.
O efeito do exemplo trabalhado foi replicado em muitos estudos para diferentes idades
grupos e assuntos, incluindo matemática, música, xadrez, esportes e programação.
Memória de Memória de
longo prazo
curto prazo
Em formação
Memória
de trabalho
Figura 10.6 A carga alemã é necessária para habilitar a seta destacada e formar a
memória de trabalho no LTM. Se a memória de trabalho estiver trabalhando muito
(ou seja, com muita carga), nenhuma informação poderá ser armazenada.
É por isso que, às vezes, após uma sessão pesada de codificação, você não consegue lembrar o que
fez. Seu cérebro estava tão engajado que não conseguia armazenar as soluções.
Agora que você está familiarizado com os três tipos de carga cognitiva, incluindo a carga relevante,
podemos reconsiderar o experimento australiano com os alunos do nono ano. Agora entendemos por
que o grupo que usou as receitas se saiu melhor em novas equações: como sua carga cognitiva não era
tão alta, eles puderam refletir e lembrar dessas receitas. Eles aprenderam que, ao resolver um problema
de álgebra, pode ser uma boa ideia mover parte da equação para o outro lado do sinal de igual, o que
requer transformar um mais em menos, ou que eles sempre podem dividir ambos os lados pelo mesmo
valores.
O tipo de habilidades que os alunos aprenderam com as receitas são exatamente o tipo de
abordagem que é útil em quase todos os problemas de álgebra e, portanto, o primeiro grupo também
pode aplicá-las às novas equações. O segundo grupo, embora engajado no pensamento profundo,
estava mais focado no problema em questão do que em regras genéricas.
As pessoas que se preocupam em ensinar receitas têm isso ao contrário. Concordo, parece tão
sensato: se queremos que as crianças sejam solucionadoras de problemas, elas devem resolver problemas.
Essa mesma linha de pensamento está presente na comunidade de programação: se você quer ser um
programador melhor, programe muito! Tenha projetos paralelos e experimente as coisas e você
aprenderá. Mas isso parece não ser verdade.
Embora os experimentos de Sweller se concentrem no ensino de matemática, experimentos
semelhantes foram feitos para programação. Esses estudos mostraram resultados semelhantes: as
crianças aprendem mais com programas de leitura, além de uma explicação que os acompanha, do que
com a programação.1 Como diz o psicólogo holandês Paul Kirschner: “Você não se torna um especialista
fazendo coisas de especialista”.
1
Marcia C. Linn e Michael J. Clancy, “O caso para estudos de caso de problemas de programação,”
Comunicações da ACM, vol. 35, não. 3, 1992, https://dl.acm.org/doi/10.1145/131295.131301.
Machine Translated by Google
Resumo 175
Vimos que estudar código explicitamente e estudar o processo de como ele foi criado pode ajudá-lo
a fortalecer suas habilidades de programação. Existem várias fontes que você pode usar ao estudar
código.
Primeiro, você não precisa estudar código sozinho; é mais útil fazê-lo com alguém. Você pode iniciar
um clube de leitura de código no trabalho com outros colegas interessados em estudar código. Se
fizerem isso juntos, será mais fácil manter o hábito de ler o código regularmente (https://code-
reading.org/). Se você estiver em um clube, poderá trocar o código e sua explicação e aprender um
com o outro.
No capítulo 5, abordamos técnicas para entender o código, incluindo fazer resumos de código.
Esses resumos podem servir como explicação para usar ao ler o código.
Você pode estudar seu próprio código e resumos, mas é ainda mais poderoso se você usar um
processo de duas etapas em que primeiro escreve um resumo para o código que escreveu e depois
o troca para aprender com o código de um colega.
EXPLORE
GITHUB Se você está explorando a prática de leitura de código sozinho, felizmente há uma infinidade
de código-fonte e documentação online. O GitHub, por exemplo, é um excelente lugar para começar
a ler código. Basta ir ao código de um repositório que você conhece um pouco, por exemplo, uma
biblioteca que você usa, e ler o código. É melhor se você escolher um repositório no qual o domínio
seja pelo menos um pouco familiar para você, para que não haja muitas palavras e conceitos
desconhecidos causando carga adicional estranha e você possa se concentrar na programação em
si.
Existem muitos posts de blog que descrevem como as pessoas resolveram um determinado problema
de programação, e estes também podem servir como ferramentas de estudo. E embora não existam
muitos, existem alguns livros que também descrevem o código e sua explicação; por exemplo, os
dois volumes de The Architecture of Open Source Systems , de Amy Brown e Greg Wilson, ou 500
Lines or Less , de Amy Brown e Michael DiBernardo.
Resumo ÿ
Enquanto muitas pessoas em programação argumentam que a resolução de problemas é uma
habilidade genérica, não é. Seu conhecimento prévio de programação, juntamente com o
problema atual que você está resolvendo, influencia a rapidez com que você pode resolver
problemas de programação.
ÿ Seu LTM armazena diferentes tipos de memórias, que desempenham um papel diferente na
resolução de problemas. As duas categorias abrangentes de memórias são memórias implícitas
e explícitas. Memórias implícitas são “memórias musculares”, tarefas que você pode executar
sem pensar nelas, como digitação por toque. Memórias explícitas são memórias das quais
você está ciente e que precisa relembrar ativamente, como a sintaxe de um loop for.
Machine Translated by Google
Parte 4
Ao colaborar no código
Até agora, neste livro, nos concentramos no desenvolvedor individual. Mas na realidade,
software é desenvolvido por equipes. Na parte final deste livro, discutiremos como
entrar no estado de fluxo, onde você pode codificar sem ser interrompido por seus
colegas. Também discutiremos como criar sistemas maiores em que outras pessoas
possam facilmente começar a trabalhar, bem como o processo de integração.
Machine Translated by Google
Machine Translated by Google
Até agora neste livro, examinamos quais processos cognitivos desempenham um papel quando
leitura e escrita de código. Nos capítulos anteriores, vimos como escrever código
que é mais fácil de ler e como resolver melhor os problemas.
Neste capítulo, direcionamos nossa atenção para longe do código em si e, em vez disso, procuramos
em que processos cognitivos desempenham um papel quando você está executando o ato de
programação. Vamos primeiro examinar o que queremos dizer quando dizemos que alguém está programando.
Vamos mergulhar em diferentes atividades que compõem o ato de programar e explorar
como apoiar melhor essas diferentes atividades.
179
Machine Translated by Google
Tarefa Difícil
Atividade
Executando Codificação Testando Lendo Refatoração
Memória de
Procurando curto prazo
Trabalhando
Compreensão memória
Memória de
Transcrição longo prazo
Figura 11.1 Uma visão geral das atividades de programação e os sistemas de memória que eles mais usam
11.1.1 Pesquisando
Pesquisar no código é a atividade em que você está olhando através de uma base de código, pesquisando
para uma informação específica. Esta pode ser a localização precisa de um bug que você precisa
endereçar, todas as chamadas de um determinado método, ou o local em que uma variável deve ser
inicializado.
breadcrumbs para si mesmo nos comentários indicando por que você visitou o código. Por exemplo,
um comentário como “Eu li este método porque pensei que poderia estar envolvido na inicialização
da classe de página” pode ajudá-lo quando você visitar o código posteriormente na mesma pesquisa.
Isso é especialmente importante quando você suspeita que pode não conseguir terminar a pesquisa
de uma só vez porque uma reunião ou o final do dia de trabalho está se aproximando.
Anotar as etapas da pesquisa ajudará você a concluí-la mais tarde.
11.1.2 Compreensão
Quando você está realizando a atividade de compreensão, você está lendo e executando código
para entender sua funcionalidade. É semelhante à pesquisa, mas com a compreensão, você não tem
uma boa compreensão do que o código faz em detalhes, possivelmente porque é um código que
você escreveu há muito tempo ou porque outra pessoa escreveu o código.
Como vimos no capítulo 5, os desenvolvedores, em média, gastam até 58% de seu tempo
compreendendo o código-fonte existente, portanto, essa atividade é bastante comum no seu dia-a-
dia.
Além de ler e executar o código, a compreensão também pode incluir a execução do código de
teste para obter uma melhor compreensão de como o código deve funcionar.
E como abordamos no capítulo 4, a compreensão também pode incluir a execução de refatorações
para tornar o código mais fácil de entender.
Refatorar o código ajuda porque a compreensão é uma atividade que pesa muito na sua memória
de trabalho. Você tem que raciocinar sobre o código que você ainda não entende completamente.
Como tal, apoiar a memória de trabalho é a melhor estratégia que você tem quando se trata de
compreensão. Tente desenhar um modelo do código e atualizá-lo sempre que aprender novas
informações. Dessa forma, em vez de recuperar informações do seu cérebro, você pode recuperá-
las de uma fonte externa, o que é mais fácil. E ter um modelo à mão também pode ajudar a detectar
equívocos que você possa ter sobre o código. Além disso, se você tiver que terminar a tarefa de
compreensão mais tarde, todas as suas anotações e desenhos podem ajudá-lo a voltar à tarefa com
mais facilidade.
11.1.3 Transcrição
Transcrição é a atividade em que você está “apenas codificando”. Você tem um plano concreto do
que deseja adicionar ou alterar à base de código e fará exatamente isso. Na sua forma mais pura, ao
transcrever você está apenas codificando e nada mais.
A transcrição é difícil principalmente no LTM porque você terá que lembrar de construções
sintáticas para implementação.
11.1.4 Incrementação
Como a incrementação é uma mistura de atividades, também pode ser difícil para os três sistemas de
memória. Assim, as tarefas de incrementação, que são as mais comuns na vida de um programador
profissional, são as que mais necessitam de suporte para os sistemas de memória em
a forma de notas e refatoração para compreensão.
Sua experiência pessoal com a linguagem de programação e a base de código disponível
afeta qual sistema é mais fortemente impactado. Se você conhece a linguagem de programação
bem, seu LTM pode não estar trabalhando muito para lembrar a sintaxe. No outro
Por outro lado, se você conhece bem a base de código, sua memória de trabalho e STM podem não ser
impactado pela busca e compreensão do código.
Mas se a base de código ou a linguagem (ou ambas) for menos familiar, a incrementação
pode ser uma tarefa difícil. Sempre que possível, tente dividir a tarefa de incrementação em
tarefas menores. Ser deliberado sobre qual subtarefa você está fazendo pode ajudá-lo a apoiar
o sistema de memória correto. Diga a si mesmo que você começará pesquisando os
informações, então compreendê-las, seguidas de adicionar o código necessário.
11.1.5 Exploração
A atividade final que Green, Blackwell e Petre discutem é explorar o código. Quando você é
realizando a atividade de exploração, você está essencialmente desenhando com código. Você
pode ter uma vaga ideia de onde você quer ir, mas programando você ganha clareza
sobre o domínio do problema e sobre as construções de programação que você
precisa usar.
Assim como na incrementação, ao explorar você provavelmente está executando várias tarefas diferentes
relacionadas à programação em rápida sucessão: escrever código, executar o código,
executando testes para ver se você está indo na direção certa, lendo o código existente e
potencialmente refatorando o código para torná-lo mais alinhado com seus planos recém-encontrados para ele.
Durante a exploração, é provável que você dependa muito de ferramentas no IDE, por exemplo,
executando testes para ver se pequenas alterações afetaram algum teste, usando refatoração automatizada
ferramentas, ou confiando em “encontrar dependentes” para navegar rapidamente pelo código.
Como a exploração também depende de atividades diferentes, é difícil para os três sistemas de memória,
mas especialmente para a memória de trabalho, porque você está fazendo planos e
projetos em tempo real durante a programação. Embora você possa sentir que documentar sua
planos vão atrapalhar seu fluxo e atrasá-lo, pode ser muito útil fazer algumas
notas aproximadas de sua direção de design ou decisões de design para que você possa liberar
espaço para pensar sobre o problema com mais profundidade.
EXERCÍCIO 11.1 Durante sua próxima sessão de codificação, reflita sobre as cinco atividades
da estrutura CDN. Qual atividade você está realizando na maior parte do tempo?
Que barreiras você encontrou ao pesquisar, compreender, transcrever, incrementar e explorar?
Exploração
Procurando
Compreensão
Transcrição
Incrementação
No capítulo 9, discutimos os dispositivos fNIRS como uma técnica para medir a carga cognitiva.
Com a ajuda do fNIRS, ganhamos não apenas uma compreensão de que tipo de
código causa carga cognitiva, mas também como a carga cognitiva é distribuída pelas tarefas.
1
Rini van Solingen et al., “Interrupções: Apenas um minuto nunca é”, IEEE Software, vol. 15, não. 5, https://ieeexplore
.ieee.org/document/714843.
2 Chris Parnin e Spencer Rugaber, “Estratégias de retomada para tarefas de programação interrompidas”, https://
ieeexplore.ieee.org/document/5090030.
3
Thomas D. LaToza et al., “Manutenção de modelos mentais: um estudo dos hábitos de trabalho do desenvolvedor”, https://dl.acm
.org/doi/10.1145/1134285.1134355.
Machine Translated by Google
Nakagawa encontrou dois resultados interessantes. Primeiro, para 9 dos 10 participantes, houve
uma grande variação na carga cognitiva durante a tarefa. A programação não era difícil o tempo
todo; algumas coisas eram difíceis, enquanto outras eram relativamente fáceis. Em segundo lugar,
os pesquisadores também analisaram o momento da tarefa em que ocorreu o maior aumento no
fluxo sanguíneo e, portanto, na carga cognitiva. Seus resultados mostraram que a carga cognitiva no
meio da tarefa foi maior.
Os resultados de Nakagawa podem sugerir que há uma espécie de fase de aquecimento e
resfriamento nas tarefas de compreensão do programa, entre as quais o trabalho mais difícil é
realizado. Os programadores profissionais provavelmente reconhecerão esse tempo de aquecimento
necessário para entrar “na zona” para construir um modelo mental do código e se preparar para
iniciar a atividade de transcrição. Conforme discutido anteriormente neste capítulo, pode ajudar a
delinear ativamente as subatividades em uma tarefa de programação maior.
4
Takao Nakagawa et al., “Quantificando a carga de trabalho mental dos programadores durante a compreensão do
programa com base na medição do fluxo sanguíneo cerebral: um experimento controlado”, https://posl.ait.kyushu-
u.ac.jp/ ~kamei/publications/Nakagawa_ ICSENier2014 .pdf.
Machine Translated by Google
Figura 11.3 Comentários de tarefas podem permanecer nas bases de código sem serem resolvidos.
5
Margaret Ann Storey et al., “TODO or to Bug: Exploring How Task Annotations Play a Role in the Work Practices of
Software Developers,” https://dx.doi.org/doi:10.1145/1368088.1368123.
Machine Translated by Google
SUBOBJETIVOS DA ETIQUETA
Uma terceira prática que pode ajudar a proteger contra interrupções é chamada de rotulagem de submetas,
em que você escreve explicitamente em quais pequenos passos um problema pode ser dividido. Por
Por exemplo, ao analisar e reestruturar o texto, essas etapas podem ser
Embora esses passos em si não sejam difíceis de imaginar ou lembrar, quando você
são interrompidos entre eles, pode ser complicado retornar ao código e
lembre-se do que você estava planejando fazer. Se programo uma tarefa maior, tento começar com
escrevendo as etapas no meu código como comentários, assim:
# analisa o texto
# recebe a árvore de
análise # filtra a árvore de análise e
# achata a árvore de volta ao formato de texto
Dessa forma, posso preencher pequenas partes do código e sempre tenho um plano para recorrer.
Um estudo sobre programadores de Lauren Margulieux, professora de tecnologias de aprendizagem em
do Departamento de Ciências da Aprendizagem da Georgia State University, mostrou que quando
você fornece submetas, os programadores as usam para organizar mentalmente a solução.7
Submetas são úteis para organizar seu próprio pensamento após uma interrupção, mas são
também útil em outras situações. Por exemplo, alguns dos subobjetivos colocados no código
podem permanecer como comentários e servir como documentação posteriormente. Eles também podem ser usados
em colaboração onde um programador sênior projeta os subobjetivos e outros programadores implementam partes
de uma solução maior.
6
https://marketplace.visualstudio.com/items?itemName=chrisparnin.attachables.
7
Lauren E. Margulieux et al., “Material instrucional rotulado com submetas melhora o desempenho e a transferência em
Aprendendo a desenvolver aplicativos móveis”, https://doi.org/10.1145/2361276.2361291.
Machine Translated by Google
Um exemplo é usar uma medida de tarefa dupla. Uma tarefa dupla é uma segunda tarefa que um participante
executa enquanto trabalha na tarefa original. Por exemplo, ao resolver uma equação matemática, em momentos
aleatórios uma letra A aparecerá na tela. Sempre que o A
aparece, o participante precisa clicar no A o mais rápido possível. Com que rapidez e
com precisão um participante pode executar a segunda tarefa é uma boa medida de quanto
carga cognitiva é experimentada. Pesquisadores mostraram que uma medição de dupla tarefa
pode ser uma boa maneira de estimar a carga cognitiva. Há desvantagens em usar um dual
tarefa, também, como você pode imaginar; a segunda tarefa em si também pode adicionar carga cognitiva,
interferindo na tarefa original.
Usando uma medição de tarefa dupla, a pesquisa explorou a conexão entre
carga cognitiva e interrupções. Brian P. Bailey, professor de ciência da computação na
da Universidade de Illinois, dedicou grande parte de sua carreira à compreensão do
causa e efeito das interrupções.
Em um estudo de 2001,8 50 participantes foram solicitados a realizar uma tarefa primária enquanto
sendo interrompido. O experimento utilizou diferentes tipos de tarefas, desde contar o
ocorrência de palavras em uma grade para ler algum texto e responder perguntas sobre o
texto. Ao executar essas tarefas primárias, os participantes foram interrompidos por
informações como manchetes de notícias de última hora e atualizações do mercado de ações. Este estudo
foi executado como um experimento controlado, com dois grupos. Um grupo recebeu as interrupções durante
uma tarefa primária, enquanto o outro grupo as recebeu após completar
uma tarefa.
Embora os resultados de Bailey não tenham sido surpreendentes, eles oferecem uma visão mais profunda
das interrupções. Bailey descobriu que leva mais tempo para concluir uma tarefa interrompida do que uma tarefa
não interrompida (sem contar o tempo da interrupção real) e que
as pessoas percebem que tarefas interrompidas são mais difíceis de completar do que uma tarefa não
interrompida.
Bailey não mediu apenas o tempo e a dificuldade das tarefas; também investigou o
estado emocional dos participantes. Os participantes responderam a perguntas sobre seu aborrecimento
e níveis de ansiedade cada vez que uma tarefa de interrupção aparecia. Os resultados desses
questões mostraram que o nível de incômodo experimentado é influenciado quando o
tarefa de interrupção é exibida. Participantes do grupo onde as interrupções
apareceram durante a tarefa primária ficaram mais irritados do que no grupo que viu o
interrupções após uma tarefa. O mesmo acontecia com a ansiedade. Os participantes cuja principal
tarefa foi interrompida experimentou mais ansiedade. Um estudo de acompanhamento de 2006 que utilizou um
configuração muito semelhante revelou que pessoas interrompidas também cometem o dobro de erros.9
Com base nesses resultados, pudemos concluir que pode ajudar os programadores, que
será inevitavelmente interrompido de vez em quando, para ser interrompido em momentos mais convenientes
8
Brian P. Bailey et al., “Os Efeitos das Interrupções no Desempenho da Tarefa, Aborrecimento e Ansiedade no Usuário
Interface,” http://mng.bz/G6KJ.
9
Brian P. Bailey, “Sobre a Necessidade de Sistemas de Atenção à Atenção: Medindo os Efeitos das Interrupções no
Desempenho da Tarefa, Taxa de Erros e Estado Afetivo”, Computers in Human Behavior, vo. 22, não. 4, pp. 685-708, 2006,
http://mng.bz/zGAA.
Machine Translated by Google
vezes (por exemplo, depois de ter completado uma tarefa). Com base nessa ideia, Manuela Züger,
então estudante de doutorado na universidade de Zurique, desenvolveu uma luz interativa chamada
FlowLight.
O FlowLight (https://emea.embrava.com/pages/flow) é uma luz física que um desenvolvedor
pode colocar em sua mesa ou em cima de sua tela. Com base nas interações do computador, como
velocidades de digitação e cliques do mouse, o FlowLight detecta se o programador está
profundamente engajado em uma tarefa e experimentando alta carga cognitiva. Coloquialmente, esse
estado às vezes também é chamado de “no fluxo” ou “na zona”. Quando o programador está muito
na zona e não deve ser interrompido, o FlowLight piscará em vermelho. Com um pouco menos de
atividade, o FlowLight brilhará continuamente em vermelho. Se um desenvolvedor parecer disponível,
a luz ficará verde.
Züger experimentou o FlowLight em um estudo de campo em larga escala com mais de 400
participantes de 12 países e descobriu que o FlowLight pode reduzir as interrupções em 46%.
Muitos participantes do estudo continuaram usando o FlowLight após o término do experimento, e o
FlowLight agora está disponível como um produto comercial.10
10Manuela Züger, “Reducing Interruptions at Work: A Large Scale Field Study of FlowLight,” https://
www.zora.uzh.ch/id/eprint/136997/1/FlowLight.pdf .
Machine Translated by Google
Resumo
ÿ Quando você está programando, você executa uma combinação de diferentes atividades de programação:
pesquisa, compreensão, transcrição, incremento e
exploração. Cada atividade exerce pressão sobre diferentes sistemas de memória. Portanto, cada
atividade deve ser apoiada por diferentes técnicas.
ÿ As interrupções durante a programação não são apenas irritantes; são prejudiciais à produtividade porque
leva tempo para reconstruir seu modelo mental de
o código.
ÿ Para lidar melhor com interrupções, transfira modelos mentais em notas, documentos
tação ou comentários.
ÿ Apoie deliberadamente sua memória prospectiva se você não conseguir completar uma tarefa
documentar seus planos.
ÿ Tente limitar as interrupções aos momentos em que você experimenta baixa carga cognitiva, por
exemplo, através da automação usando um FlowLight ou manualmente, definindo seu
status no Slack.
11Annie Beth Fox et al., “Distrações, distrações: as mensagens instantâneas afetam o desempenho dos estudantes
universitários em uma tarefa de compreensão de leitura simultânea? Ciberpsicologia e Comportamento, vol. 12, pp. 51–53, 2009.
https://doi.org/10.1089/cpb.2008.0107.
12Paul A. Kirschner e Aryn C. Karpinski, “Facebook® and Academic Performance,” Computers in Human Behav ior, vol.
26, não. 6, pp. 1237-1245, 2010, http://mng.bz/jBze.
13LingBei Xu, “Impact of Simultaneous Collaborative Multitasking on Communication Performance and
Experience, 2008, http://mng.bz/EVNR.
Machine Translated by Google
Projetando e
melhorar sistemas maiores
Até agora, neste livro, discutimos a melhor maneira de ler e escrever código. Para isso, nós
examinaram como os processos cognitivos desempenham um papel ao ler e escrever código.
Para bases de código maiores, no entanto, não são apenas pequenas partes do código que influenciam como
é fácil para as pessoas compreendê-lo. A maneira como você organiza o código também
influencia muito a facilidade com que outras pessoas podem interagir com o código. Isso é especialmente
verdadeiro para código em bibliotecas, frameworks e módulos que outros programadores
usar em vez de mudar.
Muitas vezes, quando falamos sobre bibliotecas, frameworks e módulos, falamos sobre
seus aspectos técnicos, como a linguagem em que são criados. No entanto, as bases de código
191
Machine Translated by Google
também pode ser visto através de uma lente cognitiva. Neste capítulo, discutiremos a CDN, que é
uma técnica para examinar as bases de código de uma perspectiva cognitiva. A CDN ajuda você a
responder a perguntas sobre grandes bases de código existentes, como “Este código será fácil para
as pessoas mudarem?” ou “Esta base de código será fácil para as pessoas encontrarem informações?”
Examinar as bases de código de uma perspectiva cognitiva em vez de técnica pode ajudá-lo a obter
uma melhor perspectiva de como as pessoas interagem com seu código.
Depois de discutirmos a CDN e estudarmos como ela pode apoiar nossa compreensão das
bases de código, veremos como usá-la para melhorar o design das bases de código existentes
usando uma estrutura adaptada chamada dimensões cognitivas das bases de código (CDCB).
No capítulo anterior descrevemos cinco atividades de programação diferentes. Este capítulo
também examinará como as propriedades de uma base de código influenciam as diferentes
atividades de programação de diferentes maneiras.
EXERCÍCIO 12.1 Pense em uma base de código que você usou recentemente e que você
mesmo não escreveu. Pode ser uma biblioteca que você usa, da qual você teve que ler o
código para entender como chamar uma função, ou uma estrutura na qual você corrigiu um bug.
também, daí o nome. Linguagens de programação, como fluxogramas, podem ser vistas como notações, uma
forma de expressar pensamentos e ideias.
As dimensões como Green, Blackwell e Petre as descrevem aplicam-se apenas a notações, mas neste livro
generalizamos o uso de CDN para bases de código em vez de linguagens de programação. Chamamos essa
versão de dimensões cognitivas de bases de código (CDCB)
e usá-lo para examinar uma base de código para entender como ele pode ser entendido e
diferentes dimensões interagem umas com as outras e como podemos usá-las para melhorar
bases de código existentes.
PROPENSÕES A ERROS
A primeira dimensão a ser discutida é chamada de propensão ao erro. Em algumas linguagens de programação
é mais fácil cometer um erro do que em outras linguagens. JavaScript está atualmente
uma das linguagens mais populares, mas é conhecida por ter alguns cantos excêntricos
casos.
Hanenberg tentou vários métodos para melhorar o código digitado dinamicamente para
torná-lo menos propenso a erros, incluindo melhor suporte e documentação IDE,
mas mesmo nessas situações, os sistemas do tipo estático superaram os dinâmicos
em termos de tempo e precisão dos programadores encontrando bugs.
Machine Translated by Google
CONSISTÊNCIA
Outra maneira de examinar como as pessoas irão interagir com uma linguagem de programação ou base
de código é a consistência: quão semelhantes são as coisas? Os nomes são sempre estruturados da
mesma maneira, por exemplo, usando os mesmos moldes de nome que discutimos no capítulo 8? O
layout dos arquivos de código é semelhante para classes diferentes?
Um exemplo de situação em que muitas linguagens de programação apresentam consistência é na
definição de funções. Talvez você nunca tenha pensado nisso, mas as funções internas normalmente
têm a mesma interface do usuário que as funções definidas pelo usuário. Quando você está olhando
para uma chamada de função como print() ou print_customer(), você não pode ver na chamada quem
criou a função, o criador da linguagem de programação ou o criador do código.
Uma estrutura ou linguagem que é inconsistente no uso de nomes e convenções pode levar a uma
maior carga cognitiva, porque levará mais energia ao seu cérebro para entender o que é o quê, e pode
levar mais tempo para encontrar informações relevantes.
A consistência está relacionada à propensão a erros, como vimos no capítulo 9. Códigos nos quais
ocorrem antipadrões linguísticos (por exemplo, nomes que não correspondem à implementação do
código) são mais propensos a erros e causam maior carga cognitiva.
DIFUSÃO
Em um capítulo anterior, abordamos os cheiros de código, que podem tornar o código mais difícil de ler.
Um cheiro de código bem conhecido é um método longo, no qual um método de uma função consiste em
muitas linhas de código, tornando-o mais difícil de entender.
Um método longo pode ser culpa do programador se ele adicionar complexidade desnecessária a
um método ou tentar encaixar muita funcionalidade em um método. No entanto, algumas linguagens de
programação também precisam de mais espaço do que outras para a mesma funcionalidade.
A difusão da dimensão cobre isso. A difusão refere-se a quanto espaço ou espaço uma construção de
programação ocupa.
Por exemplo, um loop for em Python se parece com isso:
Essa diferença entre o número de pedaços está no fato de que existem elementos
no código C++ que não estão presentes na versão Python (por exemplo, i++).
Nós também podemos ter versões mais e menos difusas do mesmo código dentro da mesma linguagem
de programação. Cobrimos o exemplo de compreensões de lista em Python nos capítulos anteriores; aqui
novamente estão dois trechos de código Python que fazem a mesma coisa:
california_braches = []
para filial em filiais:
if branch.zipcode[0] == '9'
california_branches.append(branch)
A segunda versão do código é menos difusa, o que pode afetar a legibilidade e a compreensão.
DEPENDÊNCIAS OCULTAS
A dimensão de dependências ocultas indica até que ponto as dependências são visíveis para o usuário. Um
exemplo de sistema com altas dependências ocultas é uma página HTML com um botão controlado por
JavaScript, que é armazenado em um arquivo diferente. Pode ser difícil ver no arquivo JavaScript quais páginas
HTML chamam a função em tal situação.
Outro exemplo são os requisitos para arquivos separados dos arquivos de código. A partir da base de código,
pode ser difícil ver todas as bibliotecas e estruturas que precisam ser instaladas para que o código seja
executado corretamente.
Em geral, no código, as funções que estão sendo chamadas dentro de outra função ou classe são mais
visíveis do que o contrário: quais funções ou classes chamam uma determinada função. No primeiro caso,
sempre podemos ler o texto de uma função e ver quais funções estão sendo chamadas no corpo.
Embora os IDEs modernos possam revelar dependências ocultas, como mostrado na figura 12.2, encontre
A configuração das dependências ainda requer o uso de cliques do mouse ou atalhos.
Figura 12.2 Opção no PyCharm para encontrar todos os locais de chamada de uma determinada função
Machine Translated by Google
Os criadores de código podem compensar dependências ocultas com documentação mais extensa. As equipes podem
considerar ter uma política para discutir e adotar novas
dependências, bem como documentá-las quando a adoção ocorrer.
PROVISIONALIDADE
Se uma base de código ou linguagem de programação for muito restrita (por exemplo, usando tipos,
afirmações e pós-condições), pode ser difícil usar código para expressar um pensamento. Nós
então diga que essa ferramenta tem baixa provisoriedade.
A provisoriedade é um fator essencial na capacidade de aprendizado porque expressar ideias vagas
e código incompleto pode ser necessário se você for iniciante em um determinado sistema.
Pensar em um plano para seu código enquanto pensa em tipos e sintaxe pode causar
muita carga cognitiva em iniciantes.
VISCOSIDADE
Relacionada à provisoriedade está a viscosidade: quão difícil é fazer mudanças em um determinado sistema.
Normalmente, as bases de código escritas em linguagens tipadas dinamicamente são um pouco mais fáceis de mudar.
Você pode simplesmente alterar o código e não precisa alterar todas as definições de tipo correspondentes. O código
que não é muito modular e contém grandes blocos de código também pode ser mais fácil de alterar porque pode ser
alterado diretamente e você não precisa fazer
alterações em vários lugares para várias funções ou classes.
A facilidade de mudança de um sistema depende não apenas da linguagem de programação e da própria base
de código; fatores que cercam a base de código também afetam a viscosidade.
Por exemplo, se levar muito tempo para uma base de código compilar ou executar testes, isso aumenta
viscosidade de cada mudança.
AVALIAÇÃO PROGRESSIVA
Uma dimensão relacionada à provisoriedade é a avaliação progressiva. A dimensão da avaliação progressiva descreve
quão fácil é em um determinado sistema verificar ou executar um trabalho parcial. Como vimos, um sistema com muita
provisoriedade permite que o usuário esboce
ideias incompletas. Um sistema com avaliação progressiva permite ao usuário também executar códigos incompletos
ou imperfeitos.
Machine Translated by Google
EXPRESSIVIDADE DO PAPEL
Vimos um conceito semelhante no capítulo 9, em antipadrões linguísticos. Quando uma base de código
sofre de antipadrões linguísticos, construções como funções e métodos enganam o
leitor sobre seu papel. Isso significa que a base de código tem menor expressividade de papel e
pode ser mais difícil de entender.
PROXIMIDADE DO MAPEAMENTO
pensava que APL era uma linguagem muito confusa, ela, de fato, tem uma grande proximidade do ping de
mapeamento com o domínio do cálculo vetorial.
22222ÿn
Um programa convertendo o número n em representação binária em
APL. A confusão aqui está no fato de que você pode não saber o que ÿ significa.
Por exemplo, todas as variáveis são, por padrão, vetores, como podemos ver na listagem 12.1 do
fato de que T funciona na lista de 2s. Este design é bom se você está acostumado a pensar em vetores e se os
problemas que você está resolvendo podem ser resolvidos com o uso de cálculo vetorial. COBOL também é
frequentemente nomeado como uma linguagem com uma boa proximidade de mapeamento para
domínio dos negócios e das finanças. O Excel é outro exemplo de linguagem de programação com uma boa
aproximação de mapeamento. O layout em linhas e colunas é precisamente
como os cálculos financeiros eram feitos antes mesmo de termos computadores.
A maioria das linguagens de programação modernas, incluindo Java, Python e JavaScript, não
não ter uma boa proximidade de mapeamento; não há problemas com os quais não possamos resolver
essas línguas. Claro que nem sempre isso é ruim. Pode ser muito útil ser
capaz de resolver qualquer problema com Python ou Java, e não ter que aprender um novo
linguagem de programação para cada novo projeto ou cliente.
As bases de código também podem ter uma boa proximidade de mapeamento para seu domínio de negócios.
Bases de código que reutilizam conceitos e palavras de seu domínio de destino são normalmente mais fáceis de
entender para os clientes do que bases de código que usam termos mais genéricos. Por exemplo,
um método chamado executeQuery() tem uma proximidade de mapeamento menor do que a função
encontrarClientes().
Em nosso campo, nos últimos anos, temos visto um interesse crescente em incorporar melhor o domínio no
código. Por exemplo, a filosofia de design orientado ao domínio prescreve que a estrutura e os identificadores no
código devem corresponder ao domínio de negócios.
EXERCÍCIO 12.2 Faça uma lista de todos os nomes de variáveis, funções e classes em seu
base de código. Para cada um dos nomes, investigue a proximidade do mapeamento. Você
pode fazer a si mesmo estas perguntas para cada nome de identificador:
Alguns sistemas exigem que o usuário pense muito, execute operações mentais difíceis fora
do sistema. Por exemplo, uma linguagem como Haskell exige que o usuário pense em tipos
de todas as funções e parâmetros. Você não pode ignorar as assinaturas de tipo de funções ou isso
ser quase impossível escrever código de trabalho em Haskell. Da mesma forma, C++ requer que o usuário
usar ponteiros em muitas situações e raciocinar com eles em vez de objetos.
Machine Translated by Google
É claro que as operações mentais difíceis não são de todo ruins. O pensamento que você precisa de um
usuário fazer pode compensar, por exemplo, em menos erros em um sistema de tipo estrito ou em melhor
desempenho ou uso de memória mais eficiente em ponteiros.
No entanto, quando você pede ao usuário para realizar essas operações mentais difíceis em um sistema
que você projeta, você deve estar ciente disso e considerar as operações com grande
Cuidado.
Exemplos de operações mentais difíceis que as pessoas podem realizar dentro de uma base de código são
muitas vezes situações que exigem muito da memória dos usuários. Por exemplo, pedir aos usuários que
memorizar um grande número de parâmetros para chamar na ordem certa é uma tarefa mental difícil
operação porque coloca uma grande demanda em STM.
Vimos que nomes de funções vagas têm pouca proximidade de mapeamento. Esses nomes
também criar trabalho mental árduo. Ter que memorizar nomes não informativos de funções como execute()
ou control() requer que essas funções sejam armazenadas na memória do usuário.
LTM e, portanto, também podem ser operações mentais difíceis.
Finalmente, algumas operações são difíceis porque prejudicam a memória de trabalho,
por exemplo, se os dados tiverem que ser baixados de duas fontes diferentes em dois formatos diferentes e
convertidos em um terceiro formato. O usuário terá então que acompanhar
dos diferentes fluxos e seus tipos correspondentes.
NOTAÇÃO SECUNDÁRIA
Adicionar um parâmetro nomeado a uma chamada de função em Python não altera a maneira como o
código é executado, mas permite que o IDE expresse a função de cada parâmetro
quando a função é chamada.
Machine Translated by Google
ABSTRAÇÃO
A dimensão de abstração descreve se um usuário do seu sistema pode criar suas próprias abstrações que
são tão poderosas quanto as abstrações internas. Um exemplo de abstrações que a maioria das linguagens
de programação permite é a criação de funções, objetos ou classes. Os programadores podem criar funções,
que são em muitos aspectos semelhantes às funções internas. As funções definidas pelo usuário podem ter
parâmetros de entrada e saída e funcionar da mesma forma que as funções normais. O fato de que os usuários
podem criar funções significa que os usuários podem moldar a linguagem com seus próprios blocos de
construção e adicionar suas próprias abstrações. Embora o poder de fazer suas próprias abstrações esteja
agora disponível em quase todas as linguagens, muitos programadores hoje nunca trabalharam em sistemas
de programação pré-estruturados como assembly ou algum dialeto BASIC onde tais mecanismos de abstração
não estavam disponíveis.
Bibliotecas e frameworks também podem oferecer a seus usuários a opção de criar suas próprias
abstrações. Por exemplo, permitir que um usuário de biblioteca crie uma subclasse à qual funcionalidade
adicional pode ser adicionada tem mais poder de abstração do que uma biblioteca que permite apenas
chamadas de API.
VISIBILIDADE
A visibilidade indica como é fácil ver as diferentes partes de um sistema. Em uma base de código, pode ser
difícil ver em quais classes a base de código consiste, especialmente se o código estiver dividido em arquivos
diferentes.
Bibliotecas ou frameworks também podem oferecer a seus usuários diferentes níveis de visibilidade. Por
exemplo, uma API que busca dados pode retornar uma string, um arquivo JSON ou um objeto, cada um com
uma visibilidade diferente. Se uma string for retornada, é mais difícil ver a forma dos dados e a estrutura
oferece menor visibilidade ao usuário.
Examinamos diferentes dimensões que os programas podem ter. Essas diferenças podem impactar muito a
forma como as pessoas interagem com uma base de código. Por exemplo, se uma base de código tiver uma
alta viscosidade, os futuros desenvolvedores que trabalham na base de código podem relutar em fazer
alterações. Isso pode levar a patches mais complicados em vez de mudanças profundas na estrutura da base
de código. Se uma base de código de código aberto requer operações mentais difíceis, as pessoas podem ser
menos propensas a se tornarem mantenedoras. Portanto, é importante ter uma noção do desempenho de sua
base de código nas diferentes dimensões.
A lista de dimensões cognitivas pode ser usada como uma espécie de checklist para uma base de código.
Nem todas as dimensões são importantes para todas as bases de código, mas investigar regularmente cada
uma delas e decidir como sua base de código está se saindo ajudará você a manter a usabilidade. Idealmente,
você analisa as dimensões de uma base de código regularmente (por exemplo, uma vez por ano).
EXERCÍCIO 12.3 Preencha a tabela a seguir para entender as dimensões em jogo. Quais
dimensões são importantes para sua base de código? Qual deles pode ser melhorado?
Machine Translated by Google
Propensão a erros
Consistência
Visibilidade
Dependências ocultas
Provisório
Viscosidade
Avaliação progressiva
Expressividade do papel
Proximidade do mapeamento
Notação secundária
Abstração
Visibilidade
EXERCÍCIO 12.4 Examine a lista que você criou no exercício 12.3 para as dimensões que
podem ser melhoradas. Você vê manobras de design que você poderia aplicar?
Qual seria o efeito dessas manobras em outras dimensões?
Muitas vezes, uma manobra de projeto (ou seja, uma mudança em uma dimensão) causa mudanças
Outra dimensão. Como as dimensões interagem com precisão pode depender muito
sua base de código, mas existem algumas dimensões que geralmente estão em desacordo umas com as outras.
Machine Translated by Google
Se você quiser evitar que o usuário de sua biblioteca ou estrutura cometa erros, geralmente fará isso
fazendo com que o usuário insira informações adicionais. O exemplo mais conhecido de uma dimensão
que diminui a propensão a erros é permitir que um usuário adicione tipos a entidades. Se o compilador
souber o tipo de uma entidade, essa informação pode ser usada para evitar erros, como adicionar
acidentalmente uma lista a uma string.
No entanto, quando tudo em um sistema é digitado, isso pode representar um trabalho extra para
o usuário. Por exemplo, você pode precisar converter variáveis em um tipo diferente para poder usá-las
da maneira desejada. Quando as pessoas não gostam de sistemas de tipos, mesmo tendo em vista
seu benefício na prevenção de tipos, isso geralmente se deve à viscosidade extra que um sistema de
tipos adiciona.
PROVISIONALIDADE E AVALIAÇÃO PROGRESSIVA VS. PROPENSÕES A ERROS
Um sistema com muita provisoriedade e avaliação progressiva permite que o usuário esboce e execute
código incompleto ou imperfeito. Embora essas dimensões possam ajudar alguém a pensar sobre o
problema em questão, programas incompletos podem não ser excluídos e programas imperfeitos
podem nunca ser aprimorados, levando a um código difícil de entender e, portanto, difícil de depurar,
impactando a propensão a erros.
PAPEL EXPRESSIVIDADE VS. DIFUSÃO
Vimos que a expressividade do papel pode ser criada adicionando elementos sintáticos adicionais,
como parâmetros de nomes. No entanto, os rótulos extras fazem com que o código seja mais longo. O
mesmo vale para anotações de tipo, que também expressam os papéis que as variáveis desempenham,
mas aumentam o tamanho de uma base de código.
PESQUISA Ao
pesquisar, algumas dimensões desempenham um papel importante. Por exemplo, dependências ocultas podem prejudicar a atividade de
pesquisa, pois se você não souber qual código é chamado de onde, pode ser difícil decidir o que ler em seguida e, assim, desacelerar a
pesquisa. A difusão faz com que o código seja mais longo, o que também prejudica a pesquisa simplesmente porque há mais código para
pesquisar.
Machine Translated by Google
Por outro lado, a notação secundária pode ajudar na busca porque comentários e
nomes de variáveis podem indicar onde as informações podem ser encontradas.
COMPREENSÃO
Ao transcrever (ou seja, implementar um recurso com base em um plano predefinido) alguns
dimensões que de outra forma são boas podem ser prejudiciais – por exemplo, consistência. Enquanto
uma base de código consistente pode ser mais fácil de compreender, você terá que fazer o novo
o código se encaixa na base de código ao implementar um novo recurso, o que pode levar a
esforço mental. Claro, esse esforço pode valer a pena a longo prazo, mas ainda é
esforço que precisa ser gasto.
INCREMENTAÇÃO
A adição de novos recursos a uma base de código é suportada principalmente pela proximidade do mapeamento para o
domínio. Se a base de código permite pensar no objetivo do código, em vez de
conceitos de programação, será mais fácil adicionar novo código. Bases de código com alta viscosidade,
por outro lado, dificultam a adição de código.
EXPLORAÇÃO
Explorar novas ideias de design enquanto na base de código (ou seja, explorar) é mais suportado
por sistemas que têm boa provisoriedade e avaliação progressiva.
Operações mentais difíceis e abstrações podem prejudicar a exploração porque colocam
uma alta carga cognitiva no programador, limitando a carga que pode ser gasta em
explorando o problema e o espaço da solução.
Tabela 12.1 Visão geral das dimensões e as atividades que elas apoiam ou prejudicam
Difusão Procurando
Provisório Exploração
Tabela 12.1 Visão geral das dimensões e as atividades que elas apoiam ou prejudicam (continuação)
Visibilidade Compreensão
EXERCÍCIO 12.5 Pense em sua base de código. Quais atividades são mais prováveis
ocorrer? As atividades se estabilizaram nos últimos meses? Quais dimensões desempenham um
papel nessas atividades e como sua base de código funciona
essas dimensões?
Resumo
ÿ CDN é uma estrutura que ajuda os programadores a prever o efeito cognitivo
linguagens gramaticais terão em seus usuários.
ÿ CDCB é uma extensão do CDN que ajuda os programadores a entender o impacto
suas bases de código, bibliotecas e frameworks terão em seus usuários. ÿ Em muitos
Até agora, examinamos como ler e organizar o código. Como um desenvolvedor mais sênior, no entanto, você
provavelmente lutará com sua própria confusão e com a confusão de outras pessoas mais juniores com quem
trabalha. Em muitos casos, você vai querer gerenciar a carga cognitiva que os juniores estão experimentando para
garantir que eles aprendam de forma mais eficaz.
Neste capítulo, examinaremos como melhorar seu processo de integração, seja para integrar um desenvolvedor
experiente em uma base de código desconhecida ou um programador iniciante.
Para fazer isso, primeiro examine como especialistas e iniciantes pensam e se comportam de maneira diferente.
Em seguida, abordaremos uma variedade de atividades que uma equipe pode realizar para integrar uma nova equipe
205
Machine Translated by Google
membros. Ao final deste capítulo, você estará familiarizado com três técnicas e
atividades para apoiar os recém-chegados de forma mais eficaz.
onde você diz: "Uau, isso é fácil!" podem ser momentos em que você cai na maldição de
perícia. A primeira coisa que você pode fazer para tornar o processo de integração mais fácil é perceber que
provavelmente não é tão fácil para a pessoa que está aprendendo.
Para entender melhor o comportamento dos programadores iniciantes, vamos considerar o útil
arcabouço psicológico do neopiagetismo, que explica o comportamento das pessoas quando confrontadas com novas
informações. O Neo-Piagetismo baseia-se no trabalho de Jean Piaget, um influente psicólogo do desenvolvimento que
se concentrou nos quatro estágios de desenvolvimento para
crianças pequenas. Para nossos propósitos, o neo-piagetismo descreve como os programadores se comportam
quando eles estão apenas começando a conhecer uma linguagem de programação, base de código ou paradigma.
Antes que eu possa ensinar como os programadores se comportam em cenários de aprendizado desconfortáveis, vamos
repassar os estágios comportamentais que começaram na infância. primeiro tenho que explicar
O modelo original de Piaget para crianças pequenas, mostrado na tabela 13.1. No primeiro nível,
que descreve o comportamento de crianças de 0 a 2 anos de idade, as crianças não podem
fazer planos ou supervisionar situações. Eles simplesmente experimentam as coisas (sentido) e agem
(motor) sem muita estratégia envolvida. No segundo nível, quando as crianças
entre 2 e 7 anos de idade, as crianças começam a formular hipóteses, mas essas hipóteses
muitas vezes não são muito fortes. Por exemplo, uma criança de 4 anos pode levantar a hipótese de que está chovendo
porque as nuvens são tristes. Isso não é exatamente correto, mas você pode ver que eles estão tentando
encontrar explicações para suas observações.
Machine Translated by Google
Na terceira fase, dos 7 aos 11 anos, as crianças começam a formular hipóteses sobre as quais
podem raciocinar, mas apenas em situações concretas. Eles podem, por exemplo, decidir sobre uma boa
se movem em um jogo de tabuleiro, mas acham difícil generalizar seus pensamentos e razões sobre
se esse movimento será sempre bom para tabuleiros diferentes. Esse tipo de formalidade
raciocínio acontece no estágio final – o estágio operacional concreto – quando as crianças
têm mais de 11 anos.
Estágio pré-operacional As crianças começam a formular hipóteses e planos, mas não 2-7 anos
os utilizam de forma confiável para pensar.
Estágio operacional concreto As crianças podem raciocinar sobre coisas concretas que 7-11 anos
podem ver, mas acham difícil tirar conclusões gerais.
Estágio operacional formal As crianças podem se envolver em raciocínio formal. 11 anos e mais velhos
O modelo de Piaget recebeu algumas críticas, principalmente porque ele usou seus próprios filhos para
criar o modelo. No entanto, seu trabalho lançou as bases para o neo-piagetismo, que tem grande
valor na compreensão do pensamento de programadores iniciantes. A ideia central de
neo-Piagetismo é que os níveis de Piaget não são gerais, mas específicos de domínio. Pessoas
pode operar no estágio operacional formal em um domínio, como programação em Java,
enquanto eles ainda se comportam no nível sensório-motor para programação em Python. Pode
mesmo que alguém opere no estágio operacional formal por um certo
base de código, mas volta para um nível inferior em uma nova base de código. A Tabela 13.2 descreve o
modelo neopiagetiano e suas implicações para a programação, conforme descrito por Australian
professor Raymond Lister.1
Tabela 13.2 Visão geral dos estágios neo-piagetianos de desenvolvimento e comportamento de programação correspondente
Estágio pré- As crianças começam a formular hipóteses e O programador pode prever manualmente de forma confiável o
operacional planos, mas não os utilizam de forma confiável. resultado de várias linhas de código, por exemplo, criando uma
tabela de rastreamento. Um programador pré-operacional muitas
vezes faz suposições sobre o que um pedaço de código faz.
1
Raymond Lister, “Rumo a uma epistemologia de desenvolvimento da programação de computadores”, https://dl.acm.org/
doi/10.1145/2978249.2978251.
Machine Translated by Google
Tabela 13.2 Visão geral dos estágios neo-piagetianos de desenvolvimento e comportamento de programação correspondente
Concreto As crianças podem raciocinar sobre contra O programador raciocina sobre o código dedutivamente
estágio operacional criam coisas que podem ver, mas acham difícil lendo o próprio código em vez de usar a abordagem indutiva
tirar conclusões gerais. pré-operacional.
Formal As crianças podem se envolver em O programador pode agora raciocinar de forma lógica,
estágio operacional raciocínio formal. consistente e sistemática.
No primeiro nível, ilustrado pelo programador mais à esquerda na figura 13.1, os programadores
não podem rastrear corretamente um programa (ou seja, eles não podem criar uma tabela de rastreamento como
descrito no capítulo 4). O comportamento nesta fase é comum para pessoas que não têm ou
pouca experiência em programação, mas também pode ocorrer quando os programadores alternam entre
linguagens muito diferentes (por exemplo, de JavaScript a Haskell). Como a execução do programa é tão
diferente nessas duas linguagens, um programador experiente em JavaScript
pode ter dificuldades em rastrear um programa Haskell. Porque eles estão tão focados no
código, que para eles ainda não é fácil de compreender, explicando princípios gerais
separado do código não é uma maneira útil de ensinar. Por exemplo, um programador sensório-motor que está
percorrendo o código do banco de dados não será ajudado se você começar a explicar
como o banco de dados está configurado em outro lugar no código. Primeiro, eles precisam entender o modelo
de execução.
O segundo estágio é o estágio pré-operacional, no qual um programador pode rastrear
pequenos pedaços de código, mas essa também é a única maneira de raciocinar sobre o código: usando
suas habilidades de rastreamento recém-adquiridas. Programadores pré-operacionais acham difícil explicar
o significado desse mesmo código. O programador pré-operacional é muito focado em
o próprio código e acha difícil olhar para outros artefatos, principalmente diagramas.
Apoiar programadores pré-operacionais em sua leitura ou escrita de código dando
os diagramas não serão úteis. Porque esses programadores raciocinam indutivamente
sobre o código, eles geralmente adivinham o comportamento do código com base em alguns traços.
A segunda etapa, eu acho, é mais frustrante para os programadores, mas também para os
pessoas integrando-as ou ensinando-as. Como é difícil para os programadores no estágio pré-operacional
entender o significado mais profundo do código, eles geralmente estão adivinhando.
Isso pode fazer com que os programadores neste nível pareçam erráticos. Às vezes, seus palpites são
no local, com base no conhecimento prévio que transfere (ou sorte), mas cinco minutos depois eles
expressar ideias totalmente irracionais. Esta pode ser a situação em que a pessoa que está sendo
onboarded fica frustrado e acha que o programador júnior não é inteligente ou não está dando o seu melhor. No
entanto, o estágio pré-operatório é um estágio necessário para avançar
para a próxima etapa. Treinar recém-chegados expandindo seu vocabulário de código com cartões flash pode
ajudá-los a avançar.
Machine Translated by Google
x = 12
y=5
Eu não sei o z se torna y. Os valores
z= Eu não sei o
yy = x que isso que isso y se torna x. de xey são
x=z faz. x se torna z. trocados.
faz.
Figura 13.1 Visão geral dos quatro diferentes níveis neo-piagetianos para programação
No terceiro estágio, os programadores operacionais concretos podem raciocinar sobre o código sem
rastreá-lo meticulosamente. Eles fazem isso usando conhecimento prévio: reconhecendo partes
familiares no código, lendo comentários e nomes e rastreando código apenas quando necessário (por
exemplo, ao depurar). Lister observa em seu estudo que é útil para os programadores usarem
diagramas para apoiar seu pensamento apenas no estágio operacional concreto. Os programadores
operacionais concretos começam a se comportar como programadores apropriados: eles podem
raciocinar sobre o código e podem fazer um plano e executá-lo ao escrever o código. No entanto, às
vezes eles ainda podem não ter o entendimento global de uma base de código e também podem lutar
com a reflexão sobre se algo é uma boa estratégia a seguir. Isso pode se mostrar em um
comprometimento excessivo com uma primeira estratégia (por exemplo, um programador júnior que
tentou por um dia inteiro corrigir um determinado bug e continua falhando e tentando novamente, em
vez de recuar e refletir se a estratégia escolhida é a correta 1).
A etapa final é a operação formal. Este é um programador experiente que pode raciocinar sobre
código e sobre seu próprio comportamento confortavelmente e, portanto, não é tão interessante para o
processo de integração. Esses programadores provavelmente ficarão à vontade para aprender os
detalhes das bases de código e poderão pedir ajuda quando necessário.
Os quatro estágios são apresentados como estágios distintos, mas na realidade não são. Ao aprender
um novo conceito de programação ou um novo aspecto de uma base de código, os alunos podem
voltar temporariamente para um nível inferior. Se alguém puder ler de forma confiável as funções do Python
Machine Translated by Google
sem rastreamento, mas é então exposto a funções variádicas usando *args, eles podem
precisam rastrear algumas chamadas de função para ver o que acontece antes que possam confortavelmente
leia-os sem rastrear novamente.
2 Karl Maton, “Fazendo Ondas Semânticas: Uma Chave para a Construção Cumulativa do Conhecimento”, Linguística e Educação, vol.
26, não. 1, pp. 8–22, 2013, https://www.sciencedirect.com/science/article/pii/S0898589812000678.
Machine Translated by Google
Depois que os iniciantes viram o que o conceito faz em geral, eles seguem a curva
down, um processo conhecido como unpacking. O iniciante está então pronto para aprender detalhes sobre
o conceito. Por exemplo, eles agora podem aprender que um * é usado para indicar uma variável
função em Python e que Python implementa a lista de argumentos como uma lista, então em
na realidade não há argumentos múltiplos; há um argumento que pode conter todos
argumentos da função como elementos.
Finalmente, o iniciante precisa voltar ao nível abstrato, afastando-se
de detalhes e se sentir confortável sabendo como o conceito funciona em geral. Isto
fase é chamada de reembalagem. Quando um conceito é devidamente reembalado, o aluno pode pensar
sobre isso sem focar em detalhes concretos. A reembalagem também envolve a integração do
conhecimento no LTM em relação ao conhecimento prévio – por exemplo, “C++ suporta
funções variádicas, mas Erlang não.”
Compreensão abstrata
Funções Variádicas em diferentes contextos
tem vários argumentos. e idiomas
Resumo
Nível
de Usar *
Desembalagem Reembalagem
abstração
Args são listas.
Concreto
Tempo
Figura 13.2 A onda semântica representando a explicação ideal que começa abstratamente,
permite que o aluno descompacte o conhecimento passando por detalhes concretos e
permite que o aluno reembale o conhecimento aprendido em seu LTM.
Para iniciantes, existem três antipadrões distintos que eles usam em sua profundidade e uma variedade
de explicações, cada uma ilustrada pela figura 13.3. O primeiro antipadrão é chamado de
high flatline e significa que você só usa termos abstratos. Um recém-chegado ao Python pode aprender
que o Python tem funções variáveis e por que elas são úteis, mas se eles nunca virem o
sintaxe, há muito o que aprender mais tarde.
O segundo antipadrão é o inverso, uma linha plana baixa. Alguns especialistas sobrecarregam os
iniciantes com detalhes sem explicar por que o conceito é relevante e útil. Iniciando
com “Você pode fazer uma função variadic com um * e então o Python vê todos os argumentos como
uma lista” não vai significar muito quando o iniciante não sabe quando usar um
função variada.
Machine Translated by Google
Resumo
Linha plana alta Escadas rolantes para baixo
Figura 13.3 Três antipadrões diferentes: flatlining alto (apenas explicações abstratas), flatlining baixo
(apenas explicações concretas) e escadas rolantes descendentes (começando de cima para baixo, mas
esquecendo de deixar espaço para reembalagem)
EXERCÍCIO 13.2 Escolha um conceito que você conheça bem e encontre explicações que se encaixem
todos os três locais na onda semântica, como mostrado na figura 13.1.
idioma e talvez até mesmo o domínio, realizar muitas tarefas diferentes é desnecessariamente difícil.
Tabela 13.3 Visão geral das atividades de programação e como elas podem ser usadas para apoiar os recém-chegados em um
projeto
Exploração Navegando na base de código para obter uma noção geral da base de código
Transcrição Dar ao recém-chegado um plano claro para implementar um determinado método que deve ser implementado
Incrementação Adicionando um recurso a uma classe existente, incluindo a criação do plano para o recurso
As diferentes atividades podem construir umas sobre as outras e trabalhar em partes relacionadas do código.
Por exemplo, uma primeira tarefa pode ser procurar uma aula, seguida de transcrever um
dentro dessa classe em código e, em seguida, incrementando a classe de uma maneira mais complexa.
Você também pode alternar tarefas em que o foco é aprender novos conceitos de programação com tarefas
que focam mais em conhecer o domínio, dependendo
no conhecimento prévio existente do embarcado.
EXERCÍCIO 13.3 Pense em uma atividade concreta que um recém-chegado poderia fazer em todos os cinco
categorias quando novo em sua base de código.
Pode-se também imaginar que uma equipe pode criar e manter documentos para
ajudar ainda mais os recém-chegados (por exemplo, apoiar a exploração com bons comentários e
documentos de arquitetura explicando módulos, subsistemas, estruturas de dados e
algoritmos usados no sistema).
Primeiro, você pode preparar o processo de integração para recém-chegados entendendo profundamente as
informações relevantes que desempenham um papel ao trabalhar com a base de código. Isto
pode ser feito com a equipe de saída, antes mesmo da chegada do recém-chegado.
Por exemplo, você pode querer documentar todos os conceitos de domínio importantes que as pessoas
provavelmente encontrarão no código. Outra informação relevante é tudo
bibliotecas, frameworks, bancos de dados e outras ferramentas externas usadas no código. Uma frase
como, "Nós usamos Laravel para este aplicativo da web, que implantamos no Heroku com Jenkins" pode
não exige nenhum esforço de um desenvolvedor existente, mas se você não sabe o que mesmo um desses
tools é, você pode perder todo o significado da frase. E, claro, o recém-chegado
podem saber o que é um framework web ou um servidor de automação em abstrato, mas se eles
não conheço esses nomes específicos, é difícil entender o significado e lembrar.
Como nota lateral, mesmo separado do processo de onboarding, ter uma lista atualizada de todos os
conceitos de domínio e programação relevantes para um projeto também podem ajudar
desenvolvedores.
EXERCÍCIO 13.4 Escolha um projeto no qual você trabalha com frequência. Crie duas listas que
poderia ajudar os recém-chegados: um contendo conceitos de domínio importantes e seus
descrição e um com todas as bibliotecas, frameworks e conceitos de programação importantes
que a base de código usa. Preencha a tabela a seguir.
Conceito Definição
Conceito/módulo/biblioteca Uso/definição
Machine Translated by Google
Dar aos recém-chegados uma tarefa focada será menos pesado no STM e, portanto, mais provável de
deixe espaço para carga relevante para lembrar coisas importantes sobre o código. Além de ser mais
fácil e eficaz para o iniciante, ter bons resumos de código
será útil como documentação para outros recém-chegados, mais do que mais recursos.
Se você optar por fazer com que um recém-chegado implemente um pequeno recurso, ou seja, de
claro, possível também. Mas nesse código, é melhor remover outros aspectos que criam
carga cognitiva, como pesquisar por meio de código. Você pode preparar o código relevante
antecipadamente. Você pode usar as técnicas que descrevemos no capítulo 4, como refatorar o código
relevante na classe em questão para evitar a pesquisa.
O Capítulo 4 propôs uma série de técnicas que podem apoiar a memória de trabalho,
incluindo o uso de diagramas. Pode ser difícil para alguém novo em uma base de código criar
esses artefatos. No processo de integração, o integrador pode considerar a criação de tabelas
que auxiliam a memória de trabalho.
No entanto, como descrevemos, os diagramas nem sempre são úteis para iniciantes absolutos
que podem estar relutantes em se afastar do código e ver uma imagem maior. Monitor
a utilidade dos diagramas muitas vezes e abandoná-los quando a técnica não é (ainda)
útil.
No capítulo 5, propusemos essas atividades como coisas a fazer quando você lê código como um
desenvolvedor individual. No entanto, essas tarefas também podem ser usadas quando você integra um novo
desenvolvedor em uma base de código. Quando a equipe realiza essas atividades, diminui a capacidade do recém-chegado
carga cognitiva, e eles podem então se concentrar no código com mais memória de trabalho.
A seguir, detalharemos como cada uma das sete atividades pode ser usada de forma colaborativa.
código de leitura.
ATIVANDO
Antes do início da sessão de leitura, examine os conceitos relevantes no código. Se você já fez o exercício
13.1, você pode preparar esta lista de antemão. Lembre o recém-chegado de conceitos relevantes de antemão
e alivie a confusão sobre conceitos. Isto é
melhor discutir detalhes neste momento do que quando o recém-chegado também está tentando
entender o código. Por exemplo, se você estiver usando um modelo Model-View-Controller, ele
é melhor se esse fato não for descoberto ao navegar em diferentes arquivos de código.
Após a fase de ativação, a sessão de código de leitura colaborativa pode começar. Observação
que a atividade que está sendo executada é a compreensão. Isso significa que queremos limitar a
outras quatro atividades.
DETERMINANDO A IMPORTÂNCIA
Se você tem pouco conhecimento de algo, pode ser muito difícil distinguir entre
conhecimento básico e o que é menos importante. Indicando as partes mais relevantes
código suporta recém-chegados. Isso pode ser feito em uma leitura colaborativa, na qual, por
Por exemplo, todos os membros da equipe apontam o que eles acham que são as linhas mais relevantes ou
importantes do código. Alternativamente, você pode ter um documento decorrente de sessões anteriores de
leitura de código em que os destaques importantes já foram
apontou, que um recém-chegado pode ler.
INFERINDO
Da mesma forma, pode ser difícil preencher detalhes que não são explicitamente declarados. Por exemplo, um
equipe pode ter entendimentos claros de conceitos de domínio, como remessas
deve sempre conter pelo menos um pedido, mas tais decisões podem não ter sido explicitamente documentadas
no código. Da mesma forma, para determinar a importância, você pode expressamente
apontar descobertas em uma sessão de leitura, ou essas decisões podem ter sido relatadas
para que possam ser facilmente compartilhados com os recém-chegados.
Machine Translated by Google
MONITORAMENTO
Como descrevemos, a coisa mais importante que você pode fazer no processo de integração é
acompanhar o nível de compreensão que o integrado tem atualmente. Peça-lhes regularmente
para fazer uma rápida recapitulação da leitura, definir conceitos de domínio essenciais ou
recordar um conceito de programação usado no código.
VISUALIZAÇÃO
Conforme descrito anteriormente neste livro, os diagramas servem a dois propósitos. Primeiro,
eles podem apoiar a memória de trabalho (capítulo 4) e, segundo, sua criação pode ajudar na
compreensão (capítulo 5). Dependendo do nível do recém-chegado, o onboarder pode criar um
diagrama e usá-lo para ajudar o onboardee na leitura do código, ou pedir ao recém-chegado que
o desenhe para aprofundar sua compreensão do código.
QUESTIONAMENTO Ao ler o código de forma colaborativa, pergunte e responda regularmente a perguntas sobre ele.
Dependendo do nível do recém-chegado, você pode fazer perguntas que ele responda ou
incentivá-lo a fazer perguntas que você possa responder. Os recém-chegados relativamente
experientes também podem se beneficiar de fazer perguntas para encontrar as respostas, com
sua ajuda, mas sempre monitore sua carga cognitiva em tarefas menos orientadas. Quando o
onboardee começa a adivinhar ou conclui coisas que não fazem sentido, sua carga cognitiva
pode ser excedida.
RESUMO
Como etapa final na leitura de código para integração, você pode escrever um resumo do código
que foi lido em conjunto. Um exemplo de resumo é mostrado na figura 13.4. Dependendo do
estado da documentação na base de código, esse resumo pode ser enviado à base de código
como documentação, o que é uma tarefa ótima e acolhedora para o onboardee fazer após a
sessão. Nesse processo, eles conhecerão o fluxo de trabalho da base de código (por exemplo,
criando um pull request e solicitando uma revisão sobre ele, de uma forma que não seja muito
assustadora ou muito difícil para a memória de trabalho).
Resumo 219
Resumo ÿ Os
especialistas pensam e agem de forma diferente dos iniciantes. Os especialistas podem raciocinar abstratamente
sobre o código e ter a capacidade de pensar sobre ele sem se referir ao próprio código. Iniciantes tendem a se
concentrar nos detalhes do código e têm dificuldade em voltar atrás dos detalhes. ÿ Quando os programadores
de nível médio aprendem novas informações, eles às vezes voltam
ÿ As pessoas que estão aprendendo um novo conceito precisam aprender tanto em termos abstratos
quanto em exemplos concretos. ÿ As pessoas que estão aprendendo um novo conceito também
precisam de tempo para conectar um novo conceito ao conhecimento prévio. ÿ Ao fazer a integração,
limite as atividades de programação que os recém-chegados realizam a uma
de uma vez.
epílogo
Algumas palavras para fechar este livro
Obrigado por chegar ao final deste livro. Se você leu todo o texto ou apenas alguns pedaços aqui
e ali, estou feliz que você tenha chegado a este ponto. Escrever este livro foi uma experiência
muito gratificante. Aprendi muito sobre ciência cognitiva e programação porque me aprofundei no
assunto do que nunca.
Mas também aprendi muito sobre mim. Uma das coisas que aprendo deste livro é que confusão e
se sentir sobrecarregado cognitivamente é bom e faz parte da vida e do aprendizado. Antes de
saber tudo o que sei sobre cognição, costumava ficar chateado comigo mesmo por não ser
inteligente o suficiente para ler artigos complicados ou explorar códigos desconhecidos; agora
posso ser mais gentil comigo mesmo e dizer: “Bem, talvez você tenha muita carga cognitiva”.
Para encerrar este livro, quero enfatizar que gostei imensamente de explorar, resumir e cobrir
o trabalho de grandes cientistas no campo da programação e da cognição. Embora fazer minha
própria pesquisa seja incrível, ajudar os desenvolvedores a entender a pesquisa existente tem
sido tremendamente recompensador e pode ter um impacto maior na programação do que meus
próprios projetos. Se você quiser ler ainda mais, há alguns livros que posso recomendar e alguns
cientistas que você pode querer acompanhar.
Se você quer conhecer melhor seu cérebro, eu adoro o livro Thinking Fast and Slow (Farrar,
Straus, and Giroux, 2013) de Daniel Kahneman porque ajuda você a entender seu cérebro de uma
maneira mais ampla do que este livro. Da mesma forma, o livro
221
Machine Translated by Google
222 Epílogo
1
Paul A. Kirschner et al., “Por que a orientação mínima durante a instrução não funciona: uma análise da falha
de Ensino Construtivista, Descoberta, Baseado em Problemas, Experiencial e Baseado em Investigação”,
Psicólogo Educacional, vol. 41, nº. 2, pp. 75–86, 2010, https://www.tandfonline.com/doi/abs/10.1207/
2
s15326985ep4102_1 Raymond Lister, “Toward a Developmental Epistemology of Computer Programming”, 2010, https://
dl.acm.org/doi/10.1145/2978249.2978251
Machine Translated by Google
índice
223
Machine Translated by Google
224 ÍNDICE
ÍNDICE 225
Craik, Kenneth 95
atributos críticos 113
D
LTM (memória de longo prazo) 7
código de leitura 15–16, 23–31, 79–80 depuração 182–183
Áreas de Brodmann 79-80 memória declarativa 164
evidências de fMRI 80 prática deliberada 30 código
deslocalizado 51 gráficos de
memória icônica 23–24 em
dependência combinando
relação às tarefas de programação 10–12
tabelas de estado e 61 memória de
STM (memória de curto prazo) 7–8
trabalho e 56–59
com nomeação 133–136 avaliando a
manobras de design 201–202
qualidade dos nomes 136
LTM 134–135 propensão ao erro vs. viscosidade 202
provisoriedade e avaliação progressiva vs. propensão
Nomes de variáveis
ao erro 202 papel expressividade vs. difusão 202
STM 133–134 com informações 135–136
memória de trabalho 8–9 padrões de projeto 26
refatoração cognitiva 52 determinando importância 84, 217
colaboração com colegas 175 código diagramas, desenho 216
complexo 63 técnicas de carga cognitiva DiBernardo, Michael 175
para reduzir 51–56 tipos de 49–50 dimensão de difusão 194-195, 202
razões para dificuldades com 47– interrupções 34-35 documentação 129
50 domínio de aprendizagem 215
Machine Translated by Google
226 ÍNDICE
JAVA
tentando lembrar 17
STM (memória de curto prazo) 8
Machine Translated by Google
ÍNDICE 227
K memórias 164-166
visão geral 165-166
conhecimento desaprender 166
5 ativando conhecimento prévio 84–85 ver informações e 41 repetições
benefícios do conhecimento de programação espaçadas 39–40 fortalecer
existente 113–114 texto vs. plano 75 memórias 42
modelos mentais 94, 97–102
modelos concretos 98–99 no
LTM 100–102 na memória de
eu
trabalho 98, 102 aprendendo
subobjetivos de rotulagem novas 97 máquinas nocionais
187 lambdas 53 máquinas criando conflitantes 107–108 de código-fonte no LTM 101–
102 de código-fonte na memória de trabalho 99–100 visão
nocionais de linguagem e
106–108 geral 96 armazenamento durante interrupções 185
modelos mentais conflitantes 107-108
expandindo conjuntos de máquinas nocionais 106
Meta-programação (Simonyi) 74 cheiros
substituindo construções desconhecidas 52–55 de código no nível de método 150
lambdas 53
variável mínima 121 função Minimum()
compreensões de listas 54
152 equívocos 117–123 sobre linguagens
operadores ternários 54–55
de programação 120–122 depuração
antipadrões linguísticos 154–155, 158–159
com mudança conceitual 118–119 diagnóstico
causando confusão 159 checando na base de
em nova base de código 122–123 prevenção enquanto
código 154–155 compreensões de listas 54
aprende nova programação linguagem 122 suprimir
programação ao vivo 197 método longo 152–153
119–120 modelos 109 para pensar sobre o código 92–
transferência de baixo custo 114
94 modelos mentais 94–102
nomeando 128–146
escolhendo nomes melhores 142–146
modelo de três etapas de Feitelson 145-146
Machine Translated by Google
228 ÍNDICE
poder de processamento
6 linguagens de programação 120–122
Janelas de programação (Petzold) 75
programas 75–78 estágios de compreensão
76–78 conhecimento de texto vs.
O
conhecimento de plano 75 dimensão de
avaliação progressiva 196–197, 202 memória prospectiva
integração de novos desenvolvedores
219 atividades para 213–218 186–187 dimensão de provisoriedade 196, 202 público
classe 8 public static void main 8
desenhando diagramas 216
explicando informações relevantes 215
limitando tarefas 213–214 preparando tarefas
pequenas e focadas 215–216 lendo código juntos
216–218 Q
comportamento dos iniciantes
207-211 modelo neo-piagetiano para programação questionando 89, 218
208-210
O modelo original de Piaget 207–208 R
esquecendo temporariamente as coisas 210–
211 diferença entre ver conceitos concretamente lendo o código 32, 79–84, 216–218
e abstratamente 211-213 ativando 217 fragmentando 19–21, 25–
questões em processo de 206-207 30 beacons 28–30 padrões de projeto
função open() 111, 197 tabelas 26
de operação 88–89
Machine Translated by Google
ÍNDICE 229
Simonyi, Carlos 74
Machine Translated by Google
230 ÍNDICE
variáveis 68-71
diferentes papéis de 68–69 onze
ENGENHARIA DE SOFTWARE
produtividade, reduzir a necessidade de reescritas de código e muito mais. “Ajuda você a entender
Este livro exclusivo irá ajudá-lo a alcançar esses ganhos. como seu cérebro funciona e
como você pode usá-lo de
O Cérebro do Programador desbloqueia a maneira como pensamos sobre código.
Ele oferece técnicas cientificamente sólidas que podem melhorar
forma mais eficaz para ler, escrever e
radicalmente a maneira como você domina novas tecnologias, compreende
código e memoriza sintaxe. Você aprenderá como se beneficiar da luta
colaborar no código.
—Ben McNamara, DataGeek
”
produtiva e transformar a confusão em uma ferramenta de aprendizado.
Ao longo do caminho, você descobrirá como criar recursos de estudo à “Ensina hábitos baseados na
medida que se tornar um especialista em ensinar a si mesmo e atualizar ciência para reduzir sua carga
novos colegas.
de trabalho mental e abrir
caminho para se tornar um
O que há dentro
ISBN: 978-1-61729-867-7