Refaktoryzacja ponownie
W miarę rozrastania się biblioteki pomocniczej, śledzenie jej na pierwszy rzut oka staje się trudniejsze. Ponownie dokonaj refaktoryzacji, aby każda funkcja była mała i przeznaczona do jednego celu.
Przełamywanie złożoności
Aby ułatwić śledzenie i utrzymywanie funkcji, podziel ją na mniejsze, skupione funkcje. Zacznij od oddzielenia sprawdzania poprawności od kierowania komunikatów.
Utwórz funkcję sprawdzającą
Możemy wykorzystać część funkcji sprawdzającą argumenty message i output i przenieść ją do osobnej funkcji. W ten sposób podstawowa funkcja send-message nie musi się martwić o walidację, co ułatwia jej przestrzeganie.
(define (validate-message message output)
;; Sprawdź, czy wiadomość jest niepustym ciągiem znaków
(if (or (not (string? message)) (string=? message ""))
(error "Message must be a non-empty string"))
;; Sprawdź, czy wyjście jest jednym z oczekiwanych miejsc docelowych
(if (not (member output '(gui error-console terminal)))
(error "Invalid output destination: " output)))Uprość wysyłanie wiadomości
Teraz, gdy sprawdzanie poprawności zostało przeniesione do osobnej funkcji, funkcja send-message może skupić się na samym wysyłaniu wiadomości. Będzie to znacznie prostsze, ponieważ zajmie się jedynie konkretnym zadaniem skierowania wiadomości do właściwego miejsca docelowego.
(define (send-message message output)
;; Wywołaj funkcję walidacji przed kontynuowaniem
(validate-message message output)
(cond
;; Wyślij do konsoli komunikatów
((eq? output 'error-console)
(lumi-message-set-handler 2)
(lumi-message message))
;; Wyślij do okna dialogowego GUI
((eq? output 'gui)
(lumi-message-set-handler 0)
(lumi-message message))
;; Wyślij do okna terminala
((eq? output 'terminal)
(display message)))
;; Przywróć domyślną obsługę wiadomości do konsoli komunikatów
(lumi-message-set-handler 2))Dalsze rozbicie: oddziel każdą procedurę obsługi wyjścia
Każdy typ komunikatu wyjściowego (GUI, konsola komunikatów, terminal) można przenieść do własnej funkcji. Pozwala to na łatwiejsze testowanie, modyfikację i potencjalną rozbudowę w przyszłości.
(define (send-to-gui message)
(lumi-message-set-handler 0)
(lumi-message message))
(define (send-to-error-console message)
(lumi-message-set-handler 2)
(lumi-message message))
(define (send-to-terminal message)
(display message))
(define (send-message message output)
;; Wyślij do właściwego wyjścia
(cond
((eq? output 'error-console) (send-to-error-console message))
((eq? output 'gui) (send-to-gui message))
((eq? output 'terminal) (send-to-terminal message)))
;; Przywróć domyślną obsługę wiadomości do konsoli komunikatów
(lumi-message-set-handler 2))Ponowne użycie walidacji w każdej funkcji wysyłania
Ponieważ walidacja jest ważną częścią zapewnienia, że zarówno komunikat, jak i dane wyjściowe są poprawne, sensowne jest, aby każda funkcja send-* przeprowadziła własną walidację. Dzięki temu niezależnie od tego, które wyjście zostanie wywołane, zawsze najpierw sprawdzimy wejścia.
(define (send-to-gui message)
;; Zweryfikuj wiadomość przed kontynuowaniem
(validate-message message 'gui)
(lumi-message-set-handler 0)
(lumi-message message))
(define (send-to-error-console message)
;; Zweryfikuj wiadomość przed kontynuowaniem
(validate-message message 'error-console)
(lumi-message-set-handler 2)
(lumi-message message))
(define (send-to-terminal message)
;; Zweryfikuj wiadomość przed kontynuowaniem
(validate-message message 'terminal)
(display message))Zobacz, że usunęliśmy walidację z funkcji send-message i przenieśliśmy odpowiedzialność na każdą osobną funkcję wyjściową. Ta zmiana zapewnia, że każde miejsce docelowe (GUI, konsola komunikatów, terminal) obsługuje własną walidację, upraszczając send-message i trzymając logikę sprawdzania bliżej miejsca, w którym jest potrzebna.
Takie podejście może uprościć send-message, czyniąc ją dyspozytorem, przy jednoczesnym zapewnieniu, że każda funkcja send-to-* poprawnie waliduje wiadomość przed przetworzeniem.
Przenosząc walidację do każdej funkcji send-to-*, umożliwiliśmy ich ponowne użycie jako samodzielnych funkcji. Oznacza to, że możemy wywołać send-to-gui, send-to-error-console lub send-to-terminal bezpośrednio, bez polegania na dyspozytorze send-message. Każda z tych funkcji obsługuje teraz w pełni własną logikę i może być używana niezależnie w innych częściach kodu lub w innych wtyczkach, dzięki czemu kod jest bardziej modułowy i elastyczny.
Korzyści z refaktoryzacji
- Wyraźne oddzielenie obaw: Każda funkcja obsługuje teraz tylko jedną odpowiedzialność, dzięki czemu kod jest łatwiejszy do zrozumienia.
- Rozszerzalność: Dodawanie nowych typów wyjść jest proste. Wystarczy zdefiniować nową funkcję, taką jak
send-to-filelubsend-to-logger, a następnie dodać gałąź w instrukcjicond. - Zapewniać ponowne użycie: Każdą z tych funkcji obsługi wyników można ponownie wykorzystać w innym miejscu projektu lub udostępnić wielu wtyczkom.
- Spójność: Ponowne użycie funkcji sprawdzania poprawności w każdej funkcji
send-to-*zapewnia, że wszystkie dane wyjściowe są prawidłowo sprawdzane, co czyni kod bardziej niezawodnym.
Zrefaktoryzowana wersja biblioteki:
;; Cel: Wysyła wiadomość do okna dialogowego GUI
(define (send-to-gui message)
;; Zweryfikuj wiadomość przed kontynuowaniem
(validate-message message 'gui)
(lumi-message-set-handler 0)
(lumi-message message))
;; Cel: Wysyła wiadomość do konsoli komunikatów
(define (send-to-error-console message)
;; Zweryfikuj wiadomość przed kontynuowaniem
(validate-message message 'error-console)
(lumi-message-set-handler 2)
(lumi-message message))
;; Cel: Wysyła wiadomość do okna terminala
(define (send-to-terminal message)
;; Zweryfikuj wiadomość przed kontynuowaniem
(validate-message message 'terminal)
(display message))
;; Cel: Wysyła wiadomość do właściwego miejsca docelowego wyjścia
(define (send-message message output)
(cond
((eq? output 'error-console) (send-to-error-console message))
((eq? output 'gui) (send-to-gui message))
((eq? output 'terminal) (send-to-terminal message)))
;; Przywróć domyślną obsługę wiadomości do konsoli komunikatów
(lumi-message-set-handler 2))
;; Cel: Sprawdza, czy wiadomość jest niepustym ciągiem znaków i czy wyjście jest poprawne
(define (validate-message message output)
;; Sprawdź, czy wiadomość jest niepustym ciągiem znaków
(if (or (not (string? message)) (string=? message ""))
(error "Message must be a non-empty string"))
;; Sprawdź, czy wyjście jest jednym z oczekiwanych miejsc docelowych
(if (not (member output '(gui error-console terminal)))
(error "Invalid output destination: " output)))Czy to już wszystko? Nie — jest jeszcze sporo do zrobienia, czytaj dalej.