Rozhranie v Go: Interface

Sunday, Nov 15, 2020 by Lenka
Go programovanie s rozhraniami.

To, že je Go skvelý a výnimočný jazyk sme si už veľakrát dokázali. Poďme sa bližšie pozrieť na rozhranie (interface).

Úvod

Rozhrania v Go boli navrhnuté podľa protokolov z programovacieho jazyka Smalltalk. Ide o špecifikáciu jednej alebo viacerých metód ich názvom, parametrami a návratovými typmi, ktoré sú spoločné pre entity určitou zdieľanou vlastnosťou. Rozhrania však nešpecifikujú telo - správanie metódy.

V súvislosti s Go rozhraniami sa môžeme stretnúť s pojmom Duck typing - ak to kráča ako kačka, ak to kváka ako kačka, tak to musí byť kačka! - čo voľne preložené znamená, že vhodnosť objektu je určená implementovanými metódami a vlastnosťami a nie typom samotného objektu. Tento pojem nie je presne definovaný a hovorí, že typová zhoda nie je staticky kontrolovaná. V Go však túto informáciu automaticky kontroluje prekladač a preto ani nie je nutné explicitne určovať, ktoré štruktúry implementujú dané rozhranie. Go autori preferujú pojem “structural typing”.

Praktická ukážka

Vysvetlime si rozhrania na praktickom príklade. Predstavme si, že v kancelárii máme kávovar CoffeeMaker s funkciou Coffee(int) a Done(). Pre jednoduchosť, tieto funkcie len vypisujú na výstup. Funkcia Coffee má aj argument size (type int) pre veľkosť našej kávy.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
type CoffeeMaker struct {
	CoffeeSensor float64
	WatterSensor float64
}
func (cm CoffeeMaker) Coffee(size int) {
	fmt.Printf("Making coffee %d ml\n", size)
}
func (cm CoffeeMaker) Done() {
	fmt.Println("Done")
}

Časom sme dokúpili inteligentnejší kávovar LatteMaker, ktorý okrem Coffee(int) a Done() má aj napeňovač mlieka - metódu Milk(), ktorá tiež len vypisuje na výstup.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
type LatteMaker struct {
	CoffeeSensor float64
	WatterSensor float64
	MilkSensor float64
}
func (lm LatteMaker) Coffee(size int) {
	fmt.Printf("Making coffee %d ml\n", size)
}
func (lm LatteMaker) Milk() {
	fmt.Println("Milk")
}
func (lm LatteMaker) Done() {
	fmt.Println("Done")
}

Keďže stále inovujeme, nastavme si automatické ovládanie kávovaru. Vytvorme si funkciu, ktorá ako argument zoberie kávovar a veľkosť kávy akú chceme, urobí kávu z daného kávovaru (device.Coffee(int)) a ukončí činnosť (device.Done()).

1
2
3
4
func makeCoffee(device CoffeeMaker, size int) {
	device.Coffee(size)
	device.Done()
}

Vyskúšajme si jednoduchý main, kde si vytvoríme CoffeeMaker a spravíme kávu pomocou funkcie makeCoffee. Dostaneme skvelé espresso.

1
2
3
4
func main() {
	var maker CoffeeMaker
	makeCoffee(maker, 30)
}
$ go run main.go
Making coffee 30 ml
Done

Lenže, keď podobný main použijeme pre náš nový LatteMaker dostaneme Error, ktorý hovorí, že funkcia makeCoffee nepozná typ LatteMaker.

1
2
3
4
func main() {
	var maker LatteMaker
	makeCoffee(maker, 90)
}
$ go run main.go
main.go: cannot use latter (type LatteMaker) as type CoffeeMaker in argument 
to makeCoffee

Riešením nie je nič iné ako rozhranie!

Dve štruktúry, ktoré zdieľajú rovnakú vlastnosť - robenie kávy - špecifikujeme pomocou ich spoločných metód.

Vytvoríme si jednoduché rozhranie, ktoré hovorí, že objekt, ktorý ho bude implementovať musí obsahovať metódu Coffee s int parametrom a metódu Done bez parametra. Obe metódy sú bez návratovej hodnoty. Ak by sme mali metódu s navratovou hodnotou, jej typ musí byť tiež definovaný v rozhraní.

1
2
3
4
type Maker interface {
	Coffee(int)
	Done()
}

Štruktúry predstavujúce naše kávovary nemusíme upravovať, pretože ich metódy vyhovujú rozhraniu Maker. Jediné, čo musíme spraviť, je upraviť funkciu makeCoffee, aby akceptovala Maker.

1
2
3
4
func makeCoffee(device Maker, size int) {
	device.Coffee(size)
	device.Done()
}

Overíme to v main funkcii.

1
2
3
4
5
6
func main() {
	var coffeer CoffeeMaker
	makeCoffee(coffeer, 30)
	var latter LatteMaker
	makeCoffee(latter, 250)
}
$ go run main.go
Making coffee 30 ml
Done
Making latte 250 ml
Done

CoffeeMaker uvarí skvelé espresso a LatteMaker veľké latte.

Celý kód

Pri použití, štruktúrovať do viacerých súborov. play.golang.org

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package main

import "fmt"

type Maker interface {
	Coffee(int)
	Done()
}

type CoffeeMaker struct {
	CoffeeSensor float64
	WatterSensor float64
}

type LatteMaker struct {
	CoffeeSensor float64
	WatterSensor float64
	MilkSensor float64
}

func (cm CoffeeMaker) Coffee(size int) {
	fmt.Printf("Making coffee %d ml\n", size)
}
func (cm CoffeeMaker) Done() {
	fmt.Println("Done")
}

func (lm LatteMaker) Coffee(size int) {
	fmt.Printf("Making coffee %d ml\n", size)
}
func (lm LatteMaker) Milk() {
	fmt.Println("Milk")
}
func (lm LatteMaker) Done() {
	fmt.Println("Done")
}

func makeCoffee(device Maker, size int) {
	device.Coffee(size)
	device.Done()
}

func main() {
	var coffeer CoffeeMaker
	makeCoffee(coffeer, 30)
	var latter LatteMaker
	makeCoffee(latter, 250)
}

Zhrnutie

Vyskúšali sme si Go rozhrania vysvetliť na jednoduchom príklade, kde sme mali 2 podobné zariadenia - kávovary, ktoré mali spoločnú metódu Coffee(int) s rovnakym typom parametru a metódu Done(). Na základe týchto vlastností sme mohli vytvoriť rozhranie, ktorému sme tieto spoločné črty zadefinovali a následne všade, kde sme chceli pracovať s kávovarmi, sme používali rozhranie.

Domáca úloha

O rozhraní môžeme stokrát počuť, stokrát čítať, ale aj tak ho najlepšie pochopíme až keď si ho reálne vyskúšame. A preto hor sa do vlastného rozhrania!

Rozhliadnite sa okolo seba a vytvorte ďalší príklad na rozhrania. Môže definovať viacero objektov aj obsahovať 1 či viac spoločných vlastností, s argumentmi a návratovými hodnotami. Inšpirujte sa a napíšte nám, čo sa vám podarilo vytvoriť. Tešíme sa!

Referencie

  1. Go by Example: Interfaces
  2. An Introduction to Programming in Go: Interfaces
  3. Effective Go: Interfaces

Vaše otázky, návrhy a komentáre

Verím, že vás tento návod inšpiroval a budem vďačný ak dáte spätnú väzbu a pomôžete mi zamerať sa na to čo by vás zaujímalo.

TAK ČO HOVORÍŠ ?

Kontaktuj nás ak potrebuješ pomoc, veľmi radi pomôžeme.

Kontaktuj nás