Ruby po kapkách (11.) – definice tříd
Minule jsme si ukázali základy definování vlastních tříd a předvedli jsme si, jak vytvořit přístupové metody k proměnným, které jsou jinak z vnějšku třídy nedostupné. Dnes se podíváme, jak pomocí vlastních přístupových metod oddělit vnitřní fungování třídy od služeb, které nabízí aplikace.
Předpokládejme, že se má námi vyvíjená hra prodávat zejména v USA. Až doposud se proto veškerá manipulace s hmotností odehrávala v librách. Z důvodu rozšíření fyzikálních výpočtů, které chceme implementovat ve třídě Robot
, by se nám však hodilo, aby byla hmotnost uvnitř třídy reprezentována v kilogramech. Všechen kód, který třídu Robot
používá však pracuje s librami. Jak tento problém jednoduše vyřešit? Stačí nám vhodně předefinovat metody pracující s proměnnou @mass
.
class Robot # nová definice třídy Robot, verze 3
LB_KG = 0.454 # konstanta pro přepočet liber na kg (libra je cca 0.454kg)
def initialize(xpos, ypos, mass) # inicializace proměnných
@xpos = xpos
@ypos = ypos
@mass = mass * LB_KG # hodnota přijde v librách, přepočítáme ji na kg
end
acoder_reader :xpos, :ypos # běžné přístupové metody pro čtení
acoder_writer :xpos, :ypos # a zápis
def mass # přístupová metoda pro čtení ‚@mass‘
@mass / LB_KG # přepočteme zpět z kg na libry
end
def mass=(mass) # přístupová metoda pro zápis ‚@mass‘
@mass = mass * LB_KG # přepočteme z liber na kg
end
def to_s
„X = #{@xpos}, Y = #{@ypos}, M = #{@mass}kg“ # kontrolní výpis ponecháme v kg
end
end
Vnitřně je hmotnost ukládána v kilogramech a vnější kód pracuje s librami. Přístupové metody provádějí potřebné konverze. Protože jsme si díky zapouzdření jisti, že k proměnným nelze přistupovat přímo, nemusíme se obávat problémů, kdybychom se v budoucnosti rozhodli převést vnitřní fungování zpět na libry.
V příkladu jsem zavedli konstantu LB_KG
. Podle konvence je v Ruby název konstanty psán velkými písmeny. Tato konstanta není logicky přiřazena k instanci, ale k celé třídě, protože je pro všechny instance stejná. Jak uvidíme je přístupná dokonce i mimo třídu a prakticky tak v kombinaci se jménem třídy funguje jako globální konstanta. (Mimochodem dalo by se říci, že definováním třídy je definována konstanta stejného jména jako má třída.) Stojí za zmínku, že neměnnost konstanty v Ruby není vynucena. Interpret zobrazí při pokusu o změnu hodnoty konstanty pouze varování.
a = Robot.new(7, 8, 5) # vytvoříme novou instanci
puts a.to_s # výpis proměnných ukazuje vnitřní hodnotu v kg
puts a.mass # přístupová metoda ukazuje naopak hodnotu v librách
puts Robot::LB_KG # konverzní konstanta je pomocí ‚::‘ přístupná mimo třídu
puts Math::PI # na ukázku: konstanta vestavěného modulu obsahující pí
Po spuštění kódu získáme následující výpis:
X = 7, Y = 8, M = 2.27kg
5.0
0.454
3.141592654
Kromě konstanty sdílené všemi instancemi (a celým programem) lze zavést i proměnnou, která bude sdílena všemi instancemi. Říká se jí „class variable“ či proměnná třídy (v některých jazycích se také můžete setkat s označením statická proměnná). Název proměnné společné pro všechny instance třídy začíná v Ruby dvěma znaky ‚@@‘. Klasickým školním příkladem použití sdílené proměnné je počítání vzniklých instancí.
class Robot # definice třídy ‚Robot‘, verze 4
LB_KG = 0.454
@@count = 0 # class variable je nutné inicializovat v definici třídy
def initialize(xpos, ypos, mass)
@xpos = xpos
@ypos = ypos
@mass = mass * LB_KG
@@count += 1 # při každé nové instanci přičteme k počitadlu 1
end
#
# definice přístupových metod zůstává beze změny (lze ji zkopírovat
# z minulého příkladu)
#
def to_s
„X = #{@xpos}, Y = #{@ypos}, M = #{@mass}kg, C = #{@@count}“ # doplníme ‚@@count‘
end
end
Snadno vyzkoušíme:
a = Robot.new(7, 8, 5)
b = Robot.new(1, 2, 3)
c = Robot.new(2, 3, 4)
puts a.to_s
Výsledek je podle očekávání:
X = 7, Y = 8, M = 2.27kg, C = 3
Jak nejspíše tušíte, do třetice všeho dobrého existují i metody svázané s třídou – takzvané „class methods“ neboli metody třídy (opět se můžete setkat i s označením statické metody). S jednou jsme se již setkali. Několikrát jsme použili metodu new
. Metoda new
nemůže být metodou objektu, protože jej teprve vytváří. Na druhé straně musí být svázána s konkrétní třídou, protože postup vytváření objektů různých tříd se může lišit.
Nadefinujme si metodu třídy Robot, která zobrazí obsah proměnné @@count.
def Robot.count # definujeme metodu s názvem ‚Třída.metoda‘
@@count # vrací hodnotu počitadla
end
Co se týče zapouzdření není možné přímo přistupovat ani k proměnným třídy. Opět musíme využít metod. Metoda třídy má přístup k proměným třídy, logicky však nemůže přistupovat k žádným proměnným instancí, které ve chvíli volání ani nemusí existovat. Naopak metody instancí mohou přistupovat k proměnným dané instance i k proměnným třídy. Ověříme funkčnost námi definované metody.
a = Robot.new(7, 8, 5)
b = Robot.new(7, 8, 5)
c = Robot.new(7, 8, 5)
puts Robot.count # voláme v podobě ‚Třída.metoda‘
Výsledek je podle očekávání:
3
Uveďme si ještě příklady. Instance vestavěné třídy File
reprezentuje otevřený soubor. Otevřený soubor však nelze korektně smazat. Mazání tedy nemůže být realizováno metodou instance. Proto existuje metoda třídy File.delete
, která smaže soubor, jehož název dostane jako parametr. Stejně tak metoda File.open
otvírá soubor a teprve vytváří instanci třídy. Je to vlastně synonymum pro metodu File.new
. A metoda new
, jak jsme již uvedli, je typickou ukázkou metody svázané se třídou.
Starší komentáře ke článku
Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.
Mohlo by vás také zajímat
-
Zabezpečení e-mailů: Jak můžete chránit vaši firemní komunikaci
13. prosince 2023 -
Fandíme českým sportovcům a rozdáváme hosting ZDARMA!
26. července 2024
Nejnovější
-
Výkonný a kompaktní: ASOME Max Studio s výjimečným poměrem cena/výkon
11. listopadu 2024 -
Šokující data od Microsoftu: Kyberútoky rostou o stovky procent!
8. listopadu 2024 -
Chcete jedinečnou doménu? Objevte koncovky FOOD, MEME a MUSIC!
7. listopadu 2024 -
OpenAI představilo novou funkci ChatGPT Search
6. listopadu 2024