A farm

A farm

Tanulófeladat

Bevezetés

Az error interfész

Go-ban a hibakezelés nem kivételekkel történik. A hibák ehelyett olyan típusok hétköznapi értékei, amelyek megvalósítják a beépített error interfészt. Az error interfész nagyon minimális. Egyetlen metódusa van, az Error(), amely stringként adja vissza a hibaüzenetet.

type error interface {
  Error() string
}

Amikor olyan függvényt definiálsz, amelynek futása közben olyan hiba történhet, amelynek el kell jutnia a hívóhoz, a visszatérési típusok közé fel kell venned az error-t. Ha a függvénynek több visszatérési értéke van, a konvenció szerint az error mindig az utolsó.

func DoSomething() (int, error) {
  // ...
}

Egyszerű hiba létrehozása és visszaadása

Nem kell mindig neked megvalósítanod az error interfészt. Egyszerű hiba létrehozásához használhatod az errors.New() függvényt, amely a szabványos könyvtár errors csomagjának része. Egyetlen dolgot kell átadnod: a hibaüzenetet stringként, és az errors.New() gondoskodik róla, hogy létrejöjjön egy érték, amely tartalmazza az üzenetedet, és megvalósítja az error interfészt.

Ha a függvény hibát ad vissza, jó gyakorlat, ha minden más visszatérési értékre a zéróértéket adod vissza:

func DoSomething() (SomeStruct, int, error) {
  // ...
  return SomeStruct{}, 0, errors.New("failed to calculate result")
}
Caution

Ne feltételezd, hogy minden függvény zéróértéket ad vissza a többi visszatérési értékre, ha hiba történt. A legjobb, ha abból indulsz ki, hogy hiba esetén egyik másik visszatérési érték használata sem biztonságos. Kivételt csak azok az esetek jelentenek, amikor a dokumentáció egyértelműen kimondja, hogy a többi visszatérési értéknek hiba esetén is van jelentése.

Ha egy ilyen egyszerű hibát több helyen is szeretnél használni, érdemes külön változót deklarálni neki, ahelyett hogy minden helyen az errors.New-t hívnád. A konvenció szerint a változó neve Err-rel vagy err-el kezdődjön (attól függően, hogy exportált-e vagy sem). Az ilyen hibaváltozókat gyakran őrszem hibáknak nevezik.

import "errors"

var ErrNotFound = errors.New("resource was not found")

func DoSomething() error {
  // ...
  return ErrNotFound
}

A hibára adj vissza nil-t, ezzel jelezve, hogy a függvény futása során nem történt hiba:

func Foo() (int, error) {
  return 10, nil
}

A hibák ellenőrzése

Ha olyan függvényt hívsz meg, amely hibát ad vissza, gyakori, hogy a hiba értékét egy err nevű változóban tárolod. Mielőtt felhasználod a függvény tényleges eredményét, ellenőrizned kell, hogy nem történt-e hiba.

Hogy elkerüld a kódod „happy path” részének egymásba ágyazását, először a hibaeseteket kell kezelned. A == és a != operátorral összehasonlíthatjuk a hibát a nil-lel, és tudjuk, hogy hiba történt, ha az err nem nil.

func processUserFile() error {
	file, err := os.Open("./users.csv")
	if err != nil {
		return err
	}

	// do something with file
}

A legtöbbször a hiba a hívási láncon felfelé visszaadódik, ahogy a fenti példában is látszik. A hibát másképp is kezelheted: naplózhatod, és folytathatod valami mással. Jó gyakorlat, ha a hibát vagy visszaadod, vagy naplózod, de soha nem mindkettőt.

Mivel a Go-ban a legtöbb függvény visszatérési értékei között szerepel egy hiba, az if err != nil mintával lépten-nyomon találkozni fogsz a Go-kódban.

Egyedi hibatípusok

Ha a hibádban a hibaüzenet stringnél több információt szeretnél elhelyezni, létrehozhatsz egyedi hibatípust. Ahogy korábban említettük, a Go-ban minden, ami megvalósítja az error interfészt (vagyis rendelkezik egy Error() string metódussal), használható hibaként.

Az egyedi hibatípus létrehozására általában egy structot használunk. A konvenció szerint az egyedi hibatípusok neve Error-ral végződjön. Az is a legjobb, ha az Error() string metódust pointer átvevővel definiálod; a magyarázatért nézd meg ezt a Stackoverflow-hozzászólást. Ez azt jelenti, hogy pointert kell visszaadnod az egyedi hibádra, különben nem fog error-nak számítani, mert a nem pointer érték nem biztosítja az Error() string metódust.

type MyCustomError struct {
  message string
  details string
}

func (e *MyCustomError) Error() string {
  return fmt.Sprintf("%s, details: %s", e.message, e.details)
}

func someFunction() error {
  // ...
  return &MyCustomError{
    message: "...",
    details: "...",
  }
}

Utasítások

Eljött végre a nap, amire olyan sokáig vártál, és most büszke tulajdonosa vagy egy gyönyörű farmnak az Alpokban.

Továbbra sem szeretsz túl korán kelni, hogy megetesd a teheneidet. Mivel kiváló mérnök vagy, építesz egy etetőadagolót, a FEED-M-ALL-t.

A projekted befejezéséhez már csak egyetlen dolog hiányzik: egy kódrészlet, amely kiszámítja, mennyi takarmányt kapjon minden tehén. Fontos, hogy minden tehén ugyanannyit kapjon, el kell kerülnöd a konfliktusokat. A tehenek nagyon érzékenyek.

Szerencsére nem neked kell kidolgoznod a takarmánymennyiség kiszámításához szükséges összes képletet. Egy titokzatos külső könyvtárat használsz, amelyet az interneten találtál. Elvileg ettől lesznek a legboldogabbak a tehenek. A könyvtár közzétesz egy típust, amely megvalósítja a következő interfészt. Erre fogsz támaszkodni a saját kódodban.

type FodderCalculator interface {
	FodderAmount(int) (float64, error)
	FatteningFactor() (float64, error)
}

Ahogy a kódodon dolgozol, fokozatosan javítani fogod a hibakezelést, hogy az robusztusabb legyen, és később könnyebben lehessen debugolni, amikor a mindennapi farméletedben használod.

1. Oszd el egyenletesen a takarmányt

Először is arra összpontosítasz, hogy megírd a tehenenkénti takarmánymennyiség kiszámításához szükséges kódot.

Valósítsd meg a DivideFood függvényt, amely argumentumként egy FodderCalculator-t és a tehenek számát egész számként fogadja. Ennél a részfeladatnál feltételezed, hogy az átadott tehenek száma mindig nagyobb nullánál. A függvénynek a tehenenkénti takarmánymennyiséget kell visszaadnia float64-ként, vagy egy hibát, ha az történt.

A számításhoz először le kell kérdezned az összes tehén teljes takarmánymennyiségét. Ezt úgy teheted meg, hogy meghívod a FodderAmount metódust, és átadod neki a tehenek számát. Ezenkívül szükséged van egy tényezőre is, amellyel ezt a mennyiséget meg kell szorozni. Ezt a tényezőt a FatteningFactor metódus meghívásával kapod meg. Ezzel a két értékkel és a tehenek számával most már kiszámíthatod a tehenenkénti takarmánymennyiséget (float64-ként). Ezt kell visszaadnia a DivideFood függvénynek.

Ha a meghívott metódusok egyike hibát ad vissza, a végrehajtásnak le kell állnia, és azt a hibát változatlan formában vissza kell adnia a DivideFood függvénynek.

// For this example, we assume FodderAmount returns 50
// and FatteningFactor returns 1.5.
DivideFood(fodderCalculator, 5)
// => 15 <nil>

// Now assuming FodderAmount returns an error with message "something went wrong".
DivideFood(fodderCalculator, 5)
// => 0 "something went wrong"

2. Ellenőrizd a tehenek számát

Miközben az első részfeladaton dolgoztál, rájöttél, hogy a használt külső könyvtár nem olyan jó minőségű, mint gondoltad. Például nem tud megfelelően kezelni az érvénytelen bemeneteket. Ezt a korlátot úgy szeretnéd megkerülni, hogy a saját kódodban ellenőrzést adsz a bemeneti értékhez.

Írj egy ValidateInputAndDivideFood függvényt, amelynek ugyanaz a szignatúrája, mint a fenti DivideFood-nak.

  • Ha az átadott tehenek száma nagyobb 0-nál, a függvény hívja meg a DivideFood-ot, és adja vissza a hívás eredményét.
  • Ha a tehenek száma 0 vagy kevesebb, a függvény adjon vissza egy hibát a következő üzenettel: "invalid number of cows".
ValidateInputAndDivideFood(fodderCalculator, 5)
// => 15 <nil>

ValidateInputAndDivideFood(fodderCalculator, -2)
// => 0 "invalid number of cows"

3. Javítsd a hibakezelést

Jó döntés volt ellenőrizni a tehenek számát, mielőtt továbbadtad, de nem vagy egészen elégedett a nem túl konkrét hibaüzenettel. Úgy döntesz, hogy jobban csinálod: létrehozol egy egyedi hibatípust InvalidCowsError néven.

Az egyedi hibának tartalmaznia kell a tehenek számát (int) és egy egyedi üzenetet (string), az Error metódusnak pedig a következő formátumban kell sorosítania az adatokat:

{number of cows} cows are invalid: {custom message}

Felvértezve az egyedi hibáddal valósítsd meg a ValidateNumberOfCows függvényt, amely a tehenek számát egész számként fogadja, és egy hibát (vagy nil-t) ad vissza.

  • Ha a tehenek száma kisebb 0-nál, a függvény egy InvalidCowsError hibát ad vissza, amelynek egyedi üzenete "there are no negative cows".
  • Ha a tehenek száma 0, a függvény egy InvalidCowsError hibát ad vissza, amelynek egyedi üzenete "no cows don't need food".
  • Egyébként a függvény nil-t ad vissza, jelezve, hogy az ellenőrzés sikeres volt.
err := ValidateNumberOfCows(-5)
err.Error()
// => "-5 cows are invalid: there are no negative cows"

Miután rengeteg munkát fektettél ebbe az ellenőrző függvénybe, észreveszed, hogy már este van, és otthagyod az asztalodat, hogy élvezd a naplementét a hegyek fölött. Az új ellenőrző függvény tényleges beépítését a kódodba egy másik napra hagyod.

Szerkesztés GitHubon A hivatkozás új ablakban vagy lapon nyílik meg
Go Exercism

Készen állsz elkezdeni a(z) A farm feladatot?

Iratkozz fel az Exercism-re, hogy megtanuld és elsajátítsd a(z) Go nyelvet 34 fogalom166 feladat segítségével, valódi emberi mentorálással, mindez ingyen.