Heim > Backend-Entwicklung > Golang > Einführung in die objektorientierte Programmierung (OOP) in Golang

Einführung in die objektorientierte Programmierung (OOP) in Golang

Linda Hamilton
Freigeben: 2024-12-23 12:43:11
Original
376 Leute haben es durchsucht

Wenn wir über Programmierung sprechen, meinen wir normalerweise das Schreiben einer Reihe von Funktionen, die einige Daten ändern und mit ihnen interagieren. Objektorientierte Programmierung (OOP) ist ein Programmiermodell, das sich stattdessen auf „Objekte“ konzentriert, die Daten enthalten und mit denen einige relevante Funktionen verknüpft sind. Die objektorientierte Programmierung besteht aus vier Säulen: Vererbung, Kapselung, Polymorphismus und Abstraktion. In diesem Blog werfen wir anhand von Beispielen einen Blick darauf, wie Sie jede davon in Golang implementieren können. Eine gewisse grundlegende Vorstellung von OOP wird empfohlen, aber wenn nicht, werde ich eine kurze Einführung in die Bedeutung aller vier Säulen geben.

Introduction to Object Oriented Programming (OOP) in Golang

Klassen, Objekte und Methoden

Die Kernidee der objektorientierten Programmierung lässt sich in diesen Stichpunkten zusammenfassen:

  • Sie definieren „Klassen“, bei denen es sich um eine Sammlung von Daten und Funktionen handelt, die Sie für diese Daten aufrufen können.
  • Diese spezifischen Funktionen werden „Methoden“ dieser bestimmten Klasse genannt.
  • Eine tatsächliche Instanz einer Klasse wird als „Objekt“ bezeichnet.

Schauen wir uns einen Code in Golang an, um diese drei Konzepte zu verstehen:

package main

import "fmt"

type Batman struct {
    actor string
    year int
}

func (b Batman) SayImBatman() {
    fmt.Printf("I'm %s and I'm Batman from year %d\n", b.actor, b.year)
}

func main() {
    b1 := Batman{actor: "Michael Keaton", year: 1989}
    b2 := Batman{actor: "Christian Bale", year: 2005}

    b1.SayImBatman()
    b2.SayImBatman()
}

Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

In Golang sind Klassen nichts anderes als von uns definierte Typen. Diese Typen müssen nicht unbedingt eine Struktur sein, sind es aber normalerweise, da wir in OOP mit einer Sammlung von Daten arbeiten, die von jedem Typ sein können (String, Int usw.).

Klassen sind Blaupausen für Objekte. Immer wenn Sie eine Klasse instanziieren, wird ein Objekt gebildet. In diesem Beispiel sind b1 und b2 Objekte der Batman-Klasse.

Die SayImBatman-Funktion kann für jedes Objekt der Klasse aufgerufen werden. Da sie an die Batman-Klasse gebunden ist, wird sie nicht als reguläre Funktion, sondern als Methode der Klasse bezeichnet.

Ich denke, dies sollte die Grundlagen von OOP so weit klären, dass Sie mit dem nächsten Abschnitt fortfahren können, in dem wir uns die vier Säulen von OOP ansehen.

Nachlass

Vererbung führt in die Konzepte der Klassen Eltern und Kind in OOP ein. Eine untergeordnete Klasse ist eine von einer übergeordneten Klasse abgeleitete Klasse und erbt alle ihre Methoden und Eigenschaften (Daten). Schauen wir uns einen Code an, der uns hilft, dies zu verstehen:

package main

import "fmt"

type Hero struct {
    team string
}

type Batman struct {
    Hero
    name string
}

type Ironman struct {
    Hero
    power int
}

func (h Hero) SayTeam() {
    fmt.Println("My Team is", h.team)
}

func (b Batman) SayImBatman() {
    fmt.Printf("I'm %s and I'm Batman\n", b.name)
}

func (i Ironman) SayPowerLevel() {
    fmt.Printf("I'm Ironman and my powerlevel is %d\n", i.power)
}

func main() {
    b1 := Batman{Hero{team: "Justice League"}, "Christian Bale"}
    i1 := Ironman{Hero{team: "Avengers"}, 23}

    b1.SayImBatman()
    b1.SayTeam()

    i1.SayPowerLevel()
    i1.SayTeam()
}

Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

In diesem Beispiel sind Batman und Ironman untergeordnete Klassen der übergeordneten Klasse Hero. Sie haben Zugriff auf die Eigenschaften ihrer übergeordneten Klasse, also Team, und deren Methoden, also SayTeam. Wie Sie bei der Deklaration der b1- und i1-Instanzen sehen können, geben wir die Eigenschaften der übergeordneten Klasse sowie deren spezifische Eigenschaften für die jeweiligen Klassen an. Beide können die SayTeam-Methode aufrufen, die in der übergeordneten Klasse definiert ist. Sie verfügen aber auch über separate Eigenschaften und Methoden, die für jeden von ihnen einzigartig sind.

Golang implementiert die Vererbung mithilfe der Komposition (unter Verwendung einer Struktur innerhalb einer Struktur). Es verfügt nicht über eine integrierte klassenbasierte Vererbung wie andere OOP-Sprachen wie C oder Java.

Verkapselung

Kapselung ist das Prinzip, die internen Eigenschaften eines Objekts zu verbergen und nicht zuzulassen, dass sie direkt geändert werden. Stattdessen ist es auf die Bereitstellung von Methoden zum Abrufen und Aktualisieren dieser Eigenschaften angewiesen. Schauen wir uns ein Beispiel an, um dies besser zu verstehen:

package main

import "fmt"

type Batman struct {
    actor string
    year int
}

func (b Batman) SayImBatman() {
    fmt.Printf("I'm %s and I'm Batman from year %d\n", b.actor, b.year)
}

func main() {
    b1 := Batman{actor: "Michael Keaton", year: 1989}
    b2 := Batman{actor: "Christian Bale", year: 2005}

    b1.SayImBatman()
    b2.SayImBatman()
}

Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
package main

import "fmt"

type Hero struct {
    team string
}

type Batman struct {
    Hero
    name string
}

type Ironman struct {
    Hero
    power int
}

func (h Hero) SayTeam() {
    fmt.Println("My Team is", h.team)
}

func (b Batman) SayImBatman() {
    fmt.Printf("I'm %s and I'm Batman\n", b.name)
}

func (i Ironman) SayPowerLevel() {
    fmt.Printf("I'm Ironman and my powerlevel is %d\n", i.power)
}

func main() {
    b1 := Batman{Hero{team: "Justice League"}, "Christian Bale"}
    i1 := Ironman{Hero{team: "Avengers"}, 23}

    b1.SayImBatman()
    b1.SayTeam()

    i1.SayPowerLevel()
    i1.SayTeam()
}

Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

In Golang beginnen Eigenschaften und Methoden, die aus dem Paket exportiert werden, mit einem Großbuchstaben. Wenn wir im utils-Paket „actor“ und „year“ in Kleinbuchstaben definieren, stellen wir sicher, dass sie nicht direkt geändert werden können. Stattdessen müssen Sie, wie Sie in der Datei main.go sehen, die exportierten Methoden (die mit einem Großbuchstaben beginnen) – GetActor, SetActor usw. – verwenden, um sie abzurufen und zu ändern.

Darum geht es bei der Kapselung – sicherzustellen, dass Sie versehentliche Änderungen an Daten verhindern und stattdessen Methoden für die sichere Interaktion mit den Daten bereitstellen.

Eine Sache, die Ihnen auffallen wird, ist, dass wir in allen Methoden für die Batman-Klasse einen Zeigerempfänger *Batman anstelle eines Wertempfängers Batman verwenden, wie wir es in den früheren Beispielen getan haben. Dies liegt daran, dass wir die ursprüngliche Struktur in den Set-Methoden ändern möchten. Und in Golang gilt als Best Practice: Wenn einige Methoden einen Zeigerempfänger benötigen, sorgen Sie dafür, dass alle Methoden aus Konsistenzgründen einen Zeigerempfänger verwenden. Aus diesem Grund verwenden auch die Get-Methoden einen Zeigerempfänger, obwohl sie die ursprüngliche Struktur nicht ändern.

Außerdem ist noch etwas zu beachten: Nur weil wir einen Zeigerempfänger verwenden, müssen wir Folgendes nicht tun: (&b1).GetActor. In Golang müssen Funktionen mit einem Zeigerargument einen Zeiger annehmen, Methoden mit einem Zeigerempfänger können jedoch entweder einen Wert oder einen Zeiger als Empfänger annehmen.

TL;DR: Golang übersetzt b1.GetActor automatisch als (&b1).GetActor, da die GetActor-Methode einen Zeigerempfänger hat, würde GetActor(b1) jedoch nicht in GetActor(&b1) übersetzen, wenn GetActor eine normale Funktion übernommen hätte ein Zeigerargument.

Polymorphismus und Abstraktion

Die nächsten beiden Säulen von OOP können zusammengefasst werden, da die Codebeispiele für sie ziemlich ähnlich aussehen würden. Polymorphismus bezieht sich auf die Programmierpraxis, bei der zwei verschiedene Objekte zweier verschiedener Klassen als Objekte derselben gemeinsamen Oberklasse behandelt werden können. Das bedeutet, dass Sie dieselbe Funktion für zwei verschiedene Objekte aufrufen können, als wären sie Objekte derselben Klasse. Dies sollte Ihnen einen Eindruck von den beteiligten Schnittstellen vermitteln :)

Schauen wir uns einen Code an, um dies besser zu verstehen:

package main

import "fmt"

type Batman struct {
    actor string
    year int
}

func (b Batman) SayImBatman() {
    fmt.Printf("I'm %s and I'm Batman from year %d\n", b.actor, b.year)
}

func main() {
    b1 := Batman{actor: "Michael Keaton", year: 1989}
    b2 := Batman{actor: "Christian Bale", year: 2005}

    b1.SayImBatman()
    b2.SayImBatman()
}

Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

In diesem Beispiel können der StartFight-Funktion sowohl die b1- als auch die i1-Objekte übergeben werden, obwohl sie in keiner Beziehung zueinander stehen. Versuchen Sie zu verstehen, wie sich dies von der Vererbung unterscheidet, bei der die untergeordneten Klassen Zugriff auf die Methoden der übergeordneten Klasse hatten. In diesem Beispiel gibt es keine untergeordneten und übergeordneten Klassen (und es werden auch keine Methoden gemeinsam genutzt). Stattdessen werden zwei verschiedene Objekte von einer Funktion als gleich behandelt: Dies wird als Polymorphismus bezeichnet.

Dies kann nun auch als Beispiel für Abstraktion betrachtet werden. Abstraktion ist, wie der Name schon sagt, die Programmierpraxis, Implementierungsdetails zu verbergen und stattdessen nur Funktionen bereitzustellen, die die Dinge für Sie erledigen. In diesem Beispiel müssen Sie sich nicht darum kümmern, wie die Methoden der einzelnen Helden konfiguriert sind. Sie können die StartFight-Funktion jederzeit weiterhin verwenden, wenn Sie eine der Fight-Funktionen der Helden verwenden möchten. Auf diese Weise bleiben die Implementierungsdetails vor dem Benutzer verborgen und nur die wesentlichen Details werden offengelegt.

Um nun auf den Polymorphismus zurückzukommen, gibt es zwei weitere häufige Beispiele, und zwar das Überschreiben und Überladen von Methoden.

Methodenüberschreibung

Methodenüberschreibung bezieht sich auf untergeordnete Klassen, die ihre eigene Implementierung von Methoden definieren, die in der übergeordneten Klasse definiert sind. Diese Implementierung wird nun anstelle der Implementierung der ursprünglichen übergeordneten Klasse verwendet. Nehmen wir den Code, den wir zuvor für die Vererbung verwendet haben, und sehen wir uns an, wie er mit Methodenüberschreibung aussieht:

package main

import "fmt"

type Hero struct {
    team string
}

type Batman struct {
    Hero
    name string
}

type Ironman struct {
    Hero
    power int
}

func (h Hero) SayTeam() {
    fmt.Println("My Team is", h.team)
}

func (b Batman) SayImBatman() {
    fmt.Printf("I'm %s and I'm Batman\n", b.name)
}

func (i Ironman) SayPowerLevel() {
    fmt.Printf("I'm Ironman and my powerlevel is %d\n", i.power)
}

func main() {
    b1 := Batman{Hero{team: "Justice League"}, "Christian Bale"}
    i1 := Ironman{Hero{team: "Avengers"}, 23}

    b1.SayImBatman()
    b1.SayTeam()

    i1.SayPowerLevel()
    i1.SayTeam()
}

Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Die Ausgabe dieses Programms ist:

//oops-in-go/utils/utils.go

package utils

type Batman struct {
    actor string
    year int
}

func (b *Batman) GetActor() string {
    return b.actor
}

func (b *Batman) GetYear() int {
    return b.year
}

func (b *Batman) SetActor(actor string) {
    b.actor = actor
}

func (b *Batman) SetYear(year int) {
    b.year = year
}
Nach dem Login kopieren

Die Objekte der Batman-Klasse verwenden jetzt ihre eigene SayTeam-Methode anstelle der der übergeordneten Hero-Klasse. Da die Ironman-Klasse über keine eigene SayTeam-Methode verfügt, verwendet ihr Objekt weiterhin die Methode ihrer übergeordneten Klasse. Das bedeutet „Methodenüberschreibung“, dass untergeordnete Klassen die in der übergeordneten Klasse definierten Methoden „überschreiben“.

Methodenüberladung

Dies bezieht sich darauf, dass dieselbe Funktion mehrere verschiedene Argumente annehmen kann. Diese Argumente können in Anzahl oder Typ unterschiedlich sein. Golang bietet zwei Möglichkeiten, dies zu erreichen: durch variable Funktionen und die andere durch Schnittstellen.

Sehen wir uns den Code für beide an, damit Sie ihn besser verstehen:

Verwendung variadischer Funktionen

// oops-in-go/main.go

package main

import (
    "fmt"
    "oops-in-go/utils"
)

func main() {
    b1 := utils.Batman{}
    b1.SetActor("Michael Keaton")
    b1.SetYear(1989)
    fmt.Printf("I'm %s and I'm Batman from year %d\n", b1.GetActor(), b1.GetYear())

    b1.SetActor("Christian Bale")
    b1.SetYear(2005)
    fmt.Printf("I'm %s and I'm Batman from year %d\n", b1.GetActor(), b1.GetYear())
}

Nach dem Login kopieren

Hier können Sie die listMembers-Funktion mit einer beliebigen Anzahl von Argumenten „überladen“.

Schnittstellen nutzen

package main

import "fmt"

type Hero interface {
    Fight()
}

type Batman struct {
    weapon string
}

type Ironman struct {
    weapon string
}

func (b Batman) Fight() {
    fmt.Printf("Batman hits with a %s\n", b.weapon)
}

func (i Ironman) Fight() {
    fmt.Printf("Ironman hits with a %s\n", i.weapon)
}

func StartFight(h Hero) {
    fmt.Println("Fight has started.")
    h.Fight()
}

func main() {
    b1 := Batman{"Batarang"}
    i1 := Ironman{"Repulsor rays"}

    StartFight(b1)
    StartFight(i1)
}

Nach dem Login kopieren

Die Ausgabe dieses Programms ist:

package main

import "fmt"

type Hero struct {
    team string
}

type Batman struct {
    Hero
    name string
}

type Ironman struct {
    Hero
    power int
}

func (h Hero) SayTeam() {
    fmt.Println("My Team is", h.team)
}

func (b Batman) SayImBatman() {
    fmt.Printf("I'm %s and I'm Batman\n", b.name)
}

func (i Ironman) SayPowerLevel() {
    fmt.Printf("I'm Ironman and my powerlevel is %d\n", i.power)
}

func (b Batman) SayTeam() {
    fmt.Printf("I'm Batman and my team is %s\n", b.team)
}

func main() {
    b1 := Batman{Hero{team: "Justice League"}, "Christian Bale"}
    i1 := Ironman{Hero{team: "Avengers"}, 23}

    b1.SayImBatman()
    b1.SayTeam()

    i1.SayPowerLevel()
    i1.SayTeam()
}

Nach dem Login kopieren

Hier „überladen“ wir die saySomething-Methode, um Argumente unterschiedlicher Art zu akzeptieren. Wir nehmen eine leere Schnittstelle als Argument, die ein beliebiger Typ sein kann, prüfen dann mithilfe eines Switch-Cases ihren Typ und geben die Ausgabe entsprechend aus.

Abschluss

Ich bin mir bewusst, dass dies eine lange Lektüre war, und wenn Sie bis zum Ende durchgehalten haben, möchte ich Sie wissen lassen, dass ich wirklich glücklich bin :) Ich hoffe aufrichtig, dass Sie viel Neues über objektorientierte Programmierung gelernt haben und wie man es in Golang implementiert. Ich schreibe auf meiner Website Blogs zu verschiedenen technischen Konzepten. Wenn Sie daran interessiert sind, neue Dinge zu lernen, empfehle ich Ihnen, sich für meinen Newsletter anzumelden.

Das obige ist der detaillierte Inhalt vonEinführung in die objektorientierte Programmierung (OOP) in Golang. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Quelle:dev.to
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Neueste Artikel des Autors
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage