Рефакторинг

Когда у нас есть работающая функция, мы можем сделать шаг назад и подумать о том, как лучше структурировать наш код. Цель — сделать наш плагин максимально ясным, понятным и удобным в обслуживании. Этот процесс улучшения и уточнения структуры существующего кода без изменения его поведения известен как рефакторинг.

Вот еще раз исходная функция:

(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.