Рефакторинг
Когда у нас есть работающая функция, мы можем сделать шаг назад и подумать о том, как лучше структурировать наш код. Цель — сделать наш плагин максимально ясным, понятным и удобным в обслуживании. Этот процесс улучшения и уточнения структуры существующего кода без изменения его поведения известен как рефакторинг.
Вот еще раз исходная функция:
(define (scheme-hello-world)
;; Set the message handler to output the message to a GUI dialog box
(lumi-message-set-handler 0)
(lumi-message "Hello world!\n")
;; Set the message handler to output the message to the Error Console
(lumi-message-set-handler 2)
(lumi-message "Hello world!\n")
;; Send the message to the terminal, the OS window that launched Lumi
(display "Hello world!\n"))Имя функции — это имя функции, а параметр — это то, что функция принимает в качестве входных данных. Тело — это блок кода, который запускается при вызове функции.
Абстрактная форма:
(define (function-name parameter)
body)Повторение кода
Удалите повторы заранее. (lumi-message "Hello world!\n") повторяется дважды, а строка сообщения повторяется три раза. Переменная решает повторяющуюся строку.
Переменные
В Scheme переменная имеет «область действия», о которой она известна, и эта область устанавливается с помощью оператора let. Переменная привязана к значению в части привязки, а область действия переменной находится в теле let. Переменная известна только внутри блока let и недоступна за ее пределами.
(let ((variable value))
body)Представляем переменную под названием «сообщение»:
(define (scheme-hello-world)
(let ((message "Hello world!\n"))
;; Set the message handler to output the message to a GUI dialog box
(lumi-message-set-handler 0)
(lumi-message message)
;; Set the message handler to output the message to the Error Console
(lumi-message-set-handler 2)
(lumi-message message)
;; Send the message to the terminal, the OS window that launched Lumi
(display message)))В нашем примере мы использовали переменную с именем «сообщение», привязанную к строке «Привет, мир!\n». Это позволяет нам менять содержимое сообщения один раз, а не три раза, уменьшая вероятность ошибок и делая код более гибким.
Извлечение функций
В функциональном программировании обычной практикой является рефакторинг кода для выделения повторно используемой логики в отдельные функции. При этом основная функция становится намного проще и более ориентированной на свою высокоуровневую цель, в то время как извлеченная функция выглядит более сложной, поскольку она обрабатывает подробную логику. Это сделано намеренно и соответствует основным принципам функционального программирования, таким как модульность, разделение задач и читаемость. Вот рефакторинг Привет, мир! после экстракции.
Извлекаем логику:
# !/usr/bin/env lumi-scheme-interpreter-0.1
;; Main Function
(define (scheme-hello-world)
(let ((message "Hello world!\n"))
(send-message message 'gui)
(send-message message 'error-console)
(send-message message 'terminal)))
;; Function to handle message output to various destinations
(define (send-message message output)
(cond
;; Send to the Error Console
((eq? output 'error-console)
;; Set the handler to Error Console
(lumi-message-set-handler 2)
(lumi-message message))
;; Send to the GUI dialog box
((eq? output 'gui)
;; Set the handler to GUI dialog
(lumi-message-set-handler 0)
(lumi-message message))
;; Send to the terminal window
((eq? output 'terminal)
;; Terminal output is handled with display
(display message)))
;; Restore the default message handler to the Error Console
(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")Символы
В приведенном выше примере используется тип данных, называемый символом, например «gui. Символы передаются в качестве параметров функции отправки сообщения и могут использоваться для принятия простых условных решений. Подобно символическим ключам, они являются уникальными идентификаторами. Для получения дополнительной информации о символах посетите эту страницу.
Упрощение основной функции
В исходной функции (scheme-hello-world) вся логика отправки сообщений на разные выходы (GUI, Консоль ошибок, Терминал) была смешана с основной функцией. После рефакторинга функция main просто фокусируется на том, что нужно сделать, отправляя сообщение по разным адресатам.
Рефакторинг основной функции стал проще:
- В нем четко указано его назначение: отправить одно и то же сообщение на несколько выходов.
- Это позволяет избежать загромождения основной логики повторяющимся кодом, например, установкой обработчиков сообщений для разных выходов.
- Легче читать и понимать с первого взгляда.
Сложность извлеченной функции
Напротив, функция (send-message) содержит подробную логику. Теперь он обрабатывает варианты поведения для каждого вывода (графический интерфейс пользователя, консоль ошибок, терминал). Эта функция стала немного сложнее, чем раньше, но теперь она централизована и изолирована.
Связь этого с функциональным программированием
В функциональном программировании функции рассматриваются как первоклассные граждане, то есть их можно повторно использовать, передавать и комбинировать для формирования более сложного поведения. Цель состоит в том, чтобы:- Разлагайте проблемы на более мелкие, независимые части.
- Изолируйте сложность на более мелкие функции, выполняющие конкретные задачи, например
send-message. – Сохраняйте простоту функций более высокого уровня, чтобы они могли сосредоточиться на организации потока данных и действий, не зная подробностей выполнения каждой задачи. - Разделение задач: функция заботится о том, как отправляется сообщение, в зависимости от типа вывода, что изолирует эту логику от основной функции.
- Модульность: обрабатывая всю логику отправки сообщений в одном месте, мы можем легко вносить изменения (например, добавлять новые параметры вывода), не изменяя основную функцию.
- Повторное использование: функцию
send-messageможно использовать повторно. Это означает, что если нам нужно отправить сообщение на несколько выходов в другом месте нашего кода, мы можем просто вызвать эту функцию, а не переписывать подобную логику.
В результате рефакторинга основная функция в этом примере становится декларативным заявлением о том, что происходит («отправка сообщения в три места»), а сложность отправки этих сообщений абстрагируется в функцию send-message.