【问题标题】:Generic approach and recommendation how to remove dependency通用方法和建议如何消除依赖
【发布时间】:2020-01-16 09:14:28
【问题描述】:

我有一个关于依赖注入的问题。 请考虑以下示例。

例如 selector() 是一个选择某物并保证返回接口的函数

在这个例子中

bar.node.go

type NodeTemplate struct {
     Name string
}

// satisfy interface declared in db.foo.go 
//but never imports anything from db.foo.go
func (node *NodeTemplate) GetUuidName() string {
   if node != nil {
      return node.Name
   }
   return 
}

db.foo.go

// interface declared in db.foo.go

type Node interface {
   GetUuidName() string
}

选项 A // 所以 selector 接收到 Some 接口的映射并填充映射

func SelectSomething(nodemap map[string]Node, selectFactor string) {
     // selection from db and result populate in a map
}

选项 B

另一种模式 SelectSomething 返回一个节点 和它的接口

所以另一个包将依赖于导入节点 这将引入依赖关系。

func SelectSomething(seleconsomething) []*Node {
  // do selection and return a slice of SomeInterface

n := &Node{} // here it need to be concret type T
return Node
}

因此,根据我所描述的逻辑,我认为第一种方法更好,但在这种方法中,选择需要进行具体的类型分配才能填充地图。

考虑另一个例子

db.foo.go

type Node interface {
   GetUuidName() string
}

func inserter(node *Node) error {
   // do some work 
   node.GetUuidName()
}

对于inserter这种情况,inserter没有外部依赖,inserter只需要接收满足接口的东西。在本地声明接口并消除依赖关系。

但是在选择器示例的情况下,它必须进行内存分配才能返回或填充映射或返回具有具体类型 T 的东西。所以在这两种情况下,它都必须具有内部重新表示。

所以这是我的问题,选择器可以在运行时以某种方式根据接口找出它接收的类型,并实例化该类型的对象并作为接口插入到地图中或返回接口的一部分。 ?

通过这样做,选择器函数将不依赖于它接收到的内容,它只是保证它会实例化相同的对象类型 T 并返回界面。

或者可以选择器返回接口,但我想我必须在 db 包和包 X 之间有一个双向接口,或者动态调度程序需要做一些魔法?

【问题讨论】:

  • 我不喜欢重新发明轮子,当谈到依赖注入(从长远来看,恕我直言,这个概念带来的痛苦多于好处),Go has actually a plethora of options
  • factory pattern 是尝试做这种事情的 Java 程序的主要部分(有第二个接口,可以创建第一个接口类型的对象,并将工厂作为参数传递给SelectSomething() 需要创建新对象),但在我所见的 Go 中并不是特别常见。

标签: go interface


【解决方案1】:

您希望一个类型以某种方式运行。这是通过接口实现的。这个案子也不例外。只需将所需的行为添加到您的界面,如下面的Foo 界面所示。

package main

import (
    "fmt"
    "reflect"
)

type Foo interface {
    Bar()
    TypeOf() reflect.Type
}

type Baz struct{}

func (b Baz) Bar() {
    fmt.Println("I am a Fooer!")
}

func (b Baz) TypeOf() reflect.Type {
    return reflect.TypeOf(b)
}

func DoSomeThing(f Foo) {
    f.Bar()
    fmt.Println(f.TypeOf())
}

func main() {
    fmt.Println("Hello, playground")
    b := Baz{}
    DoSomeThing(b)
}

Run on playground

【讨论】:

  • 这不是我要问的。您的示例与上一个示例中所指示的完全一致。我的问题是包 X 中的 foo() 如何在不实际在包中创建类型 T 的情况下返回一片 *interface 或映射。因此,如果类型 T 是 T2 的子集,则外部接收者可以将 case 类型键入 T2。我猜你不能需要继承,或者在一个包中你需要创建类型 T 本身实现所有接口返回,作为接口的一部分,并且在接收器上做同样的事情,所以它是双向映射。
  • @JohnnyBonelli 是的,有一个很好的理由,Go 中没有继承这样的东西。但是你可以使用组合。不过,我真的不确定我是否得到了你的问题。从我的角度来看,它过于复杂。
猜你喜欢
  • 1970-01-01
  • 2011-12-24
  • 2011-10-06
  • 1970-01-01
  • 2023-03-12
  • 2013-12-10
  • 1970-01-01
  • 1970-01-01
  • 2019-03-06
相关资源
最近更新 更多