TGViewer
Cross Join - канал о разработке Cross Join - канал о разработке @crossjoin · 3.82K subscribers
Post #378 3.45K
В догонку к предыдущему посту про гибкость vs простота. В языке Go очень интересно сделаны интерфейсы.

Например, есть какой-то класс (точнее структура с методами) с методом getWeather(city), который является обёрткой над внешним сервисом погоды.



type WeatherService struct {
apiKey string
}

func (w *WeatherService) GetWeather(city string) (string, error) {
// http вызов внешнего API
return "Sunny", nil
}


Мы можем такой класс использовать в каком-то коде, где на основании погоды происходят различные вычисления.



type WeatherAnalyzer struct {
weatherService *WeatherService // конкретный класс
}

func (wa *WeatherAnalyzer) AnalyzeWeather(city string) string {
weather, err := wa.weatherService.GetWeather(city)
if err != nil {
return "Unable to analyze weather"
}
if weather == "Sunny" {
return "Great day for a picnic!"
}
return "Maybe stay indoors"
}

// Использование
func main() {
ws := &WeatherService{apiKey: "some-api-key"}
wa := &WeatherAnalyzer{weatherService: ws}
result := wa.AnalyzeWeather("New York")
fmt.Println(result)
}


И тут мы ВНЕЗАПНО поняли, что покрывать тестами это неудобно (ибо внешние вызовы), и лучше бы написать интерфейс, чтобы потом его замокать. Или у нас вдруг появился еще один сервис погоды, и нужен интерфейс. Так вот, в Go достаточно просто ПО МЕСТУ добавить интерфейс с описанием нужных в этом месте методов, а сам класс погоды останется неизменным.

Вот как это будет выглядеть:


// WeatherService остается без изменений

type WeatherProvider interface {
GetWeather(city string) (string, error)
}

type WeatherAnalyzer struct {
weatherProvider WeatherProvider
}

func (wa *WeatherAnalyzer) AnalyzeWeather(city string) string {
weather, err := wa.weatherProvider.GetWeather(city)
if err != nil {
return "Unable to analyze weather"
}
if weather == "Sunny" {
return "Great day for a picnic!"
}
return "Maybe stay indoors"
}


Потому что в отличие от Java/PHP/etc не нужно писать что класс имплементирует что-то там. Если у класса есть методы такие же как в интерфейсе, то он подходит под сигнатуру

Поэтому ничего заранее не надо придумывать, никакую гибкость. Не надо продумывать, а где нам может понадобиться интерфейс, а какие методы в нём нужны. Ты просто создаешь интерфейс прям там, где тебе надо отвязаться от конкретики ровно с теми методами, которые нужно отвязать.

Теперь мы можем легко создать мок для тестирования (упрощенный пример)



type MockWeatherProvider struct {
MockWeather string
}

func (m *MockWeatherProvider) GetWeather(city string) (string, error) {
return m.MockWeather, nil
}

func TestWeatherAnalyzer(t *testing.T) {
mockProvider := &MockWeatherProvider{MockWeather: "Sunny"}
analyzer := &WeatherAnalyzer{weatherProvider: mockProvider}

result := analyzer.AnalyzeWeather("Test City")
expected := "Great day for a picnic!"

if result != expected {
t.Errorf("Expected %s, but got %s", expected, result)
}
}


У этого конечно есть и недостатки.
  • 👍 19
  • 🌚 5
  • 👎 1
More from @crossjoin
  1. Sep 29, 2026😱 Отправили свое резюме на 129 вакансий на хх, а в ответ тишина .. Думаете, что дело в ры…
  2. Sep 28, 2026Слышал недавно в каком-то подкасте мысль, что Haskell плохо подходит для вайбкодинга прост…
  3. Sep 26, 2026Антон Жиянов написал мини-книгу по Go-concurrency. Это что-то вроде плотного конспекта с и…
  4. Sep 22, 2026photo post
  5. Sep 14, 2026Поможем Руслану собрать фидбек. Проект некоммерческий 👆
  6. Sep 14, 2026AGBX (Agent Box) — небольшой open-source CLI для запуска Claude Code и Codex в изолированн…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →