Тип возврата интерфейса Go

У меня есть такой интерфейс:

type ViewInterface interface{
    Init() View
}

type View struct{
    Width  int
    Height int
}

Поэтому я создаю новый тип из View

type MainView View

func (m MainView) Init() MainView{
 return MainView{
   Width:10,
   Height:10,
 }
}

затем я передаю MainView к следующему методу:

func Render(views ...ViewInterface){
  for _, view := range views {
     v := view.Init()
  }
}

func main() {
  Render(MainView{})
}

Но я получаю эту ошибку:

нельзя использовать литерал MainView (тип MainView) как тип ViewInterface в аргументе для Render: MainView не реализует ViewInterface (неправильный тип для метода Init)
есть Init() MainView
хочу Init () Просмотр

Зачем MianView не то же самое, что View? Как правильно решить эту проблему?

Спасибо

2 ответа

Решение

GO имеет другую модель наследования по сравнению с основными языками, такими как Java и C#.

Почему MianView отличается от View?

Потому что они определены по-разному.

Init функция MainView возвращается MainView в то время как интерфейс требует возврата View,

Подпись Init Метод выглядит странно, он требует экземпляра структуры, потому что он является структурным методом и возвращает новый экземпляр того же типа структуры.

Попытайтесь спроектировать интерфейс вокруг логики ваших структур вместо их конструкции / времени жизни:

type ViewInterface interface{
    Render()
}

type MainView View

func (m MainView) Render() {
  // do something
}

type AnotherView

func (m AnotherView) Render() {
  // do something else
}

func Render(views ...ViewInterface){
  for _, view := range views {
     view.Render()
  }
}

Так как type MainView View является "определенным типом" и "отличается от любого другого типа, включая тип, из которого он создан.".

Вместо этого вы можете использовать псевдоним типа. type MainView = View,


Но на самом деле проблема заключается в дизайне ViewInterface а также Init(),

Init() написано как метод класса. У Go нет методов класса (или, строго говоря, классов). Вы создаете структуру и вызываете методы для нее. Простая инициализация может быть сделана прямо тогда.

view := View{ Width: 10, Height: 10 }

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

type ViewInterface interface{
    Init()
}

type View struct{
    Width  int
    Height int
}

func (v *View) Init() {
    v.Width = 10
    v.Height = 10
}

view := View{}
view.Init()

затем MainView также можно определить Init(),

type MainView struct {
    X int
    Y int
}

type (mv *MainView) Init() {
    mv.X = 23
    mv.Y = 42
}

Так как Init() берет указатель, чтобы удовлетворить ViewInterface Вы должны передать в указателях.

func main() {
    view := View{}
    mv := MainView{}
    Render(&view, &mv)
}

Но что это Render() делать инициализацию объектов в любом случае? Это уже должно быть сделано. Это должен быть рендеринг. Интерфейсы должны быть связаны с общей функциональностью, независимо от того, как она реализована. Вещи, реализующие ViewInterface, уже должны быть инициализированы.

Вместо этого вы можете сказать, что ViewInterface должен иметь Render метод.

type ViewInterface interface{
    Render()
}

затем View а также MainView может быть структурирован, как вам нравится, пока они реализуют Render(),

func (v View) Render() {
    fmt.Println("View!")
    fmt.Println(v)
}

func (mv MainView) Render() {
    fmt.Println("MainView!")
    fmt.Println(mv)
}

ЗатемRender() может взять список вещей, которые реализуют ViewInterface и позвонить Render() на каждом из них.

func Render(views ...ViewInterface){
  for _, view := range views {
     view.Render()
  }
}

Инициализируйте их, прежде чем передавать их. И теперь нет необходимости передавать указатели.

func main() {
    view := View{}
    view.Init()
    mv := MainView{}
    mv.Init()
    Render(view, mv)
}

Наконец, Маркус предложил в комментариях использовать пакет, чтобы получить что-то вроде методов класса.

# viewtest/main.go
package main

import(
    "log"
    "viewtest/view"
)

func main() {
    v := view.New()
    log.Printf("%#v", v)
}


# viewtest/view/view.go
package view

type View struct {
    Width  int
    Height int
}

func New() View {
    return View{Width: 10, Height: 10}
}

Пакеты Go требуют некоторого привыкания. Go имеет четкие представления о том, как должен быть структурирован ваш проект. Я предлагаю этот урок.

Другие вопросы по тегам