在 golang 中使用私有地图、切片的最佳做法是什么?
What is the best practice for using private Maps, Slices in golang?
我希望在更新地图时收到通知,以便我可以重新计算总计。我的第一个想法是将地图保密,并公开一个添加方法。这可行,但随后我需要能够读取和迭代地图(基本上,只读或地图的副本)。我发现地图的副本已发送,但底层数组或数据是相同的,并且实际上会被使用 "getter".
的任何人更新
type Account struct{
Name string
total Money
mailbox map[string]Money // I want to make this private but it seems impossible to give read only access - and a public Add method
}
func (a *Account) GetMailbox() map[string]Money{ //people should be able to view this map, but I need to be notified when they edit it.
return a.mailbox
}
func (a *Account) UpdateEnvelope(s string, m Money){
a.mailbox[s] = m
a.updateTotal()
}...
在 Go 中有推荐的方法吗?
然后你可以拥有私人数据和一个 public 迭代器,在每次调用时 returns 下一个 key/value 对的副本,不是吗?
与没有与特定数据结构结合的内置结构的语言相比,迭代器在 Go 中的出现较少。尽管如此,它们与其他任何地方一样易于制作,而且几乎同样易于使用,除了没有语言语法来确定迭代器的范围之外。例如,bufio.Scanner
只是一个迭代器,在...
上嫁接了一些方便的东西
您可以使用闭包轻松公开对私有数据的迭代。
如果你想禁用修改,只要不传map参数,只传keys和values,或者只传keys或只传values,随便你需要。
package main
import "fmt"
type M map[string]int
type S struct {
m M
}
func (s *S) Foreach(fn func(M, string, int)) {
for k, v := range s.m {
fn(s.m, k, v)
}
}
func main() {
s := S{m: make(M)}
s.m["xxx"] = 12
s.m["yyy"] = 254
s.m["zzz"] = 138
s.Foreach(func(m M, k string, v int) {
fmt.Println(k, v)
m[k] = v + 1
})
s.Foreach(func(m M, k string, v int) {
fmt.Println(k, v)
m[k] = v + 1
})
}
return 地图的克隆(不是地图值而是所有内容)可能更好。切片也是如此。
请注意,映射和切片是描述符。如果您 return 一个映射值,它将引用相同的底层数据结构。有关详细信息,请参阅博客 post Go maps in action。
创建新地图,复制元素并return新地图。那你就不用担心谁修改了。
制作地图的克隆:
func Clone(m map[string]Money) map[string]Money {
m2 := make(map[string]Money, len(m))
for k, v := range m {
m2[k] = v
}
return m2
}
正在测试 Clone()
函数(在 Go Playground 上尝试):
m := map[string]Money{"one": 1, "two": 2}
m2 := Clone(m)
m2["one"] = 11
m2["three"] = 3
fmt.Println(m) // Prints "map[one:1 two:2]", not effected by changes to m2
因此您的 GetMailbox()
方法:
func (a Account) GetMailbox() map[string]Money{
return Clone(a.mailbox)
}
其中一种 "recommended" 方法是 记录 邮箱用户不得修改地图(甚至可能导出地图)。
其他语言像 "If I just make most things private and const than users of my code won't be able to miss-use my code and all is fine" 一样提倡编码。我一直理解 Go 哲学更像是 "If a user of my code doesn't read my documentation or is willingly not sticking to it his code will break anyway, even if I encapsulate everything."
例如在 Go 中(如在 C 中,Java,等等)没有办法表明某些代码对于并发使用是(或不是)安全的:这些东西只是文档。从技术上讲,没有什么可以阻止用户并发调用不安全的并发使用方法或函数 除了 文档。
您会在标准库中找到这种类型的多个实例 "Users must not copy/modify/whatever this field at all/after x/etc."。
我希望在更新地图时收到通知,以便我可以重新计算总计。我的第一个想法是将地图保密,并公开一个添加方法。这可行,但随后我需要能够读取和迭代地图(基本上,只读或地图的副本)。我发现地图的副本已发送,但底层数组或数据是相同的,并且实际上会被使用 "getter".
的任何人更新type Account struct{
Name string
total Money
mailbox map[string]Money // I want to make this private but it seems impossible to give read only access - and a public Add method
}
func (a *Account) GetMailbox() map[string]Money{ //people should be able to view this map, but I need to be notified when they edit it.
return a.mailbox
}
func (a *Account) UpdateEnvelope(s string, m Money){
a.mailbox[s] = m
a.updateTotal()
}...
在 Go 中有推荐的方法吗?
然后你可以拥有私人数据和一个 public 迭代器,在每次调用时 returns 下一个 key/value 对的副本,不是吗?
与没有与特定数据结构结合的内置结构的语言相比,迭代器在 Go 中的出现较少。尽管如此,它们与其他任何地方一样易于制作,而且几乎同样易于使用,除了没有语言语法来确定迭代器的范围之外。例如,bufio.Scanner
只是一个迭代器,在...
您可以使用闭包轻松公开对私有数据的迭代。 如果你想禁用修改,只要不传map参数,只传keys和values,或者只传keys或只传values,随便你需要。
package main
import "fmt"
type M map[string]int
type S struct {
m M
}
func (s *S) Foreach(fn func(M, string, int)) {
for k, v := range s.m {
fn(s.m, k, v)
}
}
func main() {
s := S{m: make(M)}
s.m["xxx"] = 12
s.m["yyy"] = 254
s.m["zzz"] = 138
s.Foreach(func(m M, k string, v int) {
fmt.Println(k, v)
m[k] = v + 1
})
s.Foreach(func(m M, k string, v int) {
fmt.Println(k, v)
m[k] = v + 1
})
}
return 地图的克隆(不是地图值而是所有内容)可能更好。切片也是如此。
请注意,映射和切片是描述符。如果您 return 一个映射值,它将引用相同的底层数据结构。有关详细信息,请参阅博客 post Go maps in action。
创建新地图,复制元素并return新地图。那你就不用担心谁修改了。
制作地图的克隆:
func Clone(m map[string]Money) map[string]Money {
m2 := make(map[string]Money, len(m))
for k, v := range m {
m2[k] = v
}
return m2
}
正在测试 Clone()
函数(在 Go Playground 上尝试):
m := map[string]Money{"one": 1, "two": 2}
m2 := Clone(m)
m2["one"] = 11
m2["three"] = 3
fmt.Println(m) // Prints "map[one:1 two:2]", not effected by changes to m2
因此您的 GetMailbox()
方法:
func (a Account) GetMailbox() map[string]Money{
return Clone(a.mailbox)
}
其中一种 "recommended" 方法是 记录 邮箱用户不得修改地图(甚至可能导出地图)。
其他语言像 "If I just make most things private and const than users of my code won't be able to miss-use my code and all is fine" 一样提倡编码。我一直理解 Go 哲学更像是 "If a user of my code doesn't read my documentation or is willingly not sticking to it his code will break anyway, even if I encapsulate everything."
例如在 Go 中(如在 C 中,Java,等等)没有办法表明某些代码对于并发使用是(或不是)安全的:这些东西只是文档。从技术上讲,没有什么可以阻止用户并发调用不安全的并发使用方法或函数 除了 文档。
您会在标准库中找到这种类型的多个实例 "Users must not copy/modify/whatever this field at all/after x/etc."。