【发布时间】:2020-02-20 12:41:26
【问题描述】:
我正在使用工厂对象FooFactory 创建具有一些私有数据成员的Foo 类型的实例。我使用工厂,以便创建 Foo 实例的对象 Bar 不需要提供(甚至知道)这些私有数据成员:我首先使用必要的私有内容配置工厂,然后提供 @987654325 @这个已配置的工厂。
我希望Bar 通过接口使用这些Foo 对象,以便我可以使用gomock 模拟它们并测试Bar 是否正确使用它们。根据我在 go 中读到的有关接口的内容,最佳实践是定义使用它们的接口,而不是定义底层类型的位置,因此我在与 Bar 相同的包中有一个 Fooer 接口对象,而Bar 在需要Foo(或更高版本的MockFoo)的任何地方都使用此Fooer 接口。
出于同样的原因,我还希望Bar 通过接口使用FooFactory,因此我可以模拟它并断言它会在我期望的时候创建Foo 对象。同样,我在Bars 包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 接口的所有内容都说要定义接近使用它们的接口,这就是我真正想要做的.
【问题讨论】:
-
在我看来,在开始设计时就考虑了工厂和模拟对象,这似乎使事情变得过于复杂。不要那样做。这在 Java/C++/C# 中可能很好(或者至少是公认的最佳实践)。例如。如果 Foo 和 Bar 位于同一个包中,那么 Bar 了解 Foo 的内部结构可能不会有任何问题。不要整天隐藏信息。甚至 C++ 也认识朋友。不要嘲笑。写存根或伪造。 Mocking 在 Go 中不是惯用的。您的测试位于同一个包中,甚至可以检查内部结构。
-
如果您坚持使用 Factory 和 Builder:您必须让您的 Factory 返回一个 Fooer。非常简单,这就是 Go 的类型系统的工作原理。这里没有解决方法。考虑使用更多函数或函数闭包而不是对象和方法。 Go 对象通常由函数 NewFoo 构建,而不是通过 fooFactory.Build。如果此构建过程无法从第一原则完成,请尝试为 NewFoo 使用函数闭包。