Как два разных типа реализуют один и тот же метод в golang, используя интерфейсы?
Скажем, у меня две структуры:
type First struct {
str string
}
type Second struct {
str string
}
И я хочу, чтобы оба они реализовали интерфейс A:
type A interface {
PrintStr() //print First.str or Second.str
}
Кажется излишним иметь реализацию как для первой, так и для второй структур следующим образом:
func (f First) PrintStr() {
fmt.Print(f.str)
}
func (s Second) PrintStr() {
fmt.Print(s.str)
}
Есть ли способ, которым я могу иметь одну реализацию для всех структур, реализующих интерфейс A? Что-то вроде этого, но, похоже, это не работает:
func (a A) PrintStr() {
fmt.Print(a.str)
}
Спасибо!
Ответы
Ответ 1
Нет, вы не можете, , но, вы можете создать базовый тип, а затем вставить его в свою 2-структуру, поэтому нужна только реализация для базового типа:
type WithString struct {
str string
}
type First struct {
WithString
}
type Second struct {
WithString
}
type A interface {
PrintStr() //print First.str or Second.str
}
func (w WithString) PrintStr() {
fmt.Print(w.str)
}
Использование:
a := First{
WithString: WithString{
str: "foo",
},
}
Полный пример игровой площадки
Вставить документацию
Ответ 2
Если логика печати зависит от интерфейса, но не от самих структур, то лучше переносить печать на свободную функцию, которая работает через интерфейс.
В вашем случае метод PrintStr
используется для печати строки, которая является членом каждой структуры.
В этом случае это означает, что каждая структура должна реализовать интерфейс, который возвращает нужную строку, используемую для печати, а PrintStr
становится функцией, принимающей параметр Printable
.
type First struct {
str string
}
type Second struct {
str string
}
type Printable interface {
String() string
}
func (p First) String() string {
return p.str
}
func (p Second) String() string {
return p.str
}
func PrintStr(p Printable) {
fmt.Print(p.String())
}
Использование интерфейса A
не является идиоматическим, поскольку интерфейс не должен зависеть от реализации его функциональности.
Вместо этого с этим решением вы все равно можете сохранить интерфейс A, но упростите каждую реализацию:
func (f First) PrintStr() {
PrintStr(f)
}
func (s Second) PrintStr() {
PrintStr(s)
}
Он по-прежнему является избыточным, но логика заключается в функции, вызываемой оттуда, что ограничивает необходимость копирования в случае модификации логики печати.
Этот шаблон распространен в стандартной библиотеке Go, потому что многие полезные функции построены на интерфейсах, которые они не могут распространяться, например io.Reader.
Это простой интерфейс только с одним методом, но он используется полностью из многих других пакетов.
Если вы посмотрите на ioutil.ReadAll, можно утверждать, что он мог быть реализован как другой метод интерфейса io.Reader
, однако это упрощает чтение читателей, концентрируясь на их единственном методе, позволяя любому разработчику использовать ReadAll бесплатно.
Ответ 3
Возможно, это не лучший способ решить вашу проблему, но вы можете использовать оболочку, чтобы избежать "реализации" функции дважды, примерно так:
type First struct {
str StringWrapper
}
type Second struct {
str StringWrapper
}
type StringWrapper struct {
str string
}
func (f StringWrapper) PrintStr() {
fmt.Print(f.str)
}
func main() {
var a First = First{str:StringWrapper{str: "aaa"}};
a.str.PrintStr();
}
Ответ 4
Почему бы просто не оставить эту функцию вне интерфейса и передать тип A в качестве параметра?
type A interface {}
type First struct {
str string
}
type Second struct {
str string
}
func PrintStr(a A) {
fmt.Print(a.str)
}