Встраивание интерфейсов
Встраивание интерфейсов в Go позволяет одному интерфейсу включать методы другого. Это делается путем указания имени встраиваемого интерфейса внутри определения нового. В результате новый интерфейс будет содержать все методы встроенного интерфейса, а также свои собственные. Это удобный способ композиции, когда один тип должен реализовывать несколько наборов методов.
Синтаксис
type NewInterface interface {
EmbeddedInterface1
EmbeddedInterface2
OwnMethod1()
}
В этом примере NewInterface включает все методы
EmbeddedInterface1 и EmbeddedInterface2,
а также добавляет собственный метод OwnMethod1.
Пример
Давайте определим два простых интерфейса: Reader с методом Read
и Writer с методом Write. Затем создадим интерфейс ReadWriter,
встроив в него оба:
package main
import "fmt"
type Reader interface {
Read() string
}
type Writer interface {
Write(data string)
}
// Встраивание интерфейсов Reader и Writer
type ReadWriter interface {
Reader
Writer
}
Теперь структура, реализующая оба метода, будет автоматически удовлетворять
интерфейсу ReadWriter:
type MyFile struct {
content string
}
func (f *MyFile) Read() string {
return f.content
}
func (f *MyFile) Write(data string) {
f.content = data
}
Полный рабочий пример с использованием интерфейса ReadWriter:
package 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)
}
Результат выполнения кода:
"Hello, World!"
Пример
Рассмотрим встраивание нескольких интерфейсов с пересекающимися методами. Если встроенные интерфейсы имеют метод с одинаковым именем и сигнатурой, он включается один раз:
package 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()
}
Результат выполнения кода:
"Closing resource1"
"Flushing resource1"
Как видите, интерфейс CloseFlusher включает методы
Close и Flush без дублирования.
Пример
Встраивание интерфейсов часто применяется для расширения стандартных интерфейсов.
Например, можно расширить io.Reader методом ReadByte:
package 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]))
}
Результат выполнения кода:
"Byte: a"
"Read: bcd"
В этом примере EnhancedReader включает ByteReader
и добавляет метод Read, создавая более мощный интерфейс
для работы с байтовыми потоками.
Смотрите также
-
понятие
интерфейсовв Go,
которые определяют набор методов для реализации -
механизм
неявной реализации,
когда структура автоматически удовлетворяет интерфейсу -
применение
пустого интерфейса,
который может хранить значения любого типа -
конструкцию
приведения типов,
позволяющую извлечь конкретное значение из интерфейса