dongmou3615
2019-09-18 13:22
浏览 92
已采纳

如何在golang中隐藏默认类型的构造函数?

The reason I have this question is that I often make mistakes that I forgot to specify a value to a struct's field, then the compiler is fine, but the zero value causes bugs.

Here is an example. Say I have a type Foo defined like this in a package:

package types

type Foo struct {
  RequiredField1 string
  RequiredField2 string
}

It is exposed and has 2 fields that I'd like both of them to be specified when the Foo struct is created.

Then I can import and use it in my main function like this:

package main

import (
  "github.com/foo/bar/types"
)

func main() {
  f := &Foo{ 
    RequiredField1: "fff",
  }
  fmt.Printf("f.RequiredField2: %v", f.RequiredField2)
}

This causes a bug because I forgot to specify RequiredField2 which is required. And I often make this kind of bugs :p

Now, in order to leverage the compile and prevent this mistake, I made a constructor function for Foo and asks for the required values.

func NewFoo(field1 string, field2 string) *Foo {
  return &Foo{Field1: field1, Field2: field2}
}

So that if I forgot to pass field2, the compiler won't compile my code:

func main() {
    f := NewFoo("foo")
    fmt.Printf("f.Field2: %v", f.Field2)
}

The build will fail:

./prog.go:18:13: not enough arguments in call to NewFoo
    have (string)
    want (string, string)

Now the question is: how to stop the foreign callers (callers from other namespaces) from calling the default constructor of Foo, that is &Foo, and force them to use the NewFoo?

If I can, then I'm safe since NewFoo is the only way to create the Foo type and the compiler helps me ensure all fields are present when calling it.

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 邀请回答

3条回答 默认 最新

  • douji2520 2019-09-19 13:27
    已采纳

    You can avoid this problem by making Foo type unexported.

    You can make an interface that is Exported and has all the methods that Foo type performs.

    Example:-

    type Foo interface{
    DoSomething()
    }
    
    type foo struct{
    RequiredField1 string
    RequiredField2 string
    }
    
    func NewFoo(requiredField1 string,requiredField2 string)*foo{
    return &foo{
    RequiredField1:requiredField1,
    RequiredField2:requiredField2,
    }
    }
    
    func (f *foo)DoSomething(){
    // your implementation
    }
    

    In this way all the caller will be able to access it but cannot create foo without NewFoo function.

    点赞 打赏 评论
  • drkrsx3135168 2019-09-18 13:24

    How to hide the default type constructor in golang?

    You cannot for an exported type.

    Get used to it, it is not a problem in real life.

    (For unexported types, just provide a func NewUnexportedType(...) unexportedType. )

    点赞 打赏 评论
  • duancai9010 2019-09-18 14:48

    Effective Go

    it's helpful to arrange when designing your data structures that the zero value of each type can be used without further initialization.


    we often need to change the type definition, like adding more fields

    That is why you should make the zero value for struct fields significant. The behavior is by design. For example, it is used to maintain the Go 1 compatibility guarantee.

    点赞 打赏 评论

相关推荐 更多相似问题