在Go语言中使用自定义类型的最佳实践是什么?如何使用别名构建类型?

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

Best practices to use own type in golang. Alians to build type

问题

我想知道在Go语言中何时或是否应该使用自定义类型。当这样做可以更容易理解我的代码时,或者当我不应该使用自定义类型时。

示例:
我想创建一个带有MAC主机名map类型:

第一种方式是最简单的方式:

var machines map[string]string{
   "11:22...": "myHost",
   "22:33..":  "yourHost",
}

第二种方式:

type MAC string
type HOST string

machines := map[MAC]HOST{
   MAC("11:22.."): HOST("myHost"),
   MAC("22:33.."): HOST("yourHost"),
}

在上面的示例中,我可以通过编写方法来对我的类型MACHOST进行额外的控制,例如验证、比较等,这样会更好吗?

第三种方式:

type MACHINES map[string]string

m := MACHINES{}
m = map[string]string{
   "11:22..": "myHost",
   "22:33":   "yourHost",
}

对我来说,上面的示例更难理解,对其他人来说也不够直观。我认为上面的示例也应该涉及到HOSTMAC,因为类型MACHINE并没有告诉开发者应该如何实现,所以我希望可以这样:

type MACHINES map[MAC]HOST

然而,请给出关于在Go语言中使用自定义类型的更好理解的注释。

英文:

I wonder when or if it should be use own type in golang. When this will be more friendly to understand my code, or when I shouldn't use own type.

Example:
I want to create map type with MAC, and name host:

in first way the simplest I can do that

var machines map[string]string{
   "11:22...": "myHost",
   "22:33..":  "yourHost",
}

in second way

type MAC string
type HOST string

  machines := map[MAC]HOST{
       MAC("11:22..") : HOST("myHost"),
       MAC("22:33..") : HOST("yourHost"),
    }

In above exmaple I can get additional controle on my type MAC, HOST trought write method to validation, compare etc it is better ?

Third way

type MACHINES map[string]string

m := MACHINES{}
	m = map[string]string{
	 "11:22.." : "myHost",
	 "22:33" : "yourHost",
	}

above example for me is worst to understand less intuitive to some else. I think that above example should be also filled about HOST, and MAC because type MACHINE nothing to say developer how this should be implement so I would like

  type MACHINES map[MAC]HOST

However, please about comment to better understand about usage own type in golang.

答案1

得分: 5

不评论你具体的例子,一般来说有几个原因你会想要使用一个新的类型:

  • 你需要在该类型上定义方法。
  • 你不希望该类型可以与字面量或者与其派生类型的变量进行比较(例如,为了减少用户的困惑或确保他们不会做一些无效的操作,比如尝试将你的特殊字符串与其他随机字符串进行比较)。
  • 你只是需要一个地方来放置文档,或者将返回特定类型的方法进行分组(例如,如果你有几个返回net.Conn的Dial方法,你可以创建一个type Conn net.Conn,并返回它,仅仅是为了将这些函数在Conn类型的头部在godoc中进行分组,或者为返回的net.Conn提供一般性的文档)。
  • 因为你希望人们能够检查某个泛型类型是否来自你的包(例如,即使你的Conn只是一个普通的net.Conn,它也给了你类型切换和检查它是否为yourpackage.Conn的选项)。
  • 你希望一个函数接受预定义列表中的参数,并且不希望用户能够创建可以传递进去的新值(例如,一个未导出类型的导出常量列表)。
英文:

Without commenting on your specific example, there are a few reasons you'd generally want to use a new type:

  • You need to define methods on the type
  • You don't want the type to be comparable with literals or variables with the type it's derived from (eg. to reduce user confusion or make sure they don't do something invalid like attempt to compare your special string with some other random string)
  • You just need a place to put documentation, or to group methods that return a specific type (eg. if you have several Dial methods that return a net.Conn, you might create a type Conn net.Conn and return that instead just for the sake of grouping the functions under the Conn type header in godoc or to provide general documentation for the net.Conn returned by the methods).
  • Because you want people to be able to check if something of a generic type came from your package or not (eg. even if your Conn is just a normal net.Conn, it gives you the option of type switching and checking if it's a yourpackage.Conn as well)
  • You want a function to take an argument from a predefined list of things and you don't want the user to be able to make new values that can be passed in (eg. a list of exported constants of an unexported type)

答案2

得分: 1

创建类型别名只在需要添加额外方法(如验证函数)或者想要记录某个值的预期用法(例如,net.IP 类型)时才有用。

类型别名可以帮助你避免 API 误解,但如果你使用的是常量值,则无法起到作用。例如,下面的代码是有效的:

type Host string
type Mac string
hosts := map[Mac]Host{"ad:cb..": "localhost"}

关于 Go 中常量的工作原理,你可以查看 Rob Pike 的博客文章

英文:

Creating type alias is only useful when you require to add extra methods (such as validation functions) or when you want to document the desired use of some value (for example, the net.IP type).

Type alias could help you to prevent API misunderstanding, but won't if you're using constant values. For example, this code is valid:

type Host string
type Mac string
hosts := map[Mac]Host{"ad:cb..": "localhost"}

For further information about how constants work in Go, you can check the Rob Pike's blog post

答案3

得分: 0

Go语言中最重要的特性之一是接口(interfaces)。通过定义接口的方法,你可以实现接口,而实现接口的唯一方式是在类型中添加方法。例如,假设你想要实现fmt包中的Stringer接口。

在你的例子中,type MACHINES map[string]string,你需要在你的类型中添加一个名为String的方法。

func (m MACHINES) String() string {
    s := "MACHINES\n"
    for k, v := range m {
        s += fmt.Sprintf("%v: %v\n", k, v)
    }
    return s
}

现在,任何接受Stringer接口的函数也可以接受你的MACHINES类型,因为你实现了Stringer接口。

例如,fmt.Printf函数会检查传入的类型是否实现了Stringer接口。

m := MACHINES{"foo":"bar", "baz": "bust"}
fmt.Printf("%s", m)

将调用MACHINESString方法。

你可以在playground中查看示例。

英文:

One of the most important features of Go is interfaces. In Go by defining the method/s of an Interface you implement the interface, and the only way to implement an interface is by adding a Method to your type. For instance, say you want to implement the Stringer interface from the fmt package.

In your example type MACHINES map[string]string you would add a method called String to your type.

func (m MACHINES) String() string {
    s := "MACHINES\n"
    for k, v := range m {
        s += fmt.Sprintf("%v: %v\n", k, v)
    }
    return s
}

Any other function that accepts the Stringer interfaces can now also accept your MACHINES type since you implemented the Stringer interface.

For example the fmt.Printf checks if the passed in type implements the Stringer interface

m := MACHINES{"foo":"bar", "baz": "bust"}
fmt.Printf("%s", m)

Will invoke MACHINES String method

Example from the playground

答案4

得分: 0

在我看来,你确实应该使用自己的类型。

举个例子,所有参数都是“string”或“int”的函数参数列表很容易混淆,而且很容易出错。

关于你的类型名称,我有一点建议。"MAC"是媒体访问控制的首字母缩写,所以应该保持全大写。但是"HOST"应该改为"Host",因为它只是一个单词。我不记得具体在哪里,但是Go语言有一种推荐的命名形式,即驼峰命名法,其中包括全大写的缩写,比如"GetIPv4AddressFromDNS"。

英文:

You do want to use your own types in my opinion.

As an example, lists of function arguments that are all "string" or all "int" are confusing and very easy to get wrong.

And a comment on your type names. "MAC" is an acronym for Media Access Control so it should stay as all caps. But "HOST" should be "Host" since it is just a word. I don't recall where it is exactly, but there's a recommended form for Go names which is CamelCase with all-capital abbreviations, like "GetIPv4AddressFromDNS"

答案5

得分: 0

我认为使用自定义类型的另一个原因是,如果你不确定某个东西的正确类型,比如uint8 / uint16,而且你希望以后能够轻松地在一个地方进行更改。

然而,这将导致在使用内置类型的方法时需要进行转换。或者你需要在自定义类型上定义这些方法。

英文:

I think another reason to use your own types, that hasn't been mentioned here, is if you are not sure exactly what the right type for something is e.g. uint8 / uint16 and you want to easily change it later all in one place.

This will however make conversions necessary whenever you want to use the built-in types' methods. Or you will need to define them on your own type.

huangapple
  • 本文由 发表于 2016年10月27日 01:08:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/40268276.html
匿名

发表评论

匿名网友

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

确定