使用设置受保护值的Golang继承

ryb*_*bit 2 inheritance go

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

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

当真正没有层次结构时,为"子类"提供setter的最佳方法是什么?

这意味着我想:

type Bottom interface {
    GetYouSome()

    // rote things
    SetSpeed(int)
    DeliveryMechanism() chan string
}
Run Code Online (Sandbox Code Playgroud)

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

我想我会从一个实现死记硬背的基础开始,这意味着实际的实现将提供'GetYouSome'方法.我也希望这些存在于不同的包中.将有几十个实现,我希望将命名空间用沙箱(即它们都可以使用const DefaultPort).

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

.
??? src
    ??? main.go
    ??? parent
        ??? child
        ?   ??? child.go
        ??? parent.go
Run Code Online (Sandbox Code Playgroud)

child.go我们在哪里实际创建了几种类型Bottom,但parent.go实际上我们定义了样板代码(setters/getters).问题是我不能在任何地方实例化频道!

理想情况下,实现看起来像这样:

//------------- 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")
}
Run Code Online (Sandbox Code Playgroud)

我可以通过两种方式来解决这个问题.

(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)
}
Run Code Online (Sandbox Code Playgroud)

要么!(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
}
Run Code Online (Sandbox Code Playgroud)

当真正没有层次结构时,为"子类"提供setter的最佳方法是什么?

Lin*_*ope 14

你的主要问题是你是一名Java程序员.我并不是说那是吝啬,或者是对Java程序员的诽谤; 我的意思是,Java中常见的思维方式与您在Go中的设计思维完全不同.也许没有尝试用C编写Haskell代码那么多,但它们仍然是完全不同的思维模式.

我写了很多试图"修复"你的代码的草稿,但是你设计它的方式基本上与没有真正的概念继承的语言不一致.如果你发现自己在Go中使用了"base"或"parent"这两个词,甚至在某种程度上使用了"generic"这个词,那么你可能会对类型系统进行一场精彩的战斗.我认为这是一个阶段,大多数来自OO语言的人都必须通过这种方式进行斗争.

我建议查看Go标准库并查看它们如何布置它们的包.通常,您将找到以下内容:一个包将定义一个或多个接口,以及在这些接口上运行的功能.但是,接口的实际具体实现的数量要么不存在要么非常小.然后,在另一个包中,它们提供实用的具体实现.最引人注目的是,与您的代码相比,除了实现一个接口自动部分实现另一个接口之外,永远不会有任何部分实现.

没有什么必然与你的"错误" GenericBottom,我当然自己制作了隐藏的伪抽象类,但在Go中你想要的是将它们作为未导出的genericBottom,并且与所有具体实现在同一个包中.那个或在Go 1.4 的内部包中.

您现在可能会说,"但是如果我需要在其他地方定义其他接口的具体实现呢?" 好吧,你将复制代码.这可以被视为(并且可能是)Go类型系统的弱点,但是当在不同的包中实现接口时,重复一些代码肯定是惯用且常见的.这就是为什么事情会internal有所缓解.

另外请注意,不要像处理类一样对待包.这是我一开始遇到的一个问题.对于中大型软件包来说,其中包含10个或更多文件是相对不起眼​​的.只要它们是一个单一的概念单元,那就是它们的用途.这就是Go中实现"受保护"功能的方式(实际上更像是默认/无修饰符),通过让类型共享包.他们都可以访问彼此未被移植的字段.

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

我会推荐几乎完全重写.挑战自己; 每当你想到像"儿童班"这样的词语停下来做其他事情.看看你能想出什么.我想你会发现虽然起初有点困难,但你最终会通过与类型系统合作编写代码来完成更多工作,即使它导致某些代码口吃.