sergoslav_0
@sergoslav_0
PHP / Magento / Laravel

Как решить проблему X does not implement Y при работе с интерфесами?

Допустим есть структура Repository которая хранит в себе другие структуры с методами для работы с DB (в примере ниже - Subrepo). Чтобы избежать жесткой связи с конкретной реализацией Repository, я предполагаю что нужно использовать интерфейсы.
Но никак не могу решить проблему, когда интерфейс содержит метод который возвращает другой интерфейс.

import "fmt"

//import repository

type RepositoryInterface interface {
 GetSubrepo() SubrepoInterface // << Не работает
 // GetSubrepo() Subrepo // << Работает
}

type SubrepoInterface interface {
 HelloSubrepo()
}

func main() {
 r := Repository{subrepo: Subrepo{}}
 useRepository(r)
}

// func touchRepository(c Repository) {
func useRepository(r RepositoryInterface) {
 sr := r.GetSubrepo()
 sr.HelloSubrepo()
}

// ******************** Repository package ****************
type Repository struct {
 subrepo Subrepo
}

func (r *Repository) GetSubrepo() Subrepo {
 return r.subrepo
}

type Subrepo struct {
}

func (sr *Subrepo) HelloSubrepo() {
 fmt.Printf("hello subrepo\n")
}


Ошибка:
./prog.go:18:16: cannot use r (variable of type Repository) as RepositoryInterface value in argument to useRepository: Repository does not implement RepositoryInterface (wrong type for method GetSubrepo)
have GetSubrepo() Subrepo
want GetSubrepo() SubrepoInterface


Подскажите, как правильно решить такую задачу, сохранив слабую связанность через интерфейсы.
  • Вопрос задан
  • 185 просмотров
Решения вопроса 1
sergoslav_0
@sergoslav_0 Автор вопроса
PHP / Magento / Laravel
Как вариант, мне подсказали решение через дженерики

package main

import "fmt"

//import repository

type RepositoryInterface[T SubrepoInterface] interface {
	GetSubrepo() T
}

type SubrepoInterface interface {
	HelloSubrepo()
}

func main() {
	r := &Repository{subrepo: &Subrepo{}}
	useRepository[*Subrepo](r)
}

func useRepository[T SubrepoInterface](r RepositoryInterface[T]) {
	sr := r.GetSubrepo()
	sr.HelloSubrepo()
}

// ******************** Repository package ****************
type Repository struct {
	subrepo *Subrepo
}

func (r *Repository) GetSubrepo() *Subrepo {
	return r.subrepo
}

type Subrepo struct {
}

func (sr *Subrepo) HelloSubrepo() {
	fmt.Printf("hello subrepo\n")
}
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 2
По-хорошему, у вас репозиторий не должен никогда возвращать интерфейс. В Го принято возвращать конкретный тип, а не интерфейс. https://github.com/golang/go/wiki/CodeReviewCommen...

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

Если сказать по-другому: в domain-пакетах у вас не должно быть импортов каких-то структур из пакета БД, только наоборот — из пакета БД импортить типы из domain.
Ответ написан
Комментировать
Maksclub
@Maksclub
maksfedorov.ru
package main

import "fmt"

type RepositoryInterface interface {
	GetSubrepo() SubrepoInterface
}
type SubrepoInterface interface {
	HelloSubrepo()
}

func main() {
	r := Repository{subrepo: Subrepo{}}
	useRepository(&r)
}

// func touchRepository(c Repository) {
func useRepository(r RepositoryInterface) {
	sr := r.GetSubrepo()
	sr.HelloSubrepo()
}

// ******************** Repository package ****************
type Repository struct {
	subrepo Subrepo
}

func (r *Repository) GetSubrepo() SubrepoInterface {
	return &r.subrepo
}

type Subrepo struct {
}

func (sr *Subrepo) HelloSubrepo() {
	fmt.Printf("hello subrepo\n")
}
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы