Los geht's Objektorientierte Programmierung in Golang

Go ist eine seltsame Mischung aus alten und neuen Ideen. Es ist ein sehr erfrischender Ansatz, bei dem es nicht scheut ist, etablierte Vorstellungen von "wie man Dinge macht" wegzuwerfen. Viele Leute sind sich nicht einmal sicher, ob Go eine objektorientierte Sprache ist. Lassen Sie mich das jetzt ausruhen. Es ist! 

In diesem Lernprogramm erfahren Sie alles über die Feinheiten des objektorientierten Designs in Go, wie die Säulen der objektorientierten Programmierung wie Einkapselung, Vererbung und Polymorphismus in Go ausgedrückt werden und wie Go sich mit anderen Sprachen vergleicht.

Go ist eine unglaublich mächtige Programmiersprache. Sie lernen alles vom Schreiben einfacher Hilfsprogramme bis zum Erstellen von skalierbaren, flexiblen Webservern in unserem gesamten Kurs.

Die Go-Design-Philosophie

Gos Wurzeln basieren auf C und allgemeiner auf der Algol-Familie. Ken Thompson sagte halb scherzhaft, Rob Pike, Robert Granger und er hätten sich zusammengetan und beschlossen, C ++ zu hassen. Ob es ein Witz ist oder nicht, Go unterscheidet sich sehr von C ++. Dazu später mehr. Bei Go geht es um ultimative Einfachheit. Dies wird ausführlich von Rob Pike in Less erklärt, exponentiell mehr.

Gehen Sie gegen andere Sprachen

Go hat keine Klassen, keine Objekte, keine Ausnahmen und keine Vorlagen. Es verfügt über eine Speicherbereinigung und eine integrierte Parallelität. Das auffälligste Versäumnis, was objektorientiert betrifft, ist, dass es in Go keine Typhierarchie gibt. Dies steht im Gegensatz zu den meisten objektorientierten Sprachen wie C ++, Java, C #, Scala und sogar dynamischen Sprachen wie Python und Ruby.

Objektorientierte Sprachfunktionen

Go hat keine Klassen, aber es gibt Typen. Insbesondere hat es Strukturen. Strukturen sind benutzerdefinierte Typen. Strukturtypen (mit Methoden) dienen ähnlichen Zwecken wie Klassen in anderen Sprachen.

Structs

Eine Struktur definiert den Zustand. Hier ist eine Creature-Struktur. Es hat ein Namensfeld und eine boolesche Flagge namens Real, die uns sagt, ob es sich um eine echte Kreatur oder eine imaginäre Kreatur handelt. Strukturen haben nur Zustand und kein Verhalten.

type Creature struct Namenszeichenfolge Real bool 

Methoden

Methoden sind Funktionen, die auf bestimmte Typen angewendet werden. Sie haben eine Empfängerklausel, die festlegt, für welchen Typ sie arbeiten. Hier ist ein Dump () Methode, die auf Creature-Strukturen arbeitet und ihren Status druckt:

func (c Creature) Dump () fmt.Printf ("Name: '% s', Real:% t \ n", c.Name, c.Real)

Dies ist eine ungewöhnliche Syntax, aber sie ist sehr explizit und klar (anders als das implizite "this" oder das verwirrende "Selbst" von Python)..

Einbetten

Sie können anonyme Typen ineinander einbetten. Wenn Sie eine namenlose Struktur einbetten, stellt die eingebettete Struktur ihren Zustand (und ihre Methoden) direkt für die Einbettungsstruktur bereit. Zum Beispiel die FlyingCreature hat einen Namenlosen Kreatur eingebettete Struktur, was a bedeutet FlyingCreature ist ein Kreatur.

type FlyingCreature struct Creature WingSpan int

Wenn Sie eine Instanz von FlyingCreature haben, können Sie jetzt direkt auf die Attribute Name und Real zugreifen.

dragon: = & FlyingCreature Creature "Drache", falsch,, 15, fmt.Println (dragon.Name) fmt.Println (dragon.Real) fmt.Println (dragon.WingSpan)

Schnittstellen

Schnittstellen sind das Markenzeichen von Gos objektorientierter Unterstützung. Schnittstellen sind Typen, die Methodensätze deklarieren. Ähnlich wie Schnittstellen in anderen Sprachen haben sie keine Implementierung. 

Objekte, die alle Schnittstellenmethoden implementieren, implementieren die Schnittstelle automatisch. Es gibt keine Vererbung, keine Unterklassen oder das Schlüsselwort "implementiert". Im folgenden Codeausschnitt implementiert der Typ Foo die Fooer-Schnittstelle (die Namen der Go-Schnittstelle enden mit "er")..

Typ Fooer-Schnittstelle Foo1 () Foo2 () Foo3 () Typ Foo-Struktur  Funktion (F Foo) Foo1 () Fmt.Println ("Foo1 () hier") Funktion (F Foo) Foo2 () Fmt .Println ("Foo2 () here") func (f Foo) Foo3 () fmt.Println ("Foo3 () here")

Objektorientiertes Design: The Go Way

Mal sehen, wie Go sich an den Säulen der objektorientierten Programmierung orientiert: Einkapselung, Vererbung und Polymorphismus. Dies sind Funktionen von klassenbasierten Programmiersprachen, den beliebtesten objektorientierten Programmiersprachen.

Im Kern handelt es sich bei Objekten um Sprachkonstrukte, deren Zustand und Verhalten den Zustand beeinflussen und ihn anderen Teilen des Programms selektiv zugänglich machen. 

Verkapselung

Go kapselt Dinge auf Paketebene. Namen, die mit einem Kleinbuchstaben beginnen, sind nur in diesem Paket sichtbar. Sie können alles in einem privaten Paket ausblenden und bestimmte Typen, Schnittstellen und werkseitige Funktionen bereitstellen. 

Zum Beispiel hier, um das zu verbergen Foo Geben Sie oben ein und machen Sie nur die Schnittstelle verfügbar, die Sie umbenennen könnten, in Kleinbuchstaben foo und bieten a NewFoo ()Funktion, die die öffentliche Fooer-Schnittstelle zurückgibt:

Geben Sie Folgendes ein: foo struct  func (f foo) Foo1 () fmt.Println ("Foo1 () here") func (f foo) Foo2 () fmt.Println ("Foo2 () here") func (f foo) Foo3 () fmt.Println ("Foo3 () here") func NewFoo () Fooer return & Foo 

Dann kann Code aus einem anderen Paket verwendet werden NewFoo () und Zugang zu einer Fooer Schnittstelle vom internen implementiert foo Art:

f: = NewFoo () f.Foo1 () f.Foo2 () f.Foo3 ()

Erbe

Vererbung oder Unterklassifizierung war immer ein kontroverses Thema. Es gibt viele Probleme mit der Implementierungsvererbung (im Gegensatz zur Schnittstellenvererbung). Mehrfachvererbung, wie sie von C ++, Python und anderen Sprachen implementiert wird, leidet unter dem tödlichen Diamanten des Todes, aber selbst die Einzelvererbung ist kein Problem mit dem fragilen Problem der Basisklasse. 

Moderne Sprachen und objektorientiertes Denken favorisieren jetzt Komposition statt Vererbung. Go nimmt es zu Herzen und hat keinerlei Typhierarchie. Sie können Implementierungsdetails über die Komposition freigeben. Aber Go, in einer sehr seltsamen Wendung (die wahrscheinlich aus pragmatischen Bedenken stammte), erlaubt eine anonyme Komposition über das Einbetten. 

In jeder Hinsicht entspricht die Komposition durch Einbetten eines anonymen Typs der Implementierungsvererbung. Eine eingebettete Struktur ist genauso fragil wie eine Basisklasse. Sie können auch eine Schnittstelle einbetten, die dem Erben von einer Schnittstelle in Sprachen wie Java oder C ++ entspricht. Es kann sogar zu einem Laufzeitfehler kommen, der nicht zur Kompilierzeit erkannt wird, wenn der Einbettungstyp nicht alle Schnittstellenmethoden implementiert. 

Hier wird die Fooer-Schnittstelle von SuperFoo eingebettet, die Methoden werden jedoch nicht implementiert. Der Go-Compiler lässt Sie gerne ein neues SuperFoo erstellen und die Fooer-Methoden aufrufen, schlägt jedoch offensichtlich zur Laufzeit fehl. Das kompiliert:

Typ SuperFooer struct Fooer func main () s: = SuperFooer  s.Foo2 ()

Das Ausführen dieses Programms führt zu einer Panik:

panic: Laufzeitfehler: ungültige Speicheradresse oder Nullzeiger-Dereferenzierung [Signal 0xb code = 0x1 addr = 0x28 pc = 0x2a78] goroutine 1 [running]: panic (0xde180, 0xc82000a0d0) /usr/local/Cellar/go/1.6/libexec/ src / runtime / panic.go: 464 + 0x3e6 main.main () /Users/gigi/Documents/dev/go/src/github.com/oop_test/main.go:104 + 0x48 exit status 2 Der Prozess wurde mit dem Exit-Code beendet 1

Polymorphismus

Polymorphismus ist die Essenz der objektorientierten Programmierung: Die Fähigkeit, Objekte unterschiedlichen Typs einheitlich zu behandeln, solange sie an derselben Schnittstelle haften. Go-Schnittstellen bieten diese Funktion auf eine direkte und intuitive Weise. 

Hier ist ein ausführliches Beispiel, in dem mehrere Kreaturen (und eine Tür!), Die die Dumper-Schnittstelle implementieren, erstellt und in einem Slice gespeichert werden Dump () Methode wird für jeden aufgerufen. Sie werden auch verschiedene Arten der Instantiierung der Objekte feststellen.

Pakethauptimport "fmt" Typ Creature struct Name Zeichenfolge Real bool func Dump (c * Creature) fmt.Printf ("Name: '% s', Real:% t \ n", c.Name, c.Real ) func (c Creature) Dump () fmt.Printf ("Name: '% s', Real:% t \ n", c.Name, c.Real) type FlyingCreature struct Creature WingSpan int func ( fc FlyingCreature) Dump () fmt.Printf ("Name: '% s', Real:% t, WingSpan:% d \ n", fc.Name, fc.Real, fc.WingSpan) Typ Unicorn struct Creature  type Dragon struct FlyingCreature Typ Pterodactyl-Struktur FlyingCreature -Funktion NewPterodactyl (wingSpan int) * Pterodactyl pet: = & Pterodactyl FlyingCreature Creature "Pterodactyl", true,, wingSpan,, Rücksprung pet Dumper-Schnittstelle Dump () Typ Türstruktur Dicke int Farbstring func (d Door) Dump () fmt.Printf ("Tür => Dicke:% d, Farbe:% s", d.Thickness, d.Color)  func main () creature: = & Creature "einige Kreaturen", false, uni: = Einhorn Creature "Einhorn", false,, pet1: = & Pterodactyl FlyingCreature Creature "Pterodactyl", true,, 5,, pet2: = NewPterodactyl (8) door: = & Door 3, "red" Dump (Kreatur) creature.Dump () uni.Dump () pet1.Dump ( ) pet2.Dump () creatures: = [] Creature * creature, uni.Creature, pet1.Creature, pet2.Creature fmt.Println ("Dump () durch eingebetteten Creature-Typ") für _, creature: = Range-Creatures creature.Dump () Dumper: = [] Dumper Creature, uni, pet1, pet2, door fmt.Println ("Dump () über Dumper-Schnittstelle") für _, Dumper: = Range Dumper Dumper.Dump ( )

Fazit

Go ist eine echte objektorientierte Programmiersprache. Es ermöglicht die objektbasierte Modellierung und fördert die Verwendung von Schnittstellen anstelle von konkreten Typhierarchien. Go hat einige ungewöhnliche syntaktische Entscheidungen getroffen, aber das Arbeiten mit Typen, Methoden und Schnittstellen wirkt einfach, leichtgewichtig und natürlich. 

Das Einbetten ist nicht sehr rein, aber anscheinend war Pragmatismus am Werk, und das Einbetten wurde anstelle der Komposition nach Namen vorgesehen.