Menu
Coddy logo textTech

Неявная реализация

Часть раздела Объектно-ориентированное программирование путешествия по GO на Coddy — урок 26 из 107.

Во многих объектно-ориентированных языках, таких как Java или C#, вы должны явно объявить, что тип реализует интерфейс, используя такие ключевые слова, как implements. Go использует другой подход: реализация интерфейса является неявной.

Тип удовлетворяет интерфейсу, просто реализуя все его методы. Нет необходимости объявлять эту связь где-либо. Компилятор определяет это автоматически.

type Writer interface {
    Write(data string) int
}

type FileWriter struct {
    Filename string
}

// FileWriter неявно реализует Writer
func (f FileWriter) Write(data string) int {
    fmt.Println("Writing to", f.Filename)
    return len(data)
}

Обратите внимание, что в FileWriter отсутствует объявление implements Writer. Поскольку FileWriter имеет метод Write(data string) int, соответствующий сигнатуре интерфейса, он автоматически удовлетворяет интерфейсу Writer.

Такой дизайн имеет важное последствие: вы можете определять интерфейсы уже после того, как типы были созданы. Если в сторонней библиотеке есть тип с нужными вам методами, вы можете создать интерфейс, которому он уже соответствует, не изменяя исходный код.

func Save(w Writer, content string) {
    w.Write(content)
}

func main() {
    fw := FileWriter{Filename: "data.txt"}
    Save(fw, "Hello!")  // Работает, так как FileWriter удовлетворяет Writer
}

Неявная реализация делает код на Go слабосвязанным и гибким. Типам не нужно знать об интерфейсах заранее, что упрощает построение систем из независимых частей.

challenge icon

Задание

Легко

Давайте создадим систему доставки сообщений, которая демонстрирует, как типы неявно удовлетворяют интерфейсам в Go. Вы создадите различные типы мессенджеров, которые удовлетворяют одному и тому же интерфейсу — без явного объявления этой связи.

Вы организуете свой код в двух файлах:

  • messengers.go: Определите интерфейс Messenger с единственным методом Send(message string) string. Затем создайте две структуры, которые будут неявно удовлетворять этому интерфейсу:
    • EmailMessenger с полем Address (string)
    • SMSMessenger с полем PhoneNumber (string)
    Каждая структура должна иметь метод Send, который возвращает строку, описывающую доставку. Ни одна из структур не должна явно объявлять, что она реализует Messenger — Go определит это автоматически на основе сигнатур методов.
  • main.go: Создайте функцию с именем DeliverMessage, которая принимает любой Messenger и строку сообщения, а затем возвращает результат вызова Send. Считайте контактную информацию из входных данных, создайте оба типа мессенджеров и используйте свою функцию для доставки сообщений через каждый из них.

Будут предоставлены следующие входные данные:

  • Строка 1: Email адрес
  • Строка 2: Номер телефона
  • Строка 3: Сообщение для отправки

Ваши методы Send должны возвращать строки в следующих форматах:

  • EmailMessenger: Email to [Address]: [message]
  • SMSMessenger: SMS to [PhoneNumber]: [message]

Например, при вводе alice@example.com, 555-1234 и Hello!, ваш вывод должен быть:

Email to alice@example.com: Hello!
SMS to 555-1234: Hello!

Ключевая идея здесь заключается в том, что ваша функция DeliverMessage принимает любой Messenger, и как EmailMessenger, так и SMSMessenger удовлетворяют этому интерфейсу просто за счет наличия соответствующего метода Send. Здесь нигде нет ключевого слова implements — неявная реализация в Go берет все на себя.

Попробуйте сами

package main

import (
	"bufio"
	"fmt"
	"os"
)

// TODO: Создайте функцию DeliverMessage, которая принимает любой Messenger
// и строку сообщения, затем возвращает результат вызова Send

func main() {
	reader := bufio.NewReader(os.Stdin)
	
	// Чтение адреса электронной почты
	email, _ := reader.ReadString('\n')
	email = email[:len(email)-1]
	
	// Чтение номера телефона
	phone, _ := reader.ReadString('\n')
	phone = phone[:len(phone)-1]
	
	// Чтение сообщения
	message, _ := reader.ReadString('\n')
	if len(message) > 0 && message[len(message)-1] == '\n' {
		message = message[:len(message)-1]
	}
	
	// TODO: Создайте EmailMessenger с адресом электронной почты
	
	// TODO: Создайте SMSMessenger с номером телефона
	
	// TODO: Используйте DeliverMessage для отправки сообщения через каждый мессенджер
	// и выведите результаты
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование