Heim  >  Artikel  >  Backend-Entwicklung  >  Detaillierte Erklärung unveränderlicher Typen in Go

Detaillierte Erklärung unveränderlicher Typen in Go

Guanhui
Guanhuinach vorne
2020-06-15 18:01:564183Durchsuche

Detaillierte Erklärung unveränderlicher Typen in Go

Unveränderlichkeit in Golang

So nutzen Sie Unveränderlichkeit, um die Lesbarkeit und Stabilität Ihrer Golang-Anwendungen zu verbessern

Das Konzept der Unveränderlichkeit ist sehr einfach. Sobald ein Objekt (oder eine Struktur) erstellt wurde, kann es nie mehr geändert werden. Obwohl das Konzept einfach erscheint, ist es nicht so einfach, es zu nutzen.

Wie bei den meisten Dingen in der Informatik (und im Leben) gibt es viele Möglichkeiten, das gleiche Ergebnis zu erzielen, und in Bezug auf die Invarianz gibt es keinen Unterschied. Sie sollten es sich als ein Werkzeug im Werkzeugkasten vorstellen, auf dem es verwendet wird Anwendbare Problemszenarien. Ein sehr guter Anwendungsfall für Unveränderlichkeit ist, dass Golang unter Berücksichtigung der Parallelität entwickelt wurde. Daher gibt es bei der Verwendung von Parallelität sehr häufige Möglichkeiten um einige Unveränderlichkeitskonzepte in Golang zu verwenden, um Ihren Code lesbarer und stabiler zu machen.

Exportieren Sie nur die Funktionalität einer Struktur, ohne ihre Felder zu exportieren.

Dies ähnelt der Kapselung . Erstellen Sie eine Struktur mit nicht exportierten Feldern und exportieren Sie nur die Funktionen, die funktionieren. Diese Technik ist für Schnittstellen sehr nützlich. Eine weitere gute Ergänzung zu dieser Technik ist das Hinzufügen und Exportieren einer Kreation Funktion (oder Konstruktor) zu Ihrer Struktur hinzufügen. Auf diese Weise können Sie sicherstellen, dass der Status der Struktur immer gültig ist. Kann den Code zuverlässiger machen, da Sie sich nicht bei jeder Operation mit einem ungültigen Status befassen müssen Hier ist ein sehr einfaches Beispiel:

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
}

In diesem Paket definieren wir den Typ

mit nicht exportierten Feldern

, Konstruktor Amount und value Methoden für Sobald die NewAmount-Funktion die GetValue-Struktur erstellt, kann sie nicht mehr geändert werden. Daher ist sie extern unveränderlich (es gibt jedoch keine Möglichkeit, unveränderliche Strukturen in go 1 zu erstellen). Vorschläge, dies in go 2) zu ändern. Außerdem gibt es keine , da die einzige Möglichkeit, sie zu erstellen, überprüft wurde Wertkopien anstelle von Zeigern in FunktionenAmount NewAmountAmountDas grundlegendste Konzept besteht darin, ein Objekt (oder eine Struktur) niemals zu ändern, nachdem Sie es erstellt haben. Aber wir arbeiten oft an Anwendungen, bei denen der Entitätsstatus wichtig ist. Allerdings sind der Entitätsstatus und die interne Darstellung der Entität im Programm unterschiedlich. Wenn wir Unveränderlichkeit verwenden, können wir Entitäten immer noch mehrere Zustände zuweisen. Dies bedeutet, dass sich die erstellte Struktur nicht ändert, wohl aber ihre Kopie. Dies bedeutet nicht, dass wir die Funktion zum Kopieren jedes Felds in der Struktur manuell implementieren müssen. Amount

Stattdessen können wir uns auf das native Verhalten der Go-Sprache verlassen, Werte beim Aufrufen von Funktionen zu kopieren. Für jede Operation, die den Zustand einer Entität ändert, können wir eine Funktion erstellen, die eine Struktur als Parameter (oder als Funktionsempfänger) empfängt und nach der Ausführung die geänderte Version zurückgibt. Dies ist eine sehr leistungsstarke Technik, da Sie in der Kopie alles ändern können, ohne die vom Funktionsaufrufer als Argumente übergebenen Variablen zu ändern. Das bedeutet keine Nebenwirkungen und vorhersehbares Verhalten. Wenn dieselbe Struktur an gleichzeitige Funktionen übergeben wird, erhält jede Struktur eine Kopie davon und keinen Zeiger darauf.

Wenn Sie die Slicing-Funktion verwenden, werden Sie sehen, dass dieses Verhalten auf die Funktion angewendet wird.

Zurück zu unserem Beispiel: Lassen Sie uns den Typ

implementieren, der enthält

Feld vom Typ

. Gleichzeitig fügen wir die Methoden [append](https://golang.org/pkg/builtin/#append) und

hinzu, um den Zustand der Entität

zu ändern. Account

a, err := amounts.NewAmount(10)
*// 处理错误
*log.Println(a.GetValue())

Wenn Sie sich die von uns erstellten Methoden ansehen, sehen sie so aus, als würden wir tatsächlich den Zustand der Amount-Struktur ändern, die der Empfänger der Funktion ist. Da wir keine Zeiger verwenden, ist dies nicht der Fall, und da als Empfänger dieser Funktionen eine Kopie der Struktur übergeben wird, ändern wir die Kopie, die nur innerhalb des Funktionsbereichs gültig ist, und geben sie dann zurück. Hier ist ein Beispiel für den Aufruf in einem anderen Paket: balance
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
}
DepositDas Ergebnis auf der Befehlszeile wäre: Withdraw
a, err := amounts.NewAmount(10)
acc := accounts.NewEmptyAccount()
acc2 := acc.Deposit(a)
log.Println(acc.GetBalance())
log.Println(acc2.GetBalance())
AccountWie Sie sehen können, trotz Aufruf von 🎜>-Methode , aber die Variable wird nicht wirklich geändert, sie gibt eine neue Kopie von

zurück (zugewiesen an Account), die die geänderten Felder enthält.

使用指针具有优于复制值的优点,特别是如果您的结构很大时,在复制时可能会导致性能问题,但是您应始终问自己是否值得,不要尝试过早地优化代码。尤其是在使用并发时。您可能会在一些糟糕的情况下结束。

减少全局或外部状态中的依赖性

不变性不仅可以应用于结构,还可以应用于函数。如果我们用相同的参数两次执行相同的函数,我们应该收到相同的结果,对吗?好吧,如果我们依赖于外部状态或全局变量,则可能并非总是如此。最好避免这种情况。有几种方法可以实现这一目标。

如果您在函数内部使用共享的全局变量,请考虑将该值作为参数传递,而不是直接在函数内部使用。 那会使您的函数更可预测,也更易于测试。整个代码的可读性也会更容易,其他人也将会了解到值可能会影响函数行为,因为它是一个参数,而这就是参数的用途。 这里有一个例子:

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教程

Das obige ist der detaillierte Inhalt vonDetaillierte Erklärung unveränderlicher Typen in Go. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:learnku.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen