Встраивание интерфейсов

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

Синтаксис

gotype NewInterface interface {
  EmbeddedInterface1
  EmbeddedInterface2
  OwnMethod1()
}

В этом примере NewInterface включает все методы EmbeddedInterface1 и EmbeddedInterface2, а также добавляет собственный метод OwnMethod1.

Пример

Давайте определим два простых интерфейса: Reader с методом Read и Writer с методом Write. Затем создадим интерфейс ReadWriter, встроив в него оба:

gopackage main

import "fmt"

type Reader interface {
  Read() string
}

type Writer interface {
  Write(data string)
}

// Встраивание интерфейсов Reader и Writer
type ReadWriter interface {
  Reader
  Writer
}

Теперь структура, реализующая оба метода, будет автоматически удовлетворять интерфейсу ReadWriter:

gotype MyFile struct {
  content string
}

func (f *MyFile) Read() string {
  return f.content
}

func (f *MyFile) Write(data string) {
  f.content = data
}

Полный рабочий пример с использованием интерфейса ReadWriter:

gopackage main

import "fmt"

type Reader interface {
  Read() string
}

type Writer interface {
  Write(data string)
}

type ReadWriter interface {
  Reader
  Writer
}

type MyFile struct {
  content string
}

func (f *MyFile) Read() string {
  return f.content
}

func (f *MyFile) Write(data string) {
  f.content = data
}

func main() {
  var rw ReadWriter = &MyFile{}
  rw.Write("Hello, World!")
  res := rw.Read()
  fmt.Println(res)
}

Результат выполнения кода:

go"Hello, World!"

Пример

Рассмотрим встраивание нескольких интерфейсов с пересекающимися методами. Если встроенные интерфейсы имеют метод с одинаковым именем и сигнатурой, он включается один раз:

gopackage main

import "fmt"

type Closer interface {
  Close() error
}

type Flusher interface {
  Flush() error
}

// Интерфейс, объединяющий Close и Flush
type CloseFlusher interface {
  Closer
  Flusher
}

type Resource struct {
  name string
}

func (r *Resource) Close() error {
  fmt.Printf("Closing %s\n", r.name)
  return nil
}

func (r *Resource) Flush() error {
  fmt.Printf("Flushing %s\n", r.name)
  return nil
}

func main() {
  var cf CloseFlusher = &Resource{name: "resource1"}
  cf.Close()
  cf.Flush()
}

Результат выполнения кода:

go"Closing resource1"
"Flushing resource1"

Как видите, интерфейс CloseFlusher включает методы Close и Flush без дублирования.

Пример

Встраивание интерфейсов часто применяется для расширения стандартных интерфейсов. Например, можно расширить io.Reader методом ReadByte:

gopackage main

import "fmt"

type ByteReader interface {
  ReadByte() (byte, error)
}

type EnhancedReader interface {
  ByteReader
  Read(p []byte) (int, error)
}

// Простая структура, реализующая EnhancedReader
type SimpleReader struct {
  data []byte
  pos  int
}

func (r *SimpleReader) Read(p []byte) (int, error) {
  if r.pos >= len(r.data) {
    return 0, fmt.Errorf("EOF")
  }
  n := copy(p, r.data[r.pos:])
  r.pos += n
  return n, nil
}

func (r *SimpleReader) ReadByte() (byte, error) {
  if r.pos >= len(r.data) {
    return 0, fmt.Errorf("EOF")
  }
  b := r.data[r.pos]
  r.pos++
  return b, nil
}

func main() {
  var er EnhancedReader = &SimpleReader{data: []byte("abcde")}
  b, err := er.ReadByte()
  if err == nil {
    fmt.Printf("Byte: %c\n", b)
  }

  p := make([]byte, 3)
  n, _ := er.Read(p)
  fmt.Printf("Read: %s\n", string(p[:n]))
}

Результат выполнения кода:

go"Byte: a"
"Read: bcd"

В этом примере EnhancedReader включает ByteReader и добавляет метод Read, создавая более мощный интерфейс для работы с байтовыми потоками.

Смотрите также