Låt oss gå Objektorienterad programmering i Golang

Go är en konstig blandning av gamla och nya idéer. Det har en väldigt uppfriskande inställning där det inte är rädda att kasta bort etablerade begrepp av "hur man gör saker". Många är inte ens säkra på om Go är ett objektorienterat språk. Låt mig lägga mig på vila nu. Det är! 

I denna handledning lär du dig information om alla invecklade objektorienterade design i Go, hur pilarna i objektorienterad programmering som inkapsling, arv och polymorfism uttrycks i Go, och hur går det att jämföra med andra språk.

Go är ett otroligt kraftfullt programmeringsspråk, lära allt från att skriva enkla verktyg för att bygga skalbara, flexibla webbservrar i hela vår kurs.

The Go Design Philosophy

Gos rötter är baserade på C och mer generellt på familjen Algol. Ken Thompson sa skämt att Rob Pike, Robert Granger och sig själv kom och bestämde att de hatar C ++. Oavsett om det är ett skämt eller inte, Go är väldigt annorlunda än C ++. Mer om det senare. Go handlar om ultimat enkelhet. Detta förklaras i detalj av Rob Pike i Mindre är exponentiellt mer.

Gå mot andra språk

Go har inga klasser, inga föremål, inga undantag och inga mallar. Den har skräpuppsamling och inbyggd samtidighet. Den mest slående försummelsen vad gäller objektorienterad är att det inte finns någon typhierarki i Go. Detta står i kontrast till de flesta objektorienterade språken som C ++, Java, C #, Scala och även dynamiska språk som Python och Ruby.

Gå objektorienterade språkfunktioner

Go har inga klasser, men det har typer. I synnerhet har den strukturer. Strukturer är användardefinierade typer. Strukturtyper (med metoder) tjänar liknande syften till klasser på andra språk.

structs

En struktur definierar tillstånd. Här är en skapningsstruktur. Den har ett namnfält och en boolesisk flagga som heter Real, som berättar om det är en riktig varelse eller en imaginär varelse. Strukturer håller bara tillstånd och inget beteende.

typ Creature struct Namnsträng Real bool 

metoder

Metoder är funktioner som fungerar på vissa typer. De har en mottagarklausul som anger vilken typ de arbetar på. Här är en Dumpa() metod som fungerar på Creature structs och skriver ut deras tillstånd:

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

Detta är en ovanlig syntax, men det är mycket tydligt och tydligt (till skillnad från det implicita "detta" eller Pythons förvirrande "själv").

Bädda

Du kan bädda in anonyma typer inuti varandra. Om du bäddar in en namnlös struktur, ger den inbäddade strukten sitt tillstånd (och metoder) till inbäddningsstrukturen direkt. Till exempel, FlyingCreature har en namnlös Varelse struct inbäddade i det, vilket betyder a FlyingCreature är en Varelse.

skriv FlyingCreature struct Creature WingSpan int

Nu, om du har en förekomst av en FlyingCreature, kan du få tillgång till dess namn och reella attribut direkt.

drake: = & FlyingCreature Creature "Dragon", false,, 15, fmt.Println (dragon.Name) fmt.Println (dragon.Real) fmt.Println (dragon.WingSpan)

gränssnitt

Gränssnitt är kännetecknet för Gos objektorienterade stöd. Gränssnitt är typer som förklarar uppsättningar av metoder. På samma sätt som gränssnitt på andra språk har de inget genomförande. 

Objekt som implementerar alla gränssnittsmetoder implementerar automatiskt gränssnittet. Det finns ingen arv eller subclassing eller "implementer" nyckelord. I följande kodsats implementerar Foo Fooer-gränssnittet (enligt konventionen, gå gränssnittsnamn slutar med "er").

typ Fooer-gränssnitt Foo1 () Foo2 () Foo3 () typ Foo struct  func (f Foo) Foo1 () fmt.Println ("Foo1 () här") func (f Foo) Foo2 () fmt .Println ("Foo2 () här") func (f Foo) Foo3 () fmt.Println ("Foo3 () här")

Objektorienterad design: The Go Way

Låt oss se hur Go mäter pelarna i objektorienterad programmering: inkapsling, arv och polymorfism. Det är funktioner i klassbaserade programmeringsspråk, som är de mest populära objektorienterade programmeringsspråken.

Kärnan är objekt språkkonstruktioner som har tillstånd och beteende som fungerar på staten och selektivt utsätter den för andra delar av programmet. 

inkapsling

Gå inkapslar saker på paketnivå. Namn som börjar med en liten bokstav är bara synliga i det paketet. Du kan dölja allt i ett privat paket och bara avslöja specifika typer, gränssnitt och fabriksfunktioner. 

Till exempel, här för att dölja foo skriv ovan och avslöja bara gränssnittet du kan byta namn på det till små bokstäver foo och ge en NewFoo ()funktion som returnerar public Fooer-gränssnittet:

typ foo struct  func (f foo) Foo1 () fmt.Println ("Foo1 () här") func (f foo) Foo2 () fmt.Println ("Foo2 () här") func foo3 () fmt.Println ("Foo3 () här") func NewFoo () Fooer return & Foo 

Då kan kod från ett annat paket användas NewFoo () och få tillgång till a Fooer gränssnitt implementerat av internt foo typ:

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

Arv

Arv eller subclassing var alltid en kontroversiell fråga. Det finns många problem med implementeringsarvet (i motsats till gränssnittets arv). Många arv som implementeras av C ++ och Python och andra språk lider av dödliga diametern av dödsproblem, men även ensamhet är ingen picknick med det ömtåliga basklassproblemet. 

Moderna språk och objektorienterat tänkande favoriserar nu komposition över arv. Go tar det till hjärta och har ingen typ hierarki alls. Det låter dig dela implementeringsdetaljer via komposition. Men Gå, i en väldigt märklig vridning (som troligen härrörde från pragmatiska farhågor) tillåter anonym sammansättning via inbäddning. 

För alla ändamål motsvarar kompositionen genom att bädda in en anonym typ till implementeringsarvet. En inbyggd struktur är lika bräcklig som en basklass. Du kan också bädda in ett gränssnitt, vilket motsvarar arvet från ett gränssnitt på språk som Java eller C ++. Det kan till och med leda till ett runtime-fel som inte upptäcks vid sammanställningstiden om inbyggnadstypen inte implementerar alla gränssnittsmetoder. 

Här inbjuder SuperFoo Fooer-gränssnittet, men implementerar inte dess metoder. Go-kompilatorn låter dig lyckligtvis skapa en ny SuperFoo och ringa till Fooer-metoderna, men kommer naturligtvis att misslyckas vid körning. Detta sammanställer:

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

Att köra detta program leder till panik:

panik: runtime error: ogiltig minnesadress eller nollpekare dereference [signal 0xb kod = 0x1 addr = 0x28 pc = 0x2a78] goroutine 1 [springer]: panik (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 utgångsstatus 2 Process färdig med utgångskod 1

polymorfism

Polymorfism är kärnan i objektorienterad programmering: förmågan att behandla objekt av olika slag enhetligt så länge de följer samma gränssnitt. Go-gränssnitt ger denna kapacitet på ett mycket direkt och intuitivt sätt. 

Här är ett utförligt exempel där flera varelser (och en dörr!) Som implementerar Dumper-gränssnittet skapas och lagras i en skiva och sedan Dumpa() Metoden kallas för var och en. Du kommer att märka olika stilar för att instantiera objekten också.

paketet huvudimport "fmt" typ Creature struct Namnsträng Real bool func Dump (c * Varelse) fmt.Printf ("Name:"% s ", Real:% t \ n", c.Name, c.Real ) func (c Creature) Dump () fmt.Printf ("Namn: '% s', Real:% t \ n", c.Name, c.Real) skriv FlyingCreature struct Creature WingSpan int func fc FlyingCreature) Dump () fmt.Printf ("Name:"% s ", Real:% t, WingSpan:% d \ n", fc.Name, fc.Real, fc.WingSpan) skriv Unicorn struct Creature  typ Dragon Struct FlyingCreature typ Pterodactyl struct FlyingCreature func NewPterodactyl (wingSpan int) * Pterodactyl pet: = & Pterodactyl FlyingCreature Creature "Pterodactyl", sant,, wingSpan,, return pet Dump () typ Dörrstruktur Tjocklek int Färgsträng func (d Dörr) Dump () fmt.Printf ("Dörr => Tjocklek:% d, Färg:% s", d.Tjocklek, d.Color)  func main () creature: = & Creature "någon varelse", false, uni: = Unicorn Creature "Unicorn", false, pet1: = & Pterodactyl FlyingCreature Creature "Pterodactyl", sant,, 5,, pet2: = NewPterodactyl (8) dörr: = & Dörr 3, "röd" Dump (varelse) creature.Dump () uni.Dump () pet1.Dump ) djur2.Dump () varelser: = [] Varelse * varelse, uni.Creature, pet1.Creature, pet2.Creature fmt.Println ("Dump () genom Creature embedded type") för _, varelse: = varierande varelser creature.Dump () dumpers: = [] Dumper varelse, uni, pet1, pet2, dörr fmt.Println ("Dump () through Dumper interface") för _, dumper: = dumpare )

Slutsats

Go är ett bona fide objektorienterat programmeringsspråk. Det möjliggör objektbaserad modellering och främjar bästa praxis att använda gränssnitt istället för konkreta typhierarkier. Go gjorde några ovanliga synaktiska val, men övergripande arbete med typer, metoder och gränssnitt känns enkelt, lätt och naturligt. 

Inbäddning är inte särskilt ren, men pragmatism var tydligen på jobbet och inbäddning tillhandahölls istället för enbart komposition på namn.