Подтвердить что ты не робот

Голанг и наследование

Я хочу предоставить базовую структуру методам в моей библиотеке, которые могут быть расширены.

Методы этой базовой структуры полагаются на методы из расширяющейся структуры. Это невозможно прямо в Go, потому что методы struct имеют доступ только к собственным полям structs, а не к родительским структурам.

Цель состоит в том, чтобы иметь функциональность, которую я не должен повторять в каждом расширяющемся классе.

Я придумал этот шаблон, который отлично работает, но выглядит довольно запутанным из-за его циклической структуры.

Я не нашел ничего подобного в другом коде Go. Это очень неуправляемо? Какой другой подход я мог бы принять?

type MyInterface interface {
  SomeMethod(string)
  OtherMethod(string)
}

type Base struct{
  B MyInterface
}

func (b *Base) SomeMethod(x string) {
  b.B.OtherMethod(x)
}

type Extender struct {
  Base
}

func (b *Extender) OtherMethod(x string) {
  // Do something...
}

func NewExtender() *Extender { 
  e := Extender{}
  e.Base.B = &e
  return &e
}
4b9b3361

Ответ 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).

ОБНОВЛЕНИЕ: Исправлен неверный пример.

Ответ 2

Извините, что разочаровал вас, но вы задаете неправильный вопрос. У меня была аналогичная проблема, когда я начал писать код Go.

Вы не можете просто взять иерархию классов и перевести ее в код Go, по крайней мере, не с удовлетворительными результатами. Обычно есть очень элегантный и простой способ решить такие вещи в Go, но чтобы обнаружить их, вам нужно думать немного по-другому, как вы привыкли.

К сожалению, ваш вопрос ничего не говорит о проблеме , что, которую вы пытаетесь решить. Вы только что описали как, который вы хотели бы решить. Поэтому я немного неохотно даю общий ответ, так как это не приведет к идиоматическому коду Go. Я понимаю, если вы разочарованы этим ответом, но, на мой взгляд, это самый ценный ответ, который вы можете получить:)