Best way to handle decoupling in Go with similar structs in two different packages but subitem in struct making it difficult?

huangapple go评论79阅读模式
英文:

Best way to handle decoupling in Go with similar structs in two different packages but subitem in struct making it difficult?

问题

我对Go相对较新,一直在进行大规模重写,试图尽量减少依赖关系图。我对我所做的工作感到非常满意,但有一个部分我不确定如何处理最好。如果答案是“你将在这两者之间有依赖关系”,那也没关系,我只是在寻找一个好的方法,不期望奇迹发生。

所以下面我有两个包,ab,它们都有相同的结构体。通常情况下,你可以在主函数中将一个转换为另一个,但每个结构体都有一个子项,该子项也是一个结构体,这会阻止Go允许这样做,即使子项具有相同的签名。

一种方法是在B中的结构体中引用A.TzConfig,并允许存在依赖关系,但这正是我想要摆脱的。

我猜另一种方法是创建一个接口,然后通过方法获取Loc的值,我认为这样可以工作,但我还没有尝试过,因为这意味着为仅仅是数据结构的东西创建方法(实际结构体有很多项,我在这里简化了它们),这似乎有点过度设计。

我可以将TzConfig移动到第三个模块中,这样它们两个都会引用那个模块,而不是一个引用另一个,这是我想到的全部方法。

所以我的问题是,对于有实际经验的人来说,在Go中处理这种情况的最佳方法是什么?

我应该提到它们具有重复的结构体的原因只是因为我试图打破它们之间的依赖关系,原始代码只在一个包中有结构体,而另一个包引用它。

package a

type Cfg struct {
	Addr                 string
	Loc                  TzConfig
}

type TzConfig struct {
	String string
	TZ     *time.Location `validate:"noDescent"`
}

func GetCfg() Cfg {
	t, _ := time.LoadLocation("MST")
	return Cfg{
		Addr: "abc",
		Host: "a.bc.d",
		Loc:  config.TzConfig{
			String: "MST",
			TZ:     t,
		},
	}
}
package b

type Cfg struct {
	Addr                 string
	Loc                  TzConfig
}

type TzConfig struct {
	String string
	TZ     *time.Location `validate:"noDescent"`
}

func DoSomethingWithConfig(c Cfg) {
	fmt.Println(c)
}
package main

main() {
	 c := a.GetCfg()
	 d := b.DoSomethingWithConfig(b.Cg(c))
	 fmt.Println(d)
}
英文:

I'm relatively new to go and have been doing a massive rewrite trying to reduce my dependency graph as much as possible. I'm pretty happy with where I got it but there is this one part I am not sure how to best handle. If the answer is, "You are going to have that dependency between the two", that's fine too, I'm just looking for a good approach, not expecting miracles.

So below I have two packages, a & b, and they both have identical structs. Normally you could convert one to the other in main but each has a subitem which is also a struct and that's stopping Go from allowing it even though the subitems have identical signatures.

One way would be to just reference A.TzConfig in the struct in B and let there be a dependency but that's what I am trying to get rid of.

I guess another way is to create an interface and then get the values of Loc through methods, I think that would work but I haven't tried it yet as that means creating methods for something that's just a structure of data (the actual structure has a lot of items, I reduced it to the essentials for simplicity here) which seems like overkill.

I could move TzConfig into a third module so they would both reference that instead of one referencing the other and that's about all I have thought of.

So my question is, from someone with a real experience, what would be the best way to deal with this scenario in go?

I should mention that the reason they have duplicated structs was just because I was trying to break the dependency between them, the original code just had the struct in one package and the other package referencing it.

package a

type Cfg struct {
	Addr                 string
	Loc                  TzConfig
}

type TzConfig struct {
	String string
	TZ     *time.Location `validate:"noDescent"`
}

func GetCfg() Cfg {
	t, _ := time.LoadLocation(`MST`)
	return Cfg{
		Addr: "abc",
		Host: "a.bc.d",
		Loc:  config.TzConfig{
			String: "MST",
			TZ:     t,
		},
	}
}
package b

type Cfg struct {
	Addr                 string
	Loc                  TzConfig
}

type TzConfig struct {
	String string
	TZ     *time.Location `validate:"noDescent"`
}

func DoSomethingWithConfig(c Cfg) {
	fmt.Println(c)
}
package main

main() {
	 c := a.GetCfg()
	 d := b.DoSomethingWithConfig(b.Cg(c))
	 fmt.Println(d)
}

答案1

得分: 0

IMHO,@BurakSerdar提供的建议完全可以,并且非常适合你的情况。我已经按照以下方式重新编写了代码。

common

package common

import "time"

type Cfg struct {
	Addr string
	Loc  TzConfig
}

type TzConfig struct {
	String string
	TZ     *time.Location `validate:"noDescent"`
}

在这里,你应该放置共享的结构体、函数、方法等。

a

package a

import (
	"dependencies/common"
	"time"
)

type Cfg struct {
	common.Cfg
	Host string
}

func GetCfg() Cfg {
	t, _ := time.LoadLocation("MST")
	return Cfg{
		Cfg: common.Cfg{
			Addr: "abc",
			Loc: common.TzConfig{
				String: "MST",
				TZ:     t,
			},
		},
		Host: "a.bc.d",
	}
}

在这里,你可以看到 a 包中的特定代码继承了 common 包中的共享代码,如 import 部分所示。

请注意,我使用了结构体嵌入的特性来获取在 common 包中定义的共享字段。

b

package b

import (
	"dependencies/common"
	"fmt"
)

func DoSomethingWithConfig(c common.Cfg) string {
	return fmt.Sprint(c)
}

在这里,没有什么特别需要提到的。

main

package main

import (
	"dependencies/a"
	"dependencies/b"
	"fmt"
)

func main() {
	c := a.GetCfg()
	d := b.DoSomethingWithConfig(c.Cfg)
	fmt.Println(d)
}

在这里,代码应该非常直观。我导入了 ab 包以利用它们的功能。

再次强调,这是一个主观的话题,因此没有一种万能的解决方案。对我来说,这看起来很整洁清晰。我肯定会选择这种方式。请告诉我你的想法,谢谢!

英文:

IMHO, the suggestion provided by @BurakSerdar are completely fine and fits very well for your scenario. I've rewritten the code in this way.

package common

package common

import "time"

type Cfg struct {
	Addr string
	Loc  TzConfig
}

type TzConfig struct {
	String string
	TZ     *time.Location `validate:"noDescent"`
}

Here, you should put the common structs, functions, methods, and so on.

package a

package a

import (
	"dependencies/common"
	"time"
)

type Cfg struct {
	common.Cfg
	Host string
}

func GetCfg() Cfg {
	t, _ := time.LoadLocation(`MST`)
	return Cfg{
		Cfg: common.Cfg{
			Addr: "abc",
			Loc: common.TzConfig{
				String: "MST",
				TZ:     t,
			},
		},
		Host: "a.bc.d",
	}
}

Here, you have the specific code related to the package a that inherits the shared code from the common package, as you can see in the import section.
> Please note that I used the structs embedding feature to get the shared fields defined within the common package.

package b

package b

import (
	"dependencies/common"
	"fmt"
)

func DoSomethingWithConfig(c common.Cfg) string {
	return fmt.Sprint(c)
}

Here, there is nothing special to mention.

package main

package main

import (
	"dependencies/a"
	"dependencies/b"
	"fmt"
)

func main() {
	c := a.GetCfg()
	d := b.DoSomethingWithConfig(c.Cfg)
	fmt.Println(d)
}

Here, the code should be pretty straightforward. I imported packages a and b to exploit their functionalities.

Again, I'd like to be clear that this is a subjective topic, hence there isn't a silver bullet solution. To me, looks neat and clear. I would have chosen this way for sure. Let me know and thanks!

huangapple
  • 本文由 发表于 2023年6月18日 19:32:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/76500318.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定