Тип возврата интерфейса Go - PullRequest
0 голосов
/ 09 декабря 2018

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

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 () View

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

спасибо

Ответы [ 2 ]

0 голосов
/ 09 декабря 2018

Поскольку 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 имеет твердые идеи о том, как должен быть структурирован ваш проект. Я предлагаю этот урок .

0 голосов
/ 09 декабря 2018

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()
  }
}
...