Я использую фабричный объект FooFactory
для создания экземпляров типа Foo
, который имеет некоторые закрытые члены-данные. Я использую фабрику, так что объект Bar
, который создает экземпляры Foo
, не должен предоставлять (или даже знать о) эти частные элементы данных: я сначала настраиваю фабрику с необходимыми личными данными, а затем даю Bar
эта сконфигурированная фабрика.
Я хочу, чтобы Bar
использовал эти Foo
объекты через интерфейс, чтобы я мог высмеивать их с помощью gomock
и проверить, что Bar
использует их правильно. Из того, что я читал об интерфейсах в go, лучше всего определить интерфейсы, где они используются, а не где определены базовые типы, поэтому у меня интерфейс Fooer
в том же пакете, что и мой Bar
объект, и Bar
использует этот Fooer
интерфейс везде, где он ожидает Foo
(или позже MockFoo
).
Я также хочу, чтобы Bar
использовал FooFactory
через интерфейс дляпо той же причине, поэтому я могу посмеяться над этим и утверждать, что он создает Foo
объектов, когда я этого ожидаю. И снова, я определяю интерфейс в Bar
s пакете FooBuilder
, который неявно реализует базовый FooFactory
.
Теперь проблема в в том, что FooFactory
возвращает бетон Foo
объектов, поскольку они оба находятся в одном пакете Foo
и не должны знать о локально определенных интерфейсах Bar
. Однако я хочу, чтобы FooBuilder
строил объекты типа Fooer
, а не объекты типа Foo
, потому что он не должен знать о базовых типах. Go не допускает этого, так как типы возврата различаются, и он говорит, что FooFactory
не реализует FooBuilder
.
Здесь минимальное воспроизведение без описанной выше структуры пакета:
type Foo struct{}
func (f *Foo) FooMethod() {}
type FooFactory struct{}
func (ff *FooFactory) Build() *Foo {
return &Foo{}
}
type Fooer interface {
FooMethod()
}
type FooBuilder interface {
Build() Fooer
}
func main() {
f := &Foo{}
ff := &FooFactory{}
var fooer Fooer
var fooBuilder FooBuilder
fooer = f
fooBuilder = ff // << ERROR
}
Иди жалуется:
cannot use ff (type *FooFactory) as type FooBuilder in assignment:
*FooFactory does not implement FooBuilder (wrong type for Build method)
have Build() *Foo
want Build() Fooer
Мой вопрос по сути, я делаю это правильно ? После опыта работы с профессиональными языками C ++ и Java, я пытаюсь принять концепцию go о неявном удовлетворении интерфейса, но это кажется странным. Тот факт, что это нелегко сделать, заставляет меня думать, что я делаю что-то не так, но все, что я читаю в Интернете об интерфейсах go, говорит об определении интерфейсов, близких к тому, где они используются, и именно это я действительно пытаюсь сделать.