Maison >développement back-end >Golang >Explication détaillée des types immuables dans Go
Comment tirer parti de l'immuabilité pour améliorer la lisibilité et la stabilité de vos applications Golang
Le concept d'immuabilité est très simple. Une fois qu'un objet (ou une structure) est créé, il ne peut jamais être modifié. Bien que le concept semble simple, l'utiliser ou en bénéficier n'est pas si facile.
Comme pour la plupart des choses en informatique (et dans la vie), il existe de nombreuses façons d'obtenir le même résultat, et en termes d'invariance, il n'y a pas de différence. Vous devriez y penser comme un outil dans la boîte à outils et utilisé. scénarios de problèmes applicables. Un très bon cas d'utilisation de l'immuabilité est lorsque vous effectuez une programmation simultanée. Golang a été conçu en pensant à la concurrence, donc utiliser la concurrence dans go est très courant
Quel que soit le paradigme que vous utilisez, il existe des moyens. pour utiliser certains concepts d'immuabilité dans Golang pour rendre votre code plus lisible et stable
Ceci est similaire à l'encapsulation. . Créez une structure avec des champs non exportés et exportez uniquement les fonctions qui agissent. Puisque vous n'êtes intéressé que par le comportement de ces structures, cette technique est très utile pour les interfaces. Un autre bon ajout à cette technique est d'ajouter et d'exporter une création. fonction (ou constructeur) à votre structure. De cette façon, vous pouvez vous assurer que l'état de la structure est toujours valide. Peut rendre le code plus fiable car vous n'avez pas à gérer un état invalide pour chaque opération. que vous voulez faire avec la structure. Voici un exemple très basique :
package amounts import "errors" type Amount struct { value int } func NewAmount(value int) (Amount, error) { if value < 0 { return Amount{}, errors.New("Invalid amount") } return Amount{value: value}, nil } func (a Amount) GetValue() int { return a.value }
Dans ce package, nous définissons le Amount
type, avec les champs non exportés value
, le constructeur NewAmount
et les méthodes GetValue
pour le type Amount
. Une fois que la fonction NewAmount
crée la structure Amount
, elle ne peut pas être modifiée. Par conséquent, elle est supprimée du package et est immuable en externe (il n'y a aucun moyen de créer des structures immuables dans go 1, bien qu'il y en ait. suggestions pour changer cela dans go 2). De plus, il n'y a pas de Amount
a, err := amounts.NewAmount(10)
*// 处理错误
*log.Println(a.GetValue())
[append](https://golang.org/pkg/builtin/#append)
, qui contient Account
champ de type Amount
. En même temps, nous ajoutons les méthodes balance
et Deposit
pour changer l'état de l'entité Withdraw
. Account
package accounts import ( "errors" "my-package/amounts" ) type Account struct { balance amounts.Amount } func NewEmptyAccount() Account { amount, _ := amounts.NewAmount(0) return NewAccount(amount) } func NewAccount(amount amounts.Amount) Account { return Account{balance: amount} } func (acc Account) Deposit(amount amounts.Amount) Account { newAmount, _ := amounts.NewAmount(acc.balance.GetValue() + amount.GetValue()) acc.balance = newAmount return acc } func (acc Account) Withdraw(amount amounts.Amount) (Account, error) { newAmount, err := amounts.NewAmount(acc.balance.GetValue() - amount.GetValue()) if err != nil { return acc, errors.New("Insuficient funds") } acc.balance = newAmount return acc, nil }Si vous inspectez les méthodes que nous avons créées, elles semblent que nous modifions réellement l'état de la
structure qui est le récepteur de la fonction. Puisque nous n'utilisons pas de pointeurs, ce n'est pas le cas, et puisqu'une copie de la structure est passée en tant que récepteur de ces fonctions, nous modifierons la copie qui n'est valide que dans la portée de la fonction, puis la renverrons. Voici un exemple d'appel dans un autre package : Account
a, err := amounts.NewAmount(10) acc := accounts.NewEmptyAccount() acc2 := acc.Deposit(a) log.Println(acc.GetBalance()) log.Println(acc2.GetBalance())Le résultat sur la ligne de commande serait :
2020/06/03 22:22:40 {0} 2020/06/03 22:22:40 {10}Comme vous pouvez le voir, même si
a été appelé via la variable acc
🎜>, mais la variable n'est pas réellement modifiée, elle renvoie une nouvelle copie de Deposit
(attribuée à Account
), qui contient les champs modifiés. acc2
使用指针具有优于复制值的优点,特别是如果您的结构很大时,在复制时可能会导致性能问题,但是您应始终问自己是否值得,不要尝试过早地优化代码。尤其是在使用并发时。您可能会在一些糟糕的情况下结束。
不变性不仅可以应用于结构,还可以应用于函数。如果我们用相同的参数两次执行相同的函数,我们应该收到相同的结果,对吗?好吧,如果我们依赖于外部状态或全局变量,则可能并非总是如此。最好避免这种情况。有几种方法可以实现这一目标。
如果您在函数内部使用共享的全局变量,请考虑将该值作为参数传递,而不是直接在函数内部使用。 那会使您的函数更可预测,也更易于测试。整个代码的可读性也会更容易,其他人也将会了解到值可能会影响函数行为,因为它是一个参数,而这就是参数的用途。 这里有一个例子:
package main import ( "fmt" "time" ) var rand int = 0 func main() { rand = time.Now().Second() + 1 fmt.Println(sum(1, 2)) } func sum(a, b int) int { return a + b + rand }
这个函数 sum
使用全局变量作为自己计算的一部分。 从函数签名来看这不是很清楚。 更好的方法是将rand变量作为参数传递。 因此该函数看起来应该像这样:
func sum(a, b, rand **int**) **int** { return a + b + rand }
推荐教程:《Go教程》
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!