Refatoração
Assim que tivermos uma função funcionando, podemos dar um passo atrás e pensar na melhor forma de estruturar o código. O objetivo é tornar o plug-in o mais claro, compreensível e fácil de manter possível. Este processo de melhorar e refinar a estrutura do código existente sem alterar o comportamento é conhecido como refatoração.
Aqui está a função inicial novamente:
(define (scheme-hello-world)
;; Definir o manipulador de mensagens para enviar a mensagem para uma caixa de diálogo GUI
(lumi-message-set-handler 0)
(lumi-message "Hello world!\n")
;; Definir o manipulador de mensagens para enviar a mensagem para a consola de erros
(lumi-message-set-handler 2)
(lumi-message "Hello world!\n")
;; Enviar a mensagem para o terminal, a janela do SO que iniciou o Lumi
(display "Hello world!\n"))O nome da função é o nome da função e o parâmetro é o que a função aceita como entrada. O corpo é o bloco de código executado quando a função é chamada.
Forma abstrata:
(define (function-name parameter)
body)Repetição de código
Remova a repetição antecipadamente. (lumi-message "Hello world!\n") é repetido duas vezes e a sequência da mensagem é repetida três vezes. Uma variável resolve a string repetida.
Variáveis
No Scheme, uma variável tem um “escopo”, onde é conhecida, e esse escopo é definido usando uma instrução let. A variável está vinculada a um valor na parte vinculativa e a variável tem escopo no corpo let. A variável só é conhecida dentro do bloco let e não pode ser acessada fora dele.
(let ((variable value))
body)Apresentando uma variável chamada “mensagem”:
(define (scheme-hello-world)
(let ((message "Hello world!\n"))
;; Definir o manipulador de mensagens para enviar a mensagem para uma caixa de diálogo GUI
(lumi-message-set-handler 0)
(lumi-message message)
;; Definir o manipulador de mensagens para enviar a mensagem para a consola de erros
(lumi-message-set-handler 2)
(lumi-message message)
;; Enviar a mensagem para o terminal, a janela do SO que iniciou o Lumi
(display message)))No exemplo usamos uma variável chamada “mensagem” vinculada a uma string “Olá, mundo!\n”. Isto permite alterar o conteúdo da mensagem uma vez em vez de três, reduzindo a chance de erros e tornando o código mais flexível.
Extraindo Funções
Na programação funcional, refatorar o código para extrair lógica reutilizável em funções separadas é uma prática comum. Ao fazer isso, a função principal se torna muito mais simples e mais focada no objetivo de alto nível, enquanto a função extraída parece mais complexa porque lida com a lógica detalhada. Isso é intencional e está alinhado aos princípios básicos da programação funcional, como modularidade, separação de interesses e legibilidade. Aqui está o refatorado Olá mundo! após a extração.
Extraindo a lógica:
#!/usr/bin/env lumi-scheme-interpreter-0.1
;; Função principal
(define (scheme-hello-world)
(let ((message "Hello world!\n"))
(send-message message 'gui)
(send-message message 'error-console)
(send-message message 'terminal)))
;; Função para tratar a saída de mensagens para vários destinos
(define (send-message message output)
(cond
;; Enviar para a consola de erros
((eq? output 'error-console)
;; Definir o manipulador para consola de erros
(lumi-message-set-handler 2)
(lumi-message message))
;; Enviar para a caixa de diálogo GUI
((eq? output 'gui)
;; Definir o manipulador para o diálogo GUI
(lumi-message-set-handler 0)
(lumi-message message))
;; Enviar para a janela do terminal
((eq? output 'terminal)
;; A saída do terminal é tratada com display
(display message)))
;; Restaurar o manipulador de mensagens padrão para a consola de erros
(lumi-message-set-handler 2))
(scheme-register-procedure "scheme-hello-world"
"Hello world!"
"A Scheme procedure plug-in"
"Mark Sweeney"
"Under GNU GENERAL PUBLIC LICENSE Version 3"
"2024")
(scheme-menu-register
"scheme-hello-world"
"<Image>/Funky")Símbolos
No exemplo acima, é usado um tipo de dados chamado símbolo, como ‘gui. Os símbolos são passados como parâmetros para a função de envio de mensagem e podem ser usados para tomar decisões condicionais simples. Assim como as chaves simbólicas, elas são identificadores exclusivos. Para obter mais informações sobre símbolos, visite esta página.
Simplificar a função principal
Na função original (scheme-hello-world), toda a lógica de envio de mensagens para diferentes saídas (GUI, Console de erros, Terminal) foi misturada na função principal. Após a refatoração, a função principal simplesmente se concentra em o que precisa ser feito, enviando a mensagem para diferentes destinos.
A função principal refatorada é mais simples:
- Afirma claramente o seu propósito: enviar a mesma mensagem para vários resultados.
- Evita sobrecarregar a lógica principal com códigos repetitivos, como configurar manipuladores de mensagens para diferentes saídas.
- É mais fácil ler e entender rapidamente.
A complexidade da função extraída
Em contraste, a função (enviar mensagem) é onde reside a lógica detalhada. Agora ele lida com as variações de comportamento de cada saída (GUI, Console de erros, Terminal). A função é um pouco mais complexa do que antes, mas agora está centralizada e isolada.
Relacionando isso à programação funcional
Na programação funcional, as funções são vistas como cidadãs de primeira classe, o que significa que podem ser reutilizadas, repassadas e combinadas para formar um comportamento mais complexo. O objetivo é:- Decomponha os problemas em partes menores e independentes.
- Isole a complexidade em funções menores que lidam com tarefas específicas, como
send-message. - Mantenha funções de nível superior simples para que possam se concentrar na orquestração do fluxo de dados e ações, sem a necessidade de saber os detalhes de como cada tarefa é realizada.
- Separação de interesses: A função cuida de como a mensagem é enviada com base no tipo de saída, o que isola essa lógica da função principal.
- Modularidade: Ao lidar com toda a lógica de envio de mensagens em um só lugar, podemos facilmente fazer alterações (como adicionar novas opções de saída) sem alterar a função principal.
- Reutilização: A função
send-messageé reutilizável, o que significa que se precisarmos enviar uma mensagem para várias saídas em outro lugar do código, podemos simplesmente chamar essa função em vez de reescrever uma lógica semelhante.
Ao refatorar, a função principal neste exemplo se torna uma declaração declarativa do que está acontecendo (“enviar uma mensagem para três lugares”), enquanto a complexidade de como enviar essas mensagens é abstraída na função send-message.