a-tanya

a-tanya

Tanulófeladat

Bevezetés

Az ideális világban minden tökéletesen működik. A való világban viszont a dolgok rosszul sülhetnek el, és az, hogy hogyan kezeljük ezeket a helyzeteket, fontos ahhoz, hogy a szoftverünk robusztus és megbízható legyen. A kivételek kulcsfontosságú fogalom a programozásban, amely lehetővé teszi, hogy elegánsan kezeljük a hibákat és a váratlan helyzeteket.

Ha kiváltunk egy hibát, és nem kezeljük, a program leáll, és hibaüzenetet dob. A legtöbb esetben nem szeretnéd, hogy a program leálljon, amikor hiba történik; inkább kezelni szeretnéd a hibát, és tovább futtatni a programot.

Kivétel kiváltása

A Crystalben a kivételeket a raise kulcsszóval váltjuk ki, és vagy egy String, vagy egy Exception objektum adható meg neki. Ha nem kezeljük, a program leáll, és kiírja a hibaüzenetet.

raise "This is an error"

A Crystalben számos beépített kivétel van, például ArgumentError, IndexError, KeyError, IOError, SystemCallError, TypeError, ZeroDivisionError és még sok más. Ezeknél meg kell adnod egy üzenetet a kivételnek.

raise ArgumentError.new("This is an argument error")

Kivételek kezelése

Nem szeretnénk, ha a programunk összeomlana, amikor kiváltunk egy kivételt. Ezért, ha tudjuk, hogy egy kódrészlet hajlamos a hibákra, becsomagolhatjuk egy begin blokkba, és a rescue blokkal elkaphatjuk a kivételt. A begin blokk jelöli annak a kódnak a kezdetét, amelyik kivételt válthat ki, a rescue blokk pedig kezeli a kivételt.

begin
 raise "This is an error"
rescue
 puts "An error occurred!"
end

A rescue blokk egy változóval is megadható, így megkapod a kivétel objektumát.

begin
 raise "This is an error"
rescue ex
 puts "An error occurred: #{ex.message}"
end

A rescue blokk megadható egy konkrét kivételtípussal is, hogy csak azt a kivételt kapd el.

begin
 raise ArgumentError.new("This is an argument error")
rescue ArgumentError
 puts "An argument error occurred!"
end

# or

begin
 raise ArgumentError.new("This is an argument error")
rescue ex : ArgumentError
 puts "An argument error occurred: #{ex.message}"
end

Több rescue blokkal különböző típusú kivételeket kezelhetsz. Az alábbi példában az első rescue blokk egy ArgumentError-t kap el, a második rescue blokk pedig minden más kivételt.

begin
 raise ArgumentError.new("This is an argument error")
rescue ArgumentError
 puts "An argument error occurred!"
rescue
 puts "An error occurred!"
end

A begin blokkhoz tartozhat egy else blokk is, amely akkor fut le, ha nem váltunk ki kivételt.

begin
 puts "No error occurred"
rescue
 puts "An error occurred!"
else
 puts "No error occurred"
end

Végül van egy ensure blokk, amely mindig lefut, függetlenül attól, hogy kiváltottunk-e kivételt.

begin
 raise "This is an error"
rescue
 puts "An error occurred!"
ensure
 puts "This is always executed"
end

Metóduskonvenció

Egyes metódusoknak két változata van: az egyik !-tal, a másik anélkül. Ez két különböző dolgot jelenthet. Az egyik, hogy a metódus módosítja az objektumot, a másik, hogy a metódus kivételt válthat ki.

De létezik egy másik konvenció is a metódusok ?-lel való lezárására, amelyet a Boolean fogalomnál említettünk. Egyes metódusok alapértelmezés szerint kivételt váltanak ki, de van egy ?-lel végződő változatuk is, amely kivétel kiváltása helyett nil-t ad vissza.

Ez ideális, ha el akarod kerülni, hogy hibát váltsunk ki. Ez a teljesítménynek is jót tehet, mivel nem kell stacktrace-t létrehozni, és ha megfelelően van beállítva, biztonságosabbá teheti a kódot.

Egyéni kivételek

Saját kivételeket is létrehozhatsz, ha a Exception osztályból örökölsz. Ekkor igény szerint felülírhatod az initialize metódust, hogy beállítsd a kivétel üzenetét. Ezt úgy teheted meg, hogy a @message nevű példányváltozónak értékül adod az üzenetet.

class MyException < Exception
 def initialize
   @message = "This is my exception"
 end
end

raise MyException.new

Utasítások

Eljött hát a nap, amelyre olyan sokáig vártál: 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 takarmányadagoló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 az összes képletet a takarmánymennyiség kiszámításához. Egy titokzatos külső könyvtárat használsz, amelyet az interneten találtál. Állítólag a legboldogabb teheneket eredményezi. A könyvtár közzétesz egy osztályt, amely a következő interfészt valósítja meg. A saját kódodban erre fogsz támaszkodni.

class FodderCalculator
  def fodder_amount! : Number 
  end

  def fattening_factor! : Number
  end
end

Ahogy a kódon 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 azt a kódot, amely egy tehénre jutó takarmánymennyiség kiszámításához kell.

Készítsd el a TheFarm.divide_food metódust, amely argumentumként egy FodderCalculator objektumot és a tehenek számát egész számként fogadja. Ehhez a részfeladathoz feltételezd, hogy a kapott tehenek száma mindig nagyobb nullánál. A metódusnak az egy tehénre jutó takarmány mennyiségét kell visszaadnia.

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 fodder_amount! metódust, és átadod neki a tehenek számát. Ezen kívül szükséged van egy szorzóra, amellyel ezt a mennyiséget meg kell szorozni. Ezt a szorzót a fattening_factor! metódus meghívásával kapod meg. Ezzel a két értékkel és a tehenek számával most kiszámíthatod az egy tehénre jutó takarmány mennyiségét (float64-ként).

Ha a meghívott metódusok valamelyike hibát ad vissza, a végrehajtásnak le kell állnia, és 0-t kell visszaadnia.

fodder_calculator = FodderCalculator.new(50, 1.5)
TheFarm.divide_food(fodder_calculator, 5)
# => 15.0

fodder_calculator = FodderCalculator.new(50, nil)
TheFarm.divide_food(fodder_calculator, 5)
# => 0

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

Miközben a fenti 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 kikerülni, hogy a saját kódodban ellenőrzést adsz a bemeneti értékhez.

Írj egy TheFarm.validate_input_and_divide_food függvényt, amelynek ugyanaz a szignatúrája, mint a fenti TheFarm.divide_food metódusnak.

  • Ha a kapott tehenek száma nagyobb 0-nál, a függvény hívja meg a TheFarm.divide_food metódust, és adja vissza a hívás eredményét.
  • Ha a tehenek száma 0 vagy kevesebb, a függvény dobjon egy hibát a "Number of cows must be greater than 0" üzenettel.
TheFarm.validate_input_and_divide_food(fodderCalculator, 5)
# => 15

TheFarm.validate_input_and_divide_food(fodderCalculator, -2)
# Error: Number of cows must be greater than 0

3. Javítsd a hibakezelést

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

Az egyedi hibának tárolnia kell a tehenek számát (Int32) és egy egyedi üzenetet (String), valamint az Expection metódusnak a következő formátumban kell sorosítania az adatokat:

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

Felvértezve az egyedi hibáddal, készítsd el a TheFarm.validate_number_of_cows 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.
TheFarm.validate_number_of_cows(5)
# => nil

TheFarm.validate_number_of_cows(-5)
# => InvalidCowsError: -5 cows are invalid: there are no negative cows

Miután nagy munkával felállítottad ezt az ellenőrző függvényt, észreveszed, hogy már este van, úgyhogy otthagyod az íróasztalodat, és élvezed 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
Crystal Exercism

Készen állsz elkezdeni a(z) a-tanya feladatot?

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