gpt4 book ai didi

inheritance - 使用设置保护值的 Golang 继承

转载 作者:IT王子 更新时间:2023-10-29 01:16:31 26 4
gpt4 key购买 nike

我试图更好地理解如何在 Go 中使用 protected 空间。我来自 java,这意味着我可以通过 protected 继承访问值,因为这里只有组合,我想确保我走在正确的道路上。

问题:我想在子实现中设置一个值,但不在通用接口(interface)上公开一个 setter 。

当确实没有层次结构时,为“子类”提供 setter 的最佳方法是什么?

这意味着我想要:

type Bottom interface {
GetYouSome()

// rote things
SetSpeed(int)
DeliveryMechanism() chan string
}

请注意,没有 SetDeliveryMechanism(chan string) 方法。

我想我会从一个实现死记硬背的基础开始,这意味着实际的实现将提供“GetYouSome”方法。我也希望它们存在于不同的包中。将有许多实现,我希望命名空间被沙盒化(即它们都可以使用常量 DefaultPort)。

为了说明这个问题,我做了一个小项目。它的布局如下:

.
└── src
├── main.go
└── parent
├── child
│   └── child.go
└── parent.go

child.go 中,我们实际上创建了一些类型的 Bottom,但在 parent.go 中,我们实际上定义了样板代码( setter / getter )。问题是我无法在任何地方实例化 channel !

理想的实现应该是这样的:

//------------- parent.go -------------
package parent

type Bottom interface {
GetYouSome()

// rote things
SetSpeed(int)
DeliveryMechanism() chan string
}

// Intended to implement the boring things
type GenericBottom struct {
speed int
deliveryChan chan string
}

func (bot *GenericBottom) SetSpeed(speed int) {
bot.speed = speed
}

func (bot GenericBottom) DeliveryMechanism() chan string {
return bot.deliveryChan
}

//------------- child.go -------------
package child

import "parent"

func New(speed int) parent.Bottom {
impl := new(Composite)
impl.name = "simple"
impl.SetSpeed(speed)

// illegal! not exported
// impl.deliveryChannel = make(chan string)
return impl
}
// intended so that we can seamlessly treat the Composite
// as a Bottom
type Composite struct {
parent.GenericBottom
name string
}

func (a Composite) GetYouSome() {
fmt.Println("Inside the actual implementation")
}

我认为有两种方法可以解决这个问题。

(1) 创建一个子类来包装 GenericBottom 类,传递所有方法。真令人难过,它仍然存在我无法直接访问 deliveryChannel 类的问题。我必须在父包中构建一个 new 构造函数,然后在子类中显式设置实例。

//------------- parent.go -------------
func NewGenericBottom() GenericBottom {
return GenericBottom{0, make(chan string)}
}

//------------- child.go -------------
func New(speed int) parent.Bottom {
impl := new(ExplicitComposite)
impl.name = "explicit"

// now I can set the channel? Nope
// impl.gb = parent.GenericBottom{speed, make(chan string)}

impl.gb = parent.NewGenericBottom()
impl.SetSpeed(speed)
return impl
}
// this has to pass through each method
type ExplicitComposite struct {
gb parent.GenericBottom
name string
}

func (e ExplicitComposite) GetYouSome() {
fmt.Println("Inside the explicit implementation")
}

func (e ExplicitComposite) DeliveryMechanism() chan string {
return e.gb.DeliveryMechanism()
}

func (e *ExplicitComposite) SetSpeed(speed int) {
e.gb.SetSpeed(speed)
}

或者! (2) 我可以在 GenericBottom 上添加一个 setter 方法。但是任何使用 GenericBottom 的人都可以转换为访问权限吗?我不会真的受到“保护”。

像这样:

//------------- parent.go -------------

func (bot *GenericBottom) SetChannel(c chan string) {
bot.deliveryChan = c
}

//------------- child.go -------------

type CheatersBottom struct {
parent.GenericBottom
name string
}

func (a CheatersBottom) GetYouSome() {
fmt.Println("Inside the cheaters bottom")
}

func NewCheatersBottom(speed int) parent.Bottom {
impl := new(CheatersBottom)
impl.SetChannel(make(chan string))
impl.SetSpeed(speed)
return impl
}

当确实没有层次结构时,为“子类”提供 setter 的最佳方法是什么?

最佳答案

您的主要问题是您是一名 Java 程序员。我的意思不是刻薄或中伤 Java 程序员;我的意思是,Java 中的通用思维方式与您在 Go 中的设计思维方式完全不同。也许不如尝试用 C 编写 Haskell 代码那么多,但它们仍然是完全不同的思维方式。

我已经写了很多草稿试图“修复”你的代码,但你设计它的方式与没有真正概念继承的语言根本不一致。如果你发现自己曾经在 Go 中使用过“base”或“parent”这些词,甚至在某种程度上使用过“generic”这个词,你可能会与类型系统大打出手。我认为这是大多数从 OO 语言转向 Go 的人必须经历的阶段。

我建议查看 Go 标准库并查看它们如何布置包。通常,您会发现以下内容: 一个包将定义一个或多个接口(interface),以及在这些接口(interface)上运行的函数。然而,接口(interface)的实际具体实现的数量要么不存在,要么非常少。然后,在另一个包中,他们提供实用程序的具体实现。最引人注目的是,与您的代码相比,永远不会有任何部分实现,除非实现一个接口(interface)自动部分实现另一个接口(interface)。

您的 GenericBottom 不一定是“错误”的,我当然自己制作了隐藏的伪抽象类,但在 Go 中,您想要的是将它们不导出为 genericBottom,并且在与所有具体实现相同的包中。那或在internal package从 Go 1.4 开始。

您现在可能会说,“但是如果我需要在别处定义我的接口(interface)的其他具体实现怎么办?”好吧,您将复制代码。这可以被视为(并且可能是)Go 类型系统的弱点,但在不同的包中实现接口(interface)时重复一些代码绝对是惯用的和常见的。这就是像 internal 这样的东西是为了减轻一些。

另外请注意,不要将包视为类。一开始我也有这个问题。大中型的包里面有10个以上的文件是比较不起眼的。只要它们是一个单一的概念单元,那就是它们的用途。这就是通过让类型共享一个包来在 Go 中实现“ protected ”功能的方式(实际上更像是默认/无修饰符)。他们都可以访问彼此未导出的字段。

最后一条建议是不要从 NewX 方法返回接口(interface)。你应该(几乎)总是返回一个指向结构的指针。通常你想要构造一个类型,并将它作为接口(interface)传递给另一个方法,而不是首先接收接口(interface)。

我建议完全重写。挑战自己;每次你想到“ child 类”这样的词时,停下来做点别的事情。看看你能想出什么。我认为您会发现,虽然一开始有点困难,但通过以与类型系统协作的方式编写代码,您最终会完成更多工作,即使这会导致一些代码卡顿。

关于inheritance - 使用设置保护值的 Golang 继承,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32533992/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com