Собственные пакеты: структура папок, exported и unexported имена
Содержание курса
Конвертер расстояний по пакетам
Навигаторы, логисты и путешественники нередко сталкиваются с тем, что расстояния в разных контекстах выражаются по-разному: дорожные карты оперируют километрами, американские маршруты — милями, а морские и авиационные карты — узлами и морскими милями. Перевод вручную неудобен и легко приводит к ошибкам, поэтому разумно вынести все формульные знания в одно место и обращаться к ним через чёткий интерфейс.
Именно так устроена эта программа: логика пересчёта живёт в отдельном пакете, который отвечает за всё, что связано с коэффициентами и вычислениями, но не выставляет их наружу напрямую. Внешний мир видит только готовые функции-преобразователи, не имея доступа к внутренним константам. Такое разделение позволяет поддерживать чистоту границ между тем, что программа умеет делать, и тем, как именно она это делает.
Точка запуска принимает одно расстояние и, обратившись к пакету, получает его значения сразу в нескольких единицах измерения. Результаты выводятся в строго определённом порядке — так, чтобы пользователь мог одним взглядом сравнить все три величины и выбрать нужную для своего маршрута или документа.
Что нужно сделать
Допишите две экспортируемые функции пакета distance: KilometersToMiles и KilometersToNauticalMiles. Коэффициенты уже объявлены внутри пакета и должны остаться неэкспортируемыми. Публичные тесты импортируют пакет по полному пути модуля и проверяют нулевое, единичное и дробное расстояние.
О данных в ответах
Используйте учебные данные. Не вставляйте пароли, токены, ключи доступа, паспортные и банковские данные, а также персональные данные других людей. Политика обработки данных.
- Public go test ./...
- healthGET /health → 200
Сначала выполняется go test ./..., затем сервис собирается и проверяется HTTP-запросами. Внешняя сеть и скачивание модулей отключены.
distance/convert_test.go
package distance_test
import (
"math"
"testing"
"latorn.local/golang/retrofit/lesson_20/distance"
)
func closeTo(a, b float64) bool { return math.Abs(a-b) < 1e-9 }
func TestConversions(t *testing.T) {
cases := []struct{name string; in, miles, nautical float64}{
{"zero", 0, 0, 0}, {"one", 1, 0.621371, 0.539957}, {"route", 42.5, 26.4082675, 22.9481725},
}
for _, tc := range cases { t.Run(tc.name, func(t *testing.T) {
if got := distance.KilometersToMiles(tc.in); !closeTo(got, tc.miles) { t.Errorf("miles=%v want=%v", got, tc.miles) }
if got := distance.KilometersToNauticalMiles(tc.in); !closeTo(got, tc.nautical) { t.Errorf("nautical=%v want=%v", got, tc.nautical) }
}) }
}
main.go
package main
import (
"io"
"log"
"net/http"
"latorn.local/golang/retrofit/lesson_20/distance"
)
func main() {
_ = distance.KilometersToMiles(1)
mux := http.NewServeMux()
mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.WriteHeader(http.StatusOK)
_, _ = io.WriteString(w, "ok\n")
})
if err := http.ListenAndServe(":18080", mux); err != nil {
log.Fatal(err)
}
}
cmd/convert/main.go
package main
import (
"fmt"
"io"
"os"
"latorn.local/golang/retrofit/lesson_20/distance"
)
func run(in io.Reader, out io.Writer) error {
var km float64
if _, err := fmt.Fscan(in, &km); err != nil { return err }
_, err := fmt.Fprintf(out, "kilometers=%.3f\nmiles=%.6f\nnautical_miles=%.6f\n", km, distance.KilometersToMiles(km), distance.KilometersToNauticalMiles(km))
return err
}
func main() {
if err := run(os.Stdin, os.Stdout); err != nil { fmt.Fprintln(os.Stderr, err) }
}
cmd/convert/main_test.go
package main
import (
"bytes"
"strings"
"testing"
)
func TestRun(t *testing.T) {
var out bytes.Buffer
if err := run(strings.NewReader("42.5\n"), &out); err != nil { t.Fatal(err) }
want := "kilometers=42.500\nmiles=26.408268\nnautical_miles=22.948173\n"
if out.String() != want { t.Fatalf("output=%q want=%q", out.String(), want) }
}
Отправьте решение, чтобы увидеть результаты тестов.
