Ответ 1
Как уже упоминалось в комментариях людей, Go поддерживает композицию, а не наследование.
Чтобы ответить на ваш вопрос о сокращении дублирования кода, вы можете использовать встраивание.
Используя приведенный выше пример из Effective Go, вы начинаете с очень узких интерфейсов, которые выполняют всего несколько вещей:
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
Затем вы можете объединить интерфейсы в другой интерфейс:
// ReadWriter is the interface that combines the Reader and Writer interfaces.
type ReadWriter interface {
Reader
Writer
}
Он работает аналогично для структур, где вы можете создавать структуры, которые реализуют Reader и Writer вместе в другой структуре:
type MyReader struct {}
func (r *MyReader) Read(p []byte) (n int, err error) {
// Implements Reader interface.
}
type MyWriter struct {}
func (w *MyWriter) Write(p []byte) (n int, err error) {
// Implements Writer interface.
}
// MyReadWriter stores pointers to a MyReader and a MyWriter.
// It implements ReadWriter.
type MyReadWriter struct {
*MyReader
*MyWriter
}
По сути, все, что реализует Reader
или Writer
может быть повторно использовано путем их объединения в структуру, и эта внешняя структура автоматически реализует интерфейс ReadWriter
.
Это в основном делает Dependency Injection, и это очень полезно для тестирования.
Пример из приведенного выше кода структуры:
func (rw *MyReadWriter) DoCrazyStuff() {
data := []byte{}
// Do stuff...
rw.Read(data)
rw.Write(data)
// You get the idea...
}
func main() {
rw := &MyReadWriter{&MyReader{}, &MyWriter{}}
rw.DoCrazyStuff()
}
Одна вещь, на которую следует обратить внимание, что она немного отличается от парадигмы композиции других языков, заключается в том, что структура MyReadWriter
теперь может действовать как для Reader
и для Writer
. Поэтому в DoCrazyStuff()
мы используем rw.Read(data)
вместо rw.Reader.Read(data)
.
ОБНОВЛЕНИЕ: Исправлен неверный пример.