【问题标题】:How to create a method of a structure in golang that is called automatically?如何在golang中创建自动调用的结构方法?
【发布时间】:2013-05-29 16:10:25
【问题描述】:

我必须在 golang 中创建一个类似于 2 级继承的替代品,即, 在一个包中,我有一个结构(A),它被另一个包中的另一个结构(B)继承(嵌入为匿名字段),其对象是被“ma​​in”包使用。

现在,我为“B”(BPlease)创建了一个初始化方法,该方法返回 B 的对象(例如,B_obj)。我可以在程序开始时从我的“主”包中调用这个初始化程序(BPlease)。

“B”的方法之一(比如,HelloB()),在执行过程中调用了“A”的方法(比如,HelloA()),使用“B's”对象。

但我真正想要的是,类似于“A”的构造函数,它可以在“B”调用“A”的任何方法之前初始化其字段(最好是在“main”包中创建 B_obj 时)。

如何做到这一点?

我也尝试为“A”创建一个初始化程序(APlease)并调用它(BPlease)来获取“A”的对象(A_obj)。但是我发现这个对象没用,因为我不能利用它在“B”方法(HelloB())中调用“A”方法(HelloA())。 如果有人能告诉我如何使用这个对象(A_obj),那就太好了。

这里有一些代码可以澄清我的查询:

    package A
    type A struct { whatever }
    func (A_obj *A) HelloA(){performs some operation...}           // a method of A()
    func APlease() A {
          return A{initialize A fields}
        }
-------------------------------------------------------------------------------
    package B
    type B struct {
      A
      B fields
    }

    func BPlease() B {
      return B{
      A_obj := APlease()                     // useless to me.... how to utilise this?
      initialize B fields}
    }

    func (B_obj *B) HelloB(){                            // a method of B{}
      call B_obj.HelloA()                         // valid as A is an anon field in B struct
      some other operations                       // but A's fields are not initialized for B_obj
...}         

---------------------------------------------------
package main

import "B"
import "A"

func main(){
  B_obj := B.BPlease()         // what I want is, calling this should initialize A's fields for B_obj as well so that when HelloB() calls B_obj.HelloA(), it utilises A's field that have been initialized.
}

由于字段很多,我无法将所有字段值作为参数传递给 B_obj,而且有些字段值是通过调用相同结构的方法生成的。

【问题讨论】:

  • 我认为一些代码可以更容易地说明您想要什么以及为什么需要它。
  • @nos 感谢您的建议,我已经编辑了问题。

标签: oop constructor initialization go structure


【解决方案1】:

如果这是您正在寻找的,有一些方法可以在 Go 中模拟继承,请参阅blog 中的“继承”部分

【讨论】:

    【解决方案2】:

    为了扩展沃尔克的回答,你应该可以打电话

    func BPlease() B {
      a_obj := A.APlease() // initialize the fields of A like normal
      b_obj := B{} // create a B, whose anonymous fields are not initialized yet
    
      b_obj.A = a_obj // PERHAPS WHAT YOU WANT: copy all a's fields to b's fields.
                      // if you are relying sending a_obj's address somewhere in 
                      // APlease(), you may be out of luck.
    
      b_obj.field_unique_to_B = "initialized"
      return b_obj
    }
    

    现在您可以使用由 APlease() 初始化的字段创建 B 对象,您可以在 B 的对象上调用 A 的方法,甚至可以像这样在 B 中调用 A 的方法:

    func (B_obj *B) HelloB(){
      // can't call B_obj.HelloA() like you would expect
      B_obj.A.HelloA() // this works. Go "promotes" the anonymous field's methods 
                       // and fields to B_obj
                       // but they don't appear in B_obj, they appear in B_obj.A 
      fmt.Printf("And hello from B too; %s", B_obj.field_unique_to_B)
    }
    

    我将在此处附和 Rick-777 并建议您坚持 go 的命名约定和习语; NewReader 比 ReaderPlease 更容易阅读和理解。

    我设计了一个示例,如果人们愿意,我可以将它放在 bitbucket 上。我认为当你使用真正的隐喻时,它更容易阅读。也是免责声明 - 这不是最好的代码,但它可以回答您的问题。

    文件:car/car.go

    package car
    
    import "fmt"
    
    type BaseCar struct {
        Doors  int // by default, 4 doors. SportsCar will have 2 doors
        Wheels int
    }
    
    func NewBaseCar() BaseCar {
        return BaseCar{Wheels: 4, Doors: 4}
    }
    
    // this will be used later to show that a "subclass" can call methods from self.BaseCar
    func (c *BaseCar) String() string {
        return fmt.Sprintf("BaseCar: %d doors, %d wheels", c.Doors, c.Wheels)
    }
    
    // this will be promoted and not redefined
    func (c *BaseCar) CountDoors() int {
        return c.Doors
    }
    

    文件 sportscar/sportscar.go

    package sportscar
    
    // You can think of SportsCar as a subclass of BaseCar. But go does
    // not have conventional inheritence, and you can paint yourself into
    // a corner if you try to force square c++ structures into round go holes.
    
    import ( "../car" ; "fmt" )
    
    type SportsCar struct {
        car.BaseCar // here is the anonymous field
        isTopDown   bool
    }
    
    func NewSportsCar() SportsCar {
        conv := SportsCar{} // conv.Wheels == 0
    
        conv.BaseCar = car.NewBaseCar() // now conv.Wheels == conv.Doors == 4
    
        conv.isTopDown = false // SportsCar-only field
        conv.Doors = 2         // Fewer Doors than BaseCar
        return conv
    }
    
    // SportsCar only method
    func (s *SportsCar) ToggleTop() {
        s.isTopDown = !s.isTopDown
    }
    
    // "overloaded" string method note that to access the "base" String() method, 
    // you need to do so through the anonymous field: s.BaseCar.String()
    func (s *SportsCar) String() string {
        return fmt.Sprintf("Sports%s, topdown: %t", s.BaseCar.String(), s.isTopDown)
    }
    

    文件 main.go

    package main
    
    import ( "./car" ; "./sportscar" ; "fmt")
    
    type Stringer interface { // added this complication to show
        String() string // that each car calls its own String() method
    }
    
    func main() {
        boring := car.NewBaseCar()
        fancy := sportscar.NewSportsCar()
    
        fmt.Printf("      %s\n", Stringer(&boring))
        fmt.Printf("%s\n", Stringer(&fancy))
        fancy.ToggleTop()
        fmt.Printf("%s\n", Stringer(&fancy))
    
        fmt.Println("BaseCar.CountDoors() method is callable from a SportsCar:", fancy.CountDoors())
    }
    

    【讨论】:

      【解决方案3】:

      一些元注释:“第一个结构”和“第二个结构”让人很难理解哪个是哪个。标记 A、B 和 C 等不同事物是使数学如此强大的工具。

      这是你的问题吗: 您有两种类型 A 和 B,B 嵌入 A。您要确保 B 在 A 也已初始化的意义上“完全初始化”。

      原始草图:

      type A struct { whatever }
      type B struct {
        A
        more stuff
      }
      
      func APlease(params for an A) A {
        return A{fields set up from params}
      }
      
      func BPlease(params forn an A and for an B) B {
        return B{
          A: APlease(stuff for A),
          more: set from params for B,
        }
      }
      

      应该这样做:您可以通过调用 BPlease 来请求正确设置 B,并为嵌入式 A 和 B 的其余部分提供必要的参数。

      【讨论】:

      • 构造函数有一个命名约定,在面向对象的意义上,它们并不是完全的“构造函数”,但它们是相似的。 APlease 通常称为NewABPlease 将称为NewB。在标准的 Go API 中有很多这种约定的例子(其中一些简洁地使用New(<params>)。另一个例子是golang.org/doc/effective_go.html#composite_literals
      • @Volker 感谢您的回答和建议。我已经编辑了这个问题。虽然将值作为参数传递可以实现这一点,但在我的情况下它是不切实际的。我有很多字段,很多都是通过调用相同结构的方法来初始化的。
      • 我明白了。你想做一些不可能的事情。可以这样想:B到底是如何构造它的A的? B 必须“知道”如何构造 A。如何构造?要么传递参数(你说你不能,我不相信),要么让零 A 有用(比较 bytes.Buffer 或 sync.Mutex),或者有一些全局状态来构建 A。 Jnml 是对的:Go 中没有黑魔法。
      • 叹息好吧!我会尝试争论的事情。我稍后会在这里发布我决定的设计。感谢您的帮助。
      【解决方案4】:

      不管有没有人认为该语言没有继承权:不,没有魔法方法,比如“getter”或“setter”之类的。远程相关(魔力)可能是终结器,但在这种情况下它们肯定不会有帮助。

      但是,让我建议停止在 Go 中编写语言 X。只需使用围棋。 Go 不使用“类类”继承,Go 程序员(通常)也不应该使用。把 Go 想象成一个现代化的 C。没有多少 C 代码依赖于继承。 (好吧,我知道 GObject ;-)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多