Go:
Refaktoryzacja

Jak to zrobić:

W Go refaktoryzacja może obejmować od prostych poprawek kodu po bardziej złożone zmiany. Zaczynając od podstawowego przykładu: uproszczenie początkowej funkcji Go dla lepszej czytelności i wydajności.

Przed refaktoryzacją:

package main

import "fmt"

func CalculatePrice(quantity int, price float64) float64 {
    var total float64
    if quantity > 0 {
        total = float64(quantity) * price
    } else {
        total = 0
    }
    return total
}

func main() {
    fmt.Println(CalculatePrice(10, 5.99))  // Wyjście: 59.9
}

Po refaktoryzacji:

package main

import "fmt"

func CalculatePrice(quantity int, price float64) float64 {
    if quantity > 0 {
        return float64(quantity) * price
    }
    return 0
}

func main() {
    fmt.Println(CalculatePrice(10, 5.99))  // Wyjście: 59.9
}

W wersji po refaktoryzacji usunięto else, co upraszcza przepływ funkcji, nie wpływając na jej wyjście — przykład podstawowej, lecz wpływowej techniki refaktoryzacji w Go.

Dla bardziej zaawansowanego przykładu, rozważ refaktoryzację funkcji w celu użycia interfejsów dla lepszej wielokrotności użycia i testowalności:

Przed refaktoryzacją:

package main

import "fmt"

type Logger struct{}

func (l Logger) Log(message string) {
    fmt.Println("Log:", message)
}

func ProcessData(data string, logger Logger) {
    // Wyobraź sobie jakąś obróbkę danych tutaj
    logger.Log("Dane przetworzone")
}

func main() {
    logger := Logger{}
    ProcessData("przykładowe dane", logger)
}

Po refaktoryzacji:

package main

import "fmt"

type Logger interface {
    Log(message string)
}

type ConsoleLogger struct{}

func (c ConsoleLogger) Log(message string) {
    fmt.Println("Log:", message)
}

func ProcessData(data string, logger Logger) {
    // Obróbka danych pozostaje niezmieniona
    logger.Log("Dane przetworzone")
}

func main() {
    logger := ConsoleLogger{}
    ProcessData("przykładowe dane", logger)
}

Refaktoryzacja do użycia interfejsu (Logger) zamiast konkretnego typu (ConsoleLogger) poprawia elastyczność funkcji i oddziela przetwarzanie danych od specyficznej implementacji logowania.

Głębsze spojrzenie

Refaktoryzacja w Go musi balansować między prostotą (jedną z podstawowych filozofii Go) a elastycznością potrzebną w dużych projektach oprogramowania. Biorąc pod uwagę minimalistyczne podejście Go do funkcji — bez uogólnień (do niedawna) i z silnym naciskiem na czytelność — język ten naturalnie kieruje programistów ku prostszym, bardziej utrzymywalnym strukturom kodu. Jednakże to nie oznacza, że kod Go nie korzysta z refaktoryzacji; oznacza to, że refaktoryzacja musi zawsze priorytetowo traktować jasność i prostotę.

Historycznie, brak pewnych funkcji w Go (np. uogólnień przed Go 1.18) prowadził do kreatywnych, ale czasami zawiłych rozwiązań dla ponownego wykorzystania kodu i elastyczności, czyniąc refaktoryzację dla abstrakcji powszechną praktyką. Z wprowadzeniem uogólnień w Go 1.18, programiści Go przeprowadzają teraz refaktoryzację dziedzicznych kodów, aby wykorzystać tę funkcję dla lepszego bezpieczeństwa typów i ponownego wykorzystania kodu, co demonstruje ewoluujący charakter praktyk refaktoryzacyjnych w Go.

Jednakże narzędzia Go, w tym gofmt do formatowania kodu i go vet do identyfikacji podejrzanych konstrukcji, wspomagają utrzymanie czystych baz kodów, redukując potrzebę obszernej refaktoryzacji. Choć refaktoryzacja jest nieocenionym narzędziem w arsenale programisty Go, mądre korzystanie z funkcji języka Go i narzędzi od samego początku może pomóc zminimalizować potrzebę złożonej refaktoryzacji później.