vod do objektovho programovn

Transkript

vod do objektovho programovn
Úvod do objektově
orientovaného
programování s použitím
jazyka C# pro střední
školy
Učebnice je určena pro střední školy k volnému šíření (FREE)
autor RNDr. Ilja Kraval, 2006-2007, www.objects.cz
Tato učebnice vznikla za účasti a pomoci studentů středních škol Gymnázia
Valašské Klobouky a Gymnázia Jana Pivečky Slavičín
Úvod do objektového programování s použitím jazyka C#
© Ilja Kraval, 2006
1. Co je objektové programování a
jak funguje?
Funkce, data a objekty
Pokud píšete program, tak jeho podoba hodně záleží na prostředí (jazyce) ve kterém
je program napsán. Program můžeme napsat například v C#, v Pascalu, v Delphi, v
Javě, v C++ atd. Takovýchto programovacích jazyků je nepřeberné množství, ale i
tak máme na výběr ze dvou základních filosofií, jak program napsat: Buď pomocí
objektů anebo bez nich. Pokud napíšeme program pomocí objektů, hovoříme o
objektově orientovaném programování a pokud napíšeme program bez objektů,
hovoříme o strukturálním programování (tj. ne-objektově).
Otázka zní: Jaký je tedy rozdíl mezi ne-objektovým, tedy strukturálním
programováním, a objektovým programováním?
Strukturální programování
Strukturální, tedy ne-objektové programování, je považováno za zastaralé. Je
postaveno na myšlence, že jakýkoliv program se vlastně skládá ze dvou základních
typů prvků:
1. buď se jedná o prvek reprezentující tak zvaná DATA
2. anebo se jedná o prvek reprezentující tak zvané FUNKCE.
Vysvětleme si, co budeme pod těmi dva pojmy chápat. K tomu nám nejlépe poslouží
starý dobrý známý jazyk - Pascal.
DATA
Pod prvkem DATA rozumíme obecně to, co umí držet hodnotu, konkrétně v Pascalu:
•
proměnné v paměti zavedené pomocí rezervovaného slova var
strana 2
Úvod do objektového programování s použitím jazyka C#
© Ilja Kraval, 2006
•
vstupní a výstupní parametry funkcí
•
data stálá, uložená na disku počítače (data v souborech, data v databázi
apod.), která můžeme tzv. číst
DATA jako proměnné v paměti jsou dočasnými proto, protože jejich obsah zmizí po
vypnutí počítače anebo přesněji po ukončení programu, anebo se jedná proměnné
„stálé“ (persistentní) - uložené na disk, které zůstávají v počítači i po jeho vypnutí
(obsah souborů, obsah databáze apod.).
Základní schopností prvku DATA ve strukturálním programování je držet
hodnoty svého obsahu (buď pouze v běhu programu anebo stále uložená na
disku)
FUNKCE
Druhým typem prvku strukturálního programu jsou tzv. FUNKCE, což jsou obecně
výkonné části programu. Chápeme pod nimi v syntaxi Pascalu prvky definované
rezervovaným slovem procedure, function anebo tělo programu (resp. unit).
Obecněji i mimo Pascal se jedná také o spustitelné skripty, strojový kód apod., tj.
vše, co v programu něco dělá. Na rozdíl do této schopnosti „něco vykonávat“ DATA
ve strukturálním programu nedělají nic, mají pouze schopnost držet hodnoty.
Posláním prvku FUNKCE je pracovat s prvky DATA a „to se nazývá programem“.
Ve strukturálním programu obecně platí, že DATA udržují hodnoty, se kterými
pracují FUNKCE.
Příklad strukturálního programu v Pascalu
program prg1;
strana 3
Úvod do objektového programování s použitím jazyka C#
© Ilja Kraval, 2006
var A,
//rozsah nahodneho cisla 1 az A
N,
//pocet hodu pro test
i
//citac cyklu
:integer;
function rand1toA(aA:integer):integer;
//generuje nahodne cislo od 1 do aA
begin
rand1toA := round(random(aA))+1;
end;
{test: Zada se rozsah nahodneho cisla
a pocet pokusu.
Postupne se vypisuji vysledky - nic vic}
begin
//test...
Randomize;
write ('Zadej rozsah cisel pro generaci od 1 do : ');
readln (A);
write ('Zadej pocet pokusu : ');
readln (N);
for i:=1 to N do
begin
write ('vygenerovane cislo : ',rand1toA(A),' stiskni enter');
readln;
end;
end.
Tento zkušební program, jak vidět, zavádí funkci pro generaci náhodného čísla od 1
do A a testuje ji.
strana 4
Úvod do objektového programování s použitím jazyka C#
© Ilja Kraval, 2006
Podívejme se na prvky tohoto strukturálního programu pohledem, co spadá po pojem
FUNKCE a co pod pojem DATA.
•
Hned na začátku jsou definovány tři proměnné typu integer. Ty patří pod
DATA (drží hodnoty typu integer).
•
Druhá část zavádí funkci rezervovaným slovem function , typický prvek
výkonné části programu, tj. FUNKCE („funkce pracuje s daty“)
•
Při zavedení prvku function jsou zavedeny proměnné jako vstupní a
výstupní parametry. Vstupním parametrem je integer rozsahu a výstupním
je náhodné vygenerované číslo. Tyto dva prvky (vstupní a výstupní
parametry) spadají pod DATA.
•
Samotné tělo programu (tj. begin – end v programu) spadá pod prvek
FUNKCE, tj. jedná se o výkonnou část, pracuje s daty a provádí test.
Nakonec je dobré si uvědomit, že strukturální program jinak pracovat neumí!
Ve strukturálním programu zavádíme proměnné (v různých pozicích programu)
na straně jedné a funkce a procedury, které s nimi pracují na straně druhé. Tak
je tvořen funkční strukturální program.
strana 5
Úvod do objektového programování s použitím jazyka C#
© Ilja Kraval, 2006
Objektově orientované programování
Objektový program pracuje jinak, je postaven na jiné filosofii než strukturální
programování. V objektovém programu neexistují funkce a data, místo nich se
zavádí tzv. objekt. Jak vypadá a pracuje jeden objekt?
Skladba objektu
Jeden objekt je jednou uzavřenou strukturou v programu, do které není zvně vidět.
Znamená to, že okolí objektu (tj. okolní program) nemůže přistupovat k vnitřku
objektu. Tím se logicky program v objektovém programování dělí na dvě části: Na
vnitřek objektu a vnějšek objektu. Každý kód v programu je umístěn buď mimo objekt
anebo uvnitř objektu.
Dovnitř objektu není vidět
To, že do objektu není zvně vidět, znázorníme na obrázku jako červené kolečko,
oblast uvnitř objektu označíme jako vnitřek objektu, okolí jako vnějšek objektu:
vnějšek objektu
vnitřek objektu
obrázek 1 Do objektu není z okolního programu vidět a program, tedy kód, je
rozdělen na dvě oblasti: uvnitř a vně objektu
strana 6
Úvod do objektového programování s použitím jazyka C#
© Ilja Kraval, 2006
Uvnitř objektu (tj. v oblasti, která je zvenku nepřístupná a není zvenku vidět) se
nachází tzv. vnitřní paměť objektu a vnitřní metody objektu. Vnitřní paměť slouží
k „zapamatování a podržení hodnot v objektu“, vnitřní metody jsou určeny k chování
a práce s hodnotami v paměti objektu.
Objekt obsahuje v sobě dvě části: vnitřní paměť objektu (část umožňující držet
hodnoty) a vnitřní metody objektu (umožňuje objektu pracovat s pamětí a měnit
její hodnoty)
Skladbu objektu si můžeme znázornit obrázkem:
vnitřní paměť objektu
vnitřní metody
objektu
obrázek 2 Uvnitř objektu se nacházejí vnitřní paměť a vnitřní metody objektu, zvenku
nepřístupné
Vnitřní paměť - atributy objektu
První část objektu, vnitřní paměť objektu, je nositelem struktury objektu a nazývá se
také atributy. Objekt jako struktura tedy obsahuje něco jako své „vnitřní proměnné“,
které reprezentují paměť objektu. Objekt je proto schopen držet své hodnoty ve své
strana 7
Úvod do objektového programování s použitím jazyka C#
© Ilja Kraval, 2006
paměti. Říkáme také shodně, že objekt si drží své stavy. Podobnost se strukturálním
programováním je taková, že pro vnitřek objektu se atributy chovají stejně jako
proměnné ve strukturálním programování. Rozdíl oproti strukturálnímu programování
je v předešlé větě ve slovíčku „pro vnitřek objektu“, protože uvnitř objektu jsou tyto
proměnné neboli atributy viditelné, ale mimo objekt nikoliv. Uvnitř objektu tedy
existují atributy - obdoba proměnných, se kterými objekt může pracovat, ale může
s nimi pracovat pouze on a nikdo jiný (něco jako soukromé proměnné daného
objektu).
První součástí objektu jsou členy - atributy, které si mohou pamatovat hodnoty
a reprezentují tak vnitřní paměť objektu, zvně nejsou přístupné, jsou pro objekt
soukromé, neboli privátní.
Vnitřní metody objektu
Druhou částí objektu jsou vnitřní metody. Jsou to výkonné části objektu, podobně
jako ve strukturálním programování funkce a procedury. Na rozdíl od nich jsou však
vnitřní metody umístěny uvnitř objektu a jsou také zvenku neviditelné. Jejich
posláním je provádět chování objektu. Vnitřní metody, když už jsou spuštěné (pozn.:
zatím nevíme jak), mají zpřístupněnu vnitřní paměť objektu, dobře ji vidí a pracují
s ní. Průběh vnitřní metody mění stavy objektu, tj. hodnoty v atributech.
Druhou součástí objektu jsou vnitřní metody, tj. výkonné části objektu, které
když jsou spuštěny, pracuji s vnitřní pamětí, tj. atributy a mění jejich hodnoty.
Doplníme předešlý obrázek ukazující skladbu objektu ještě o ten fakt, že vnitřní
metody pracují s vnitřní pamětí:
strana 8
Úvod do objektového programování s použitím jazyka C#
© Ilja Kraval, 2006
obrázek 3 Vnitřní paměť je přístupná vnitřním metodám a ty s ní pracují, čtou z ní a
mění hodnoty v ní
Protokol zpráv objektu
Zatím jsme zavedli objekt jako takovou strukturu, ve které jsou obsaženy vnitřní
paměť a vnitřní metody, které nejsou zvenku vidět. Pokud bychom takovou strukturu
někomu nabídli k použití, moc by z ní neměl, protože by z ní ve svém programu nic
neviděl a použít by ji nemohl (viz červené kolečko na obrázku).
Ke spolupráci s objektem slouží další část objektu, která se nazývá protokol zpráv.
Objekt obsahuje (kromě vnitřní paměti a vnitřních metod) také protokol zpráv, který
umožňuje spolupráci okolí s objektem. Protokol zpráv pracuje jako převodník
(poznámka: převodník převádí něco na něco).
V objektovém programování objekt může přijímat zprávy od okolí. V protokolu zpráv
jsou ke zprávám přiřazeny vnitřní metody a to tak, že k jedné zprávě, kterou objekt
může přijmout, je přiřazena jedna vnitřní metoda. Okolí může použít objekt tak, že
objektu pošle zprávu, ten si ji najde ve svém protokolu zpráv, tam zjistí, která vnitřní
metoda je k této zprávě přiřazena a tu spustí. Mechanismus použití objektu tedy
funguje takto:
1. program z okolí chce použít objekt
2. program z okolí pošle objektu zprávu
3. objekt vyhledá přijatou zprávu v protokolu zpráv
4. objekt spustí vnitřní metodu, která je přiřazena k této zprávě v protokolu zpráv
Tento mechanismus si můžeme zobrazit pomocí obrázku:
strana 9
Úvod do objektového programování s použitím jazyka C#
© Ilja Kraval, 2006
zpráva Z1
Z1
M1
M1
obrázek 4 Okolí posílá zprávu objektu (zde jako zelená šipka Z1), ten ji identifikuje v
protokolu zpráv (žluté Z1), najde k ní přiřazenou vnitřní metodu (zde bleděmodré M1)
a tu spustí
Tomuto mechanismu „poslání zprávy a vyvolání metody“ se zkráceně říká zavolat
metodu objektu.
Množině zpráv, kterou objekt může přijmout (a kterou tedy nabízí okolí k použití), se
také říká interface (nebo česky rozhraní) objektu.
Tomu, kdo posílá zprávu objektu a používá tak nabízenou službu objektu, se říká
klient objektu.
Okolí spolupracuje s objektem tak, že objektu pošle zprávu. Objekt tuto zprávu
nalezne v protokolu zpráv a tam také najde, která vnitřní metoda je k této
zprávě přiřazena a tu spustí.
Zapouzdření
Všimněme si na mechanismu posílání zpráv. Soustřeďme se na klienta. Ten má
zpřístupněn objekt, který může použít. Otázkou je, co z tohoto objektu klient vlastně
vidí a co může použít? Klient vidí pouze interface a jediná možnost, jak
spolupracovat s objektem, je poslat mu zprávu. Jinou možnost nemá. Tomuto jevu se
říká zapouzdření (encapsuloation).
Jevu, kdy klient vidí pouze interface objektu a nic jiného z objektu (a má proto
jedinou možnost, jak spolupracovat s objektem, zaslat mu zprávu) se nazývá
zapouzdření objektu (object encapsulation).
strana 10
Úvod do objektového programování s použitím jazyka C#
© Ilja Kraval, 2006
Objekty v objektech
Součástí vnitřní paměti objektu může být jednoduchá proměnná tzv. datového typu
(tj. proměnná typu string, integer atd.), anebo vnitřní paměť může obsahovat jiný
objekt, kterému daný objekt může poslat zprávu. Součástí vnitřní paměti objektu tedy
mohou být opět objekty. Schéma, kdy objekt posílá zprávu jinému objektu ve své
vnitřní paměti, potom může namalovat takto:
zpráva Z2
Z2
M2
M2
M1
zpráva Z1
B
Z1
M1
M1
A
obrázek 5 Objekt (zde A) může v běhu své vnitřní metody poslat zprávu jinému
objektu (zde B)
Sledujme předešlý obrázek zleva doprava:
Někdo posílá (zleva) zprávu Z1 objektu A. Objekt A zprávu Z1 identifikoval a našel
k ní metodu M1 (viz žlutá část protokolu zpráv). Následně objekt tuto metodu M1
spustí (viz šipka k metodě M1, bleděmodrý obdélník). Metoda běží (jakože shora
dolů) a v určitém momentu posílá zprávu Z2 jinému objektu, zde objektu B (viz šipka
strana 11
Úvod do objektového programování s použitím jazyka C#
© Ilja Kraval, 2006
ke zprávě Z2 z metody M1 k protokolu práv objektu B). Objekt B tuto zprávu
identifikuje jako Z2 a dál je mechanismus zřejmý, objekt B na tuto zprávu spustí
metodu M2.
Obrázek sice další zprávy nezobrazuje, ale dovedeme si představit, že přesně
takovýmto mechanismem může pokračovat běh programu dále: Uvnitř metody M2
objektu B by se pošle zpráva dalšímu objektu C atd. atd.
Všimněme si, že klientem objektu B (ten, kdo posílá zprávu objektu B) je zde objekt
A. To je charakteristický mechanismus v objektovém programování.
Pro objektově orientovaný program je charakteristické, že klientem objektu je
jiný objekt. Objekt posílá zprávu objektu, ten spustí metodu a následně posílá
zprávu dalšímu objektu, ten dalšímu a tak dál … a tak běží objektově
orientovaný program.
Co se stane, když objekt zprávu nezná?
Otázkou je, co se stane, když napíšeme takový kód, ve kterém klient objektu pošle
objektu zprávu a on ji nezná. Jinak řečeno, otázkou je, co nastane, když ji nenalezne
ve svém protokolu zpráv? Zde velmi záleží na tom, v jakém jazyce a prostředí
píšeme program. Některá prostředí na tuto situaci reagují jinak než jiná prostředí.
Zajímá nás samozřejmě nejprve, jak reaguje C#.
Pokud se pokusíme poslat objektu zprávu v C# a objekt tuto zprávu nezná, tak se
nám nepodaří program ani zkompilovat, tj. nepodaří se nám jej ani připravit ke
spuštění. Pokud napíšeme kód v C# a posíláme objektu zprávu, k vybudování celé
konstrukce nutné pro vyvolání metody v programu ze zdrojového kódu dochází již při
kompilaci programu. Kompilátor zjistí, že „něco tady nehraje“, tedy že objekt tuto
zprávu nezná, a kompilace skončí chybou.
Poznámka: Existuje jedna jediná výjimka tohoto mechanismu v C# při jednom
určitém velmi specifickém a výjimečném postupu tzv. přetypování dolů –tzv.
„downcast“, což budeme probírat mnohem později. Tato výjimka je z hlediska pojetí
C# nezajímavá a uvedený postup přetypování dolů (pokud se mu dá vyhnout) se
dokonce vřele nedoporučuje. Z hlediska kontroly programu víme už proč – chceme
vidět případné nesprávné volání objektu co nejdříve ☺ ). K této jediné možné
výjimce, která se navíc nedoporučuje, se vrátíme později.
strana 12
Úvod do objektového programování s použitím jazyka C#
© Ilja Kraval, 2006
Obecně se takovému způsobu budování poslání zprávy objektu, kdy se dopředu
kontroluje, zda objekt zprávu umí, říká „early binding“.
Pokud používáte prostředí Visual Studio, tak vývojové prostředí vás pomocí
mechanismu „chytrého napovídání“ při psaní kódu informuje, které zprávy který
objekt zná. Pokud se pokusíte napsat chybný kód, kdy objekt nezná jemu zaslanou
zprávu, upozorní vás na to editor dokonce ještě před kompilací.
V jiných prostředích (nikoliv C# !), kde se nekontroluje existence zprávy u objektu
dopředu, dochází k této kontrole až při běhu programu. Znamená to, že v okamžiku,
kdy v takovém prostředí (nikoliv C# !) spouštíte program, tak program ještě neví, zda
zpráva v objektu bude nebo nebude nalezena. Až teprve v okamžiku, kdy se klient
pokusí poslat objektu zprávu, dojde k tomuto vyhodnocení a pokud zpráva není
nalezena, tak v té chvíli opět záleží na prostředí: některé prostředí nahlásí chybu a
program nepokračuje dále a končí chybou, v jiných prostředích objekt na takovou
neznámou zprávu odpoví „já ti nerozumím“ a program běží dále.
Obecně se takovému způsobu budování poslání zprávy objektu, kdy se dopředu
nekontroluje, zda objekt zprávu umí, a zjišťuje se to až v běhu programu, říká „late
binding“.
V každém případě je pro tvorbu programu velmi výhodné, pokud vás kompilátor
anebo dokonce vývojové prostředí upozorní, že objekt nezná danou zprávu, kterou
mu chceme poslat, tj. je dobré, jak to dělá VS. Vyvarujeme se tak velmi mnoha
zbytečných chyb hned v zárodku.
strana 13
Úvod do objektového programování s použitím jazyka C#
© Ilja Kraval, 2006
2. Princip opětovné použitelnosti
Další pojem, který si potřebujeme v OOP vysvětlit, je třída, neboli class. K tomu,
abychom velmi dobře pochopili, jak funguje třída neboli class v OOP, musíme si
nejprve velmi dobře vysvětlit jeden důležitý mechanismus, který se vine celým
programováním jako červená niť. Pochopení tohoto jevu do všech důsledků nás
v budoucnu ušetří od spousty problémů. Tento jev se nazývá opětovná použitelnost.
V programování existuje „pěkný kód“ a „škaredý kód“
Pokud byste psali kód již delší dobu, tj. pracovali několik let jako programátoři, určitě
byste časem zjistili, že existují programy napsané velmi pěkně a také byste zjistili, že
oproti tomu existují programy, které jsou napsány velmi škaredě. Nemluvíme teď o
tom, že jeden kód (pěkný) je funkční a druhý kód (škaredý) není funkční. Oba kódy,
tj. jak pěkný, tak i škaredý, jsou z hlediska zadání dobře fungující, tedy oba kódy
splňují zadání. A přesto se rozlišují! Otázkou je, co činí pěkný kód pěkným a co činí
škaredý kód škaredým, když oba fungují tak, jak mají.
Poznámka: Někdy se dokonce o velmi pěkně napsaném programu hovoří jako o
„elegantním kódu“ a o programátorovi, který jej vytvořil, se hovoří jako o „mistrovi“.
Na druhé straně se vám může dostat do ruky kód, který je hodně vzdálen od kódu
s elegancí. Jedná se o velmi škaredě napsaný kód (i když je funkční). Takový kód
bývá označován velmi hanlivými výrazy, mnohdy dokonce i vulgárními. Nejslušnější
výraz bývá většinou například „paskvil“ anebo hodně tvrdě jako „čuňácký kód“
(protože to mohlo napsat jenom „čuně“).
Čím to je, že když programy splňují svá zadání, tedy jsou prakticky použitelné, tak
jeden je pěkný a druhý škaredý? Čím se tedy liší, když z hlediska užití jsou
ohodnoceny jako „splňující zadání“? Asi není třeba zdůrazňovat, že je dobré zjistit
hned na začátku výuky o objektově orientovaném programování, jak psát pěkné a
elegantní programy a jak nepsat programy škaredé.
Pokud budete pracovat jako programátoři, velmi brzy a záhy zjistíte vnější rozdíly
mezi pěkným a škaredým kódem, tj. jak se projeví rozdíl navenek, tedy jak poznáte,
že toto je nebo není „paskvil“. Pěkný kód je dobře přehledný, dobře čitelný, a i když
řeší složité věci, je jednoduchý. Škaredý kód je nečitelný, nepřehledný a je zbytečně
složitý. Není to však jenom otázka dobrých poznámek a komentářů v kódu (i když i ty
jsou velmi důležité). Hlavní rozdíl je jinde, rozdíl spočívá ve skladbě kódu. Pěkně
napsaný kód (oproti škaredému) je dobře čitelný již svou podstatou, tj. jeho
strana 14
Úvod do objektového programování s použitím jazyka C#
© Ilja Kraval, 2006
konstrukce je taková, že princip kódu je zřetelně vidět dokonce s minimem
poznámek. Škaredý kód můžete vybavit spoustou komentářů a poznámek a přesto
zůstane nečitelným!
Při psaní programu se může řešení daného problému napsat buď jako řešení s
„kódem pěkným“ anebo jako řešení s „kódem škaredým“. Vyvarujme se psaní
„škaredého kódu“, i když jeho kód je funkční. Funkční kód se snažíme psát
jako „kód pěkný a elegantní“. Pěkný a elegantní kód je čitelný již svou
podstatou takřka bez poznámek a komentářů. Škaredý kód i přes spoustu
poznámek zůstává těžko čitelný…
Základní odpověď na otázku, jak nepsat „škaredý kód“, najdete v následující
kapitolách.
Princip opětovné použitelnosti při volání funkcí
I když se jedná o obecný princip, vysvětlíme si jej na již známém starém dobrém
Pascalu. Poté si tento princip zobecníme.
Představme si, že píšeme nějakou funkci v Pascalu, označme ji třeba jako funkce F1.
Poté napíšeme druhou funkci F2, která vykonává něco jiného. Představme si, že
jsme zjistili, že jak uvnitř funkce F1, tak uvnitř funkce F2 existuje nějaká pasáž
stejného kódu, tj. stejný kód se opakuje v obou funkcích. Graficky bychom tuto
situaci mohli označit takto:
strana 15
Úvod do objektového programování s použitím jazyka C#
© Ilja Kraval, 2006
function F1
function F2
stejný kód
obrázek 6 V kódu funkcí F1 a F2 se opakuje stejný kód
Otázkou je, jak k tomuto jevu mohlo dojít?
První z možností je, že jsme kód prostě zkopírovali přes schránku (clipboard). Jedná
se o mnohdy používaný postup, který je v přímém rozporu s principem, který se teď
snažíme vysvětlit, a raději se mu vyhneme. Kopírování kódu je zaručený postup, jak
učinit kód „škaredým“.
Druhá možnost, jak k opakování kódu mohlo dojít, spočívá v tom, že jsme si tohoto
efektu shody kódu prostě nevšimli. Kód jsme sice psali dvakrát, ale obě části se liší
v detailech, jako jsou například jiné názvy proměnných apod. Shoda kódu je v tomto
případě zamaskovaná přes detaily kódu. Pokud se však zamyslíme nad tím, co
vlastně dělá „zelené“ ve funkci F1, následně se zamyslíme nad tím, co dělá „zelené“
v F2, zjistíme, že je to vlastně totéž, i když názvy všeho možného v kódu jsou
zavedeny různě.
Poznámka: K této situaci může dojít zejména v týmové spolupráci, kdy jeden
programátor píše funkci F1 a druhý píše funkci F2 a neví o sobě, nemají nic, co by je
spojovalo a proto se části kódu zopakují. Otázka týmové spolupráce je však problém
hodně složitý a určitě mimo rozsah této knihy (otázka modelování SW apod.).
Naštěstí jsme nyní v situaci, kdy funkci F1 i funkci F2 jsme napsali my sami a kód
obou funkcí máme pod kontrolou pouze my a nikdo jiný. Jak se vyhnout opakování
v kódu i v týmu je předmětem „vyšší školy programování“ a hlavně „modelování
softwaru“.
Situaci, kterou popisuje předešlý obrázek , tj. kdy se kód opakuje, budeme zkráceně
nazývat situací „před vytknutím“.
strana 16
Úvod do objektového programování s použitím jazyka C#
© Ilja Kraval, 2006
Jak známo, Pascal umožňuje řešit předešlou situaci s opakujícím se kódem ve
funkcích. V tomto jazyce existuje možnost zavolat funkci a toho využijeme. Uvedené
opakující se části kódu vytkneme, tedy doslova „vytrhneme“ z pozic, kde se opakují.
Zavedeme třetí funkci F3 a ono „zelené a opakující se“ vložíme do této třetí funkce.
V bodech, kde se původně kód opakoval, zavedeme zavolání této funkce F3. Po této
operaci přeskupení kódu (přeskupení kódu se nazývá také „refactoring kódu“) se
předešlý obrázek změní na následující:
function F1
function F2
call function F3
původně
stejný kód
function F3
obrázek 7 Situace po vytknutí stejného kódu
Všimněte si několika zajímavých skutečností při porovnání obou situací, tedy situace
před vytknutím (kód se opakuje) a po vytknutí (kód je ve společné funkci F3).
Pokud bychom sledovali chod funkce F1 (mimochodem stejně tak F2), tak bychom
zjistili, že v obou případech (tj. jak před vytknutím, tak po vytknutí) se funkcionalita v
podstatě nezměnila. V prvém případě před vytknutím (viz „obrázek 6 V kódu funkcí
F1 a F2 se opakuje stejný kód“) se vykoná posloupnost příkazů ve funkci F1 až po
„zelené“, poté se pokračuje normálně dále uvnitř F1 v „zeleném“, tj. vykoná se vše
v „zeleném“ uvnitř F1, poté se dokončí chod kódu za „zeleným“.
Pokud provedeme vytknutí (viz „obrázek 7 Situace po vytknutí stejného kódu“),
potom se situace příliš nezmění: Provede se posloupnost kódu v F1 až po volání
strana 17
Úvod do objektového programování s použitím jazyka C#
© Ilja Kraval, 2006
funkce F3, zavolá se funkce F3, provede se „zelené“ uvnitř funkce F3 (tj. begin – end
v této funkci) a až tato funkce skončí, vrátím,e se do funkce F1 a dokončí se zbytek
ve funkci F1. Tato situace se liší pouze tím, že se kousek kódu („zelený“) umístil
jinam a zavolal se, posloupnost příkazů je však stejná.
Důsledkem této skutečnosti je jeden zajímavý fakt: Představme si někoho, kdo bude
volat funkci F1 (na předešlých obrázcích není zobrazen). Vidí její hlavičku a zavolá
tuto funkci F1. Tomuto uživateli funkce F1 je úplně jedno, zda došlo nebo nedošlo
k vytknutí, tj. zda je program navržen podle schématu „před vytknutím“ anebo „po
vytknutí“. V obou případech se totiž vykoná stejná posloupnost příkazů programu,
jenom v případě volání funkce F3 se provede jeden „skok“ navíc – zavolání funkce
F3.
Zavedení funkce F3 je ukázkou využití principu opětovné použitelnosti. Funkce F3
nabízí pro okolí možné zavolání, tedy nabízí službu zavolání komukoliv, kdo ji
potřebuje. Funkce F3 je v programu tzv. opětovně použitelná.
Princip opětovné použitelnosti obecně platný
Mechanismus opětovné použitelnosti jsme si vysvětlili na volání funkcí, avšak
v programování má obecnější charakter. Je dobré tento princip znát v obecné rovině.
Má to jeden prostý důvod: Jak si za chvíli ukážeme, čím více budete dbát na princip
opětovné použitelnosti, tím bude váš kód elegantnější a naopak, čím méně bude váš
kód opětovně použitelný, tím bude „škaredší“.
Zkusme tedy najít obecný vzorec opětovné použitelnosti. Představme si, že si
prohlížíme nějaký program a v něm nalezneme nějaké dva prvky, označme je A a B.
Je třeba zdůraznit, že těmito dvěma prvky může být z hlediska programování
„cokoliv“, v předešlém příkladu se jednalo o dvě funkce. Nyní tuto situaci
zobecňujeme a nemusí se jednat pouze o funkce. Představme si, že jsme zjistili, že
v těchto dvou prvcích A a B se naleznou nějaké jejich části, které jsou shodné.
Uvedenou situaci si můžeme znázornit tímto obrázkem:
B
A
obrázek 8 V prvcích A a B softwaru je identifikována opakující se část (opakující se
klikyhák)
strana 18
Úvod do objektového programování s použitím jazyka C#
© Ilja Kraval, 2006
Opakující se část je zde zobrazena jako klikyhák. Dále si představme, že je
umožněna interakce mezi prvky taková, že jeden prvek může použít druhý prvek (u
funkcí je to možnost zavolání jedné funkce druhou). V tom případě můžeme
předešlou situaci převést na jinou, z hlediska opětovné použitelnosti lepší. Klikyhák
„vytkneme“ do společně používaného prvku. Provedeme to tak, že zavedeme nový
prvek C, do něj umístíme opakující se klikyhák a zavedeme interakci použití prvku C
prvky A a B:
B
A
C
obrázek 9 Zavedení prvku C, který je používán – aplikace opětovné použitelnosti
Pokud jsme pochopili princip opětovné použitelnosti na volání funkcí, není určitě
těžké pochopit obecný mechanismus uvedený na předešlých dvou obrázcích. Stačí
prvky A, B a C zaměnit za funkce F1, F2 a F3 a představit si volání funkcí.
Jediné, co teď ještě nemusíme chápat, je otázka, kde se všude ještě můžeme setkat
s takovouto situací (nežli jen u funkcí)? V dalším výkladu budeme tyto situace prostě
nalézat.
Princip opětovné použitelnosti umožňuje přeskupit kód tak, že se části kódu
v programu neopakují. K tomu, aby bylo možné zavést mechanismus opětovné
použitelnosti, je třeba, aby existovala interakce použití umožňující použití
prvku prvkem. Tato interakce použití umožňuje operaci „vytknutí“ opakujících
se částí do jednoho používaného prvku, který je poté používán z různých částí
programu - tím se zamezí opakování částí kódu.
strana 19
Úvod do objektového programování s použitím jazyka C#
© Ilja Kraval, 2006
Kladné důsledky použití opětovné použitelnosti
Všimněme si kladných důsledků použití opětovné použitelnosti na tvorbu a údržbu
programu. Porovnejme si proto dvě situace zavedené v mechanismu opětovné
použitelnost, tj. situace před vytknutím a situace po vytknutí.
Nejprve si ale zopakujme jeden velmi důležitý fakt: Obě situace, tj. jak situace před
vytknutím, tak situace po vytknutí, jsou dobře funkční. Máme proto možnost napsat
program jak podle prvého schématu (bez opětovné použitelnosti), tak podle druhého
schématu (s použitím opětovné použitelnosti). Obě situace se liší pouze
přeskupením kódu, nikoliv tím, že by jeden kód fungoval a druhý ne. Oba budou
fungovat. To mají obě situace společné.
Sledujme však nyní rozdíly, tj. co by se dělo, kdyby jeden programátor vyřešil tutéž
situaci tak, že by napsal kód pomocí opětovné použitelnosti a druhý programátor by
se opětovné použitelnosti při psaní kódu vyhnul. Jak tento rozdíl pocítí?
Jednoduchý, ale závažný rozdíl je v tom, že v situaci bez opětovné použitelnosti se
vyskytují opakující se části kódu. Víme již, že se nejedná o chybu zásadní, program
funguje, ale…! Co když najdeme v oné opakující se části chybu a chceme ji opravit?
Je zřejmé, že tuto chybu musíme opakovaně opravovat v různých částech programu.
Podobný problém nastane, pokud se nejedná o chybu, ale například o vylepšení
nebo o požadavek na změnu pro novou verzi programu apod. A opět máme problém
opakování změn! Je zřejmé, že tyto změny (ať už opravy anebo vylepšení) musíme
opakovaně provádět všude tam, kde se části kódu opakují. Asi není třeba
zdůrazňovat, že pokud máme program napsaný podle principu opětovné
použitelnosti, stačí opravit nebo změnit něco někde jednou a skončili jsme
s úpravami – nic se totiž neopakuje...
Prvním viditelným důsledkem skladby programu bez opětovné použitelnosti
jsou nepříjemné opakující se opravy a opakující se změny kódu při údržbě
programu anebo při vývoji nových verzí.
Tento důsledek je sice nepříjemný, ale není až tak hrozný. I když jsme sice „zdravě
líní“ a opakující se změny jsou pro nás proto nepříjemné, přece jen můžeme
namítnout: „No tak je to trochu pracnější. Když máme clipboard (schránku),
přeneseme také opravy…a je to. Zas až tak pracné to být nemusí!“
Ano, to může být pravda, až na to, že… Pokud máme kód napsaný bez použití
principu opětovné použitelnosti a tedy části kódu se opakují, tak musíme také velmi
strana 20
Úvod do objektového programování s použitím jazyka C#
© Ilja Kraval, 2006
dobře vědět, kde se ty části opakují! Zatím jsme si uváděli příklady se dvěma prvky A
a B, a přitom jsme si ono zmíněné opakování pěkně namalovali. Ale jakmile
připustíme možnost opakování částí kódu, máme problém: Takovýchto prvků, kde se
kód opakuje, může být více (a tedy nejenom dva!). Z toho plyne, že pokud
připustíme, aby se části kódu opakovaly, měli bychom si ihned bokem vést evidenci,
kde se ony opakující části vyskytují. Pokud to neučiníme, tak nastanou potíže a ne
malé: Jak budeme ony opakující se části v kódu hledat? Jak najdeme, kde se všude
to opakované vyskytuje? A tuto myšlenku doveďme do opravdu hrozivého závěru:
Co když ani nevíme, kde všude jsou ony opakující se části? Tato otázka není nic
jiného, než jiný výraz pro chaos a nepřehlednost v programu!
Plat naopak: Pokud se daná část kódu neopakuje a vyskytuje se pouze jednou, tak ji
můžeme umístit na své logické místo a tam ji také najdeme. Něco řešíme a ptáme se
„kde to jenom v kódu asi je“? Pokud máme program navržen podle principu opětovné
použitelnosti, tak odpověď je jasná a rychlá: tady, “BINGO“ a nikde jinde! Pokud však
dojde k opakování kódu, tak tyto části kódu automaticky nejsou na svých logických
místech! Jsou všude možně! A je to, co vede k nepřehlednosti a nakonec k velmi
špatné čitelnosti kódu!
V kódu bez opětovné použitelnosti nejsou věci na svých logických místech,
opakují se všude možně a proto kód není jasný, ztrácí transparenci, není
přehledný, není srozumitelný, vyžaduje další doprovodnou dokumentaci,
potřebuje další vysvětlení a další evidenci, kterou by kód s opětovnou
použitelností nepotřeboval.
Nyní je už zřejmé, že je výhodnější psát programy s využitím principu opětovné
použitelnosti než bez něj. Pokud nebudeme tento princip dodržovat, kód se stává
těžko čitelným a tedy také „škaredým“.
strana 21
Úvod do objektového programování s použitím jazyka C#
© Ilja Kraval, 2006
Optimalizace jako porušení principu opětovné použitelnosti
V některých případech programátor úmyslně přistoupí k porušení principu opětovné
použitelnosti. Ví moc dobře, že by se tam dal zavést, ale nepoužije jej a to úmyslně.
Důvodem tohoto na první pohled divného postupu je získání nějaké technologické
výhody, například zvýšení rychlosti algoritmu zpracování.
Představme si například, že je třeba rychle najít prvek v nějaké složité datové
struktuře podle nějaké jeho hodnoty, například každý datový prvek má řetězec rodné
číslo a podle něj máme vyhledat daný prvek mezi ostatními prvky. Zjistíme, že díky
složitosti těchto struktur je algoritmus nalezení prvku příliš pomalý. Z toho důvodu si
zavedeme vedle těchto struktur druhé pomocné datové struktury s rodnými čísly
(tedy podruhé). Průchod těmito prvky bude o mnoho rychlejší a díky nějaké vazbě do
původních datových struktur budeme pomocí z těchto vedlejších struktur získávat
údaje mnohem rychleji. Datové struktury evidentně obsahují zdvojeně údaje (zde
rodná čísla) jednou v původních a podruhé v pomocných strukturách a v těch rychleji
vyhledáme. Uvedené zdvojení údajů, které umožňuje rychlejší průchod, evidentně
narušuje princip opětovné použitelnosti. Projeví se to například v tom, že když se
něco změní v údajích, musíme tuto změnu provést dvakrát, jednou v původní
struktuře a podruhé v pomocné struktuře.
Poznámka: Takovýchto příkladů by bylo možné uvést více, pro náš výklad to však
není zajímavé, protože takové postupy pro vysvětlování OOP potřebovat nebudeme.
Je však dobré o tomto úmyslném postupu opuštění principu opětovné použitelnosti
vědět.
Takovému procesu, kdy programátor záměrně opustí princip opětovné použitelnosti,
aby získal nějakou technologickou výhodu, se říká optimalizace. Daň za ni je však
krutá: Je třeba vynaložit další úsilí, které souvisí se ztrátou opětovné použitelnosti.
Při optimalizaci hrozí, že kód se stane nepřehledným, tedy těžko čitelným. Při
optimalizaci je třeba dobře vysvětlit, proč tento opakující kód vypadá tak, jak vypadá,
proč a kde se opakuje atd.
Z těchto důvodů, pokud programátor nemusí, neopouští princip opětovné
použitelnosti. Tuto poučku si zapamatujeme.
V některých případech programátor úmyslně opouští princip opětovné
použitelnosti proto, aby získal nějakou technologickou výhodu, většinou se
jedná o rychlost zpracování. Tomuto procesu úmyslného opuštění principu
opětovné použitelnosti se říká optimalizace. V případě jejího použití musí
programátor zvýšit úsilí při dokumentaci kódu, protože díky optimalizaci se
strana 22
Úvod do objektového programování s použitím jazyka C#
© Ilja Kraval, 2006
kód stane hůře čitelným, mnohdy až netransparentním. Vyplývá z toho, že
pokud programátor nemusí provést optimalizaci, neprovede ji!
Opětovná použitelnost a C#
Pokud používáte nějaké vývojové prostředí, je dobré vědět, do jaké míry umožňuje
toto prostředí využívat principu opětovné použitelnosti. Některá prostředí totiž nejsou
„až tak dobrá“ a jiná umožňují využít tohoto principu plnou měrou. Dá se říci, že
právě schopnost využití principu opětovné použitelnosti je měřítkem dokonalosti
vývojového prostředí. Čím modernější a lepší prostředí, tím vyšší schopnost
v opětovné použitelnosti a naopak.
V tomto ohledu je C# a prostředí technologie .NET opravdu na špici mezi ostatími
vývojovými prostředími (pozn. spolu s Javou). Zatím nevíme jak se to dělá, ale
řekněme si, jak se projevuje tato dokonalost opětovné použitelnosti do důsledků:
Pokud využijete všech vlastností prostředí .NET (včetně ASP.NET), pak můžete
napsat jeden jediný neopakující se kód, umístit jej zkompilovaný na jeden jediný stroj
a poté jej opětovně použít po celém světě odkudkoliv, z libovolného stroje, který je
připojen k Internetu. Tak to je opravdu „super ultra mega extra opětovná
použitelnost“ ☺ !
Schopnost vývojového prostředí využít opětovnou použitelnost je měřítkem
dokonalosti prostředí. Prostředí .NET je v tomto ohledu velmi dokonalé. Můžete
napsat jeden jediný neopakující se kód, umístit jej zkompilovaný na jeden
jediný stroj a poté jej opětovně použít po celém světě odkudkoliv,
z libovolného stroje, který je připojen k Internetu.
strana 23
Úvod do objektového programování s použitím jazyka C#
© Ilja Kraval, 2006
3. Class neboli třída
Představme si následující situaci: Píšeme kód a používáme objekty, které mají
skladbu takovou, jak jsme si uvedli v předešlých kapitolách, tj. objekty mají svou
vnitřní paměť, mají vnitřní metody a mají protokol zpráv. Některé objekty se
samozřejmě svojí skladbou mohou jeden od druhého lišit a některé objekty mohou
být svými vlastnostmi shodné. Co to znamená, když řekneme, že dva objekty mají
shodné vlastnosti? Objekty shodných vlastností mají stejnou strukturu vnitřní paměti,
mají stejné vnitřní metody a mají stejný protokol zpráv. Objekty stejných vlastností
stojí v programu vedle sebe jako dva od sebe odlišené objekty, oba mají stejnou
vnitřní strukturu, vypadají jako dva klony, avšak každý může mít různé hodnoty ve
svých vnitřních pamětích. Například takto si namalujeme dva objekty A1 a A2
stejných vlastností:
A1
x = 1.5
A2
x = 2.3
obrázek 10 Dva objekty mohou mít stejné vlastnosti (jako klony), ale každý má své
vlastní hodnoty ve vnitřní paměti
Pokud bychom v programu definovali dva různé objekty, které mají mít stejné
vlastnosti dvakrát a vícekrát, tj. pro každý objekt stejných vlastností zvlášť, porušili
strana 24
Úvod do objektového programování s použitím jazyka C#
© Ilja Kraval, 2006
bychom princip opětovné použitelnosti, protože bychom tutéž definici vlastností
objektu psali vícekrát. Abychom se proto vyhnuli opakované definici objektů stejných
vlastností, zavádí se v OOP třída neboli class. Princip je velmi jednoduchý (když už
víme, jak funguje opětovná použitelnost):
Vlastnosti objektů stejných vlastností definujeme ve třídě. Jedna třída sdružuje jednu
definici pro všechny objekty stejných vlastností. Objekt má poté vlastnosti dány tím, z
jaké pochází třídy. Dva objekty z téže třídy mají stejné vlastnosti, tj. mají stejný
protokol zpráv, stejnou strukturu vnitřní paměti a stejné vnitřní metody. Jsou to dva
klony, ale každý žije v programu samostatně. Jediné, co je spojuje, je to, že když
vznikaly, použila se stejná definice vlastností objektů.
Objekt, který pochází z dané třídy, se nazývá instance dané třídy.
Základní poslání třídy (class) v OOP je zavést „centrální“ tedy opětovně
použitelnou definici vlastností vznikajících stejných objektů. Objekt z dané
třídy má poté vlastnosti dány tím, z jaké pochází třídy. Objektu z dané třídy také
říkáme instance dané třídy.
Co je to třída v čisté teorii OOP
V úplně čisté teorii objektově orientovaného programování je vše objektem. Když se
tedy zeptáme, co je třída v čisté teorii OOP, tak už známe odpověď – v čisté teorii je
třída objektem. Znamená to, že každá třída v čisté teorii OOP má takovou strukturu,
jak je popsána u objektu v předešlých kapitolách a to už známe: Každá třída jako
objekt má své metody a má své atributy (viz předešlé kapitoly). Navíc kromě toho má
každá třída schopnost „centralizovat definice pro nové objekty“, kvůli tomu přece
existuje. Jak tak třída činí, si povíme za chvíli.
Musíme si nyní vysvětlit jeden zdánlivý protimluv. Mohlo by se na první pohled zdát,
že jsme se dostali do kruhu: K tomu abychom definovali vlastnosti objektů a využili
přitom princip opětovné použitelnosti, zavádíme třídu. Přitom třída je objekt.
Znamená to, že potřebujeme pro definici třídy opět nějakou třídu, když je třída
objektem? Není to kruh? Odpověď zní: Nikoliv.
Třídu jsme zavedli proto, abychom centralizovali definici objektů, které vypadají
stejně, tj. pro objekty, které jsou klony. Základním posláním třídy je definovat
vlastnosti instancí třídy, instancí ze třídy může být několik (bůhví kolik). Otázkou je,
potřebujeme několik úplně stejných tříd stejně jako instancí? Odpověď zní ne, jako
třída centralizující vlastnosti pro jednu skupinu objektů stejných vlastností stačí jedna
třída, nemusíme (dokonce bychom neměli) opakovat stejnou třídu podruhé. Třídy
strana 25
Úvod do objektového programování s použitím jazyka C#
© Ilja Kraval, 2006
proto nemusíme zavádět stejně jako instance z těchto tříd, protože vždy potřebujeme
pouze jednu třídu pro jednu skupinu objektů stejných vlastností. Jinak řečeno,
nepotřebujeme skupinu stejných tříd a proto nemusíme zavádět stejný mechanismus
opětovné použitelnosti pro třídy. Vyplývá z toho, že pokud chceme zavést třídu, stačí
nějak v programu prostě říci: „Budiž tato jedna třída“ a je to…
Jak to vlastně třída dělá s instancemi při zrodu?
Protože v čisté teorii OOP má třída vlastnosti objektu a současně má vlastnosti
„předpisu“ pro objekty společných vlastností, musí se třída skládat ze dvou částí.
Jednu část nazveme „class section“ (sekce třídy), ta zavádí vlastnosti třídy jako
objektu, zavádí její atributy a její metody.
Druhou část třídy nazvěme „instance section“ (instanční sekce), ta má v sobě definici
objektů, jak mají objekty z této třídy vypadat (instance), až se zrodí. Zde je uschován
onen centralizující předpis „DNA“, tj. zde je nějak uvedeno, jak budou vypadat
instance z této třídy. Pokud bychom si třídu v čisté teorii OOP namalovali, vyjde nám
takovýto obrázek:
metody a atributy třídy
(class section)
......
.........
.....
definice vlastností
instancí
(instance section)
obrázek 11 Třída je v čisté teorii OOP objektem a skládá se ze dvou částí: class
section a instance section
Instance ze třídy vzniká následujícím mechanismem:
strana 26
Úvod do objektového programování s použitím jazyka C#
© Ilja Kraval, 2006
Ve třídě musí být zabudována alespoň jedna zpráva, kterou třídě jako objektu
můžeme poslat, a která reprezentuje požadavek na vznik nové instance. Třída
provede následující:
•
požadavek na vznik objektu převezme
•
podívá se do předpisu, jak vypadá požadovaný objekt (viz předešlý obrázek),
•
požádá systém o prostředky (paměť apod.) a vytvoří nový objekt přesně
takových vlastností, jaký vidí u sebe v předpisu
•
vyplivne ven úplně nový čerstvě narozený objekt (instanci) jako odpověď na
přijatou zprávu.
Zprávě, kterou třída přijímá a která dává vzniknout objektu, se říká konstruktor.
Poslat tuto zprávu třídě se nazývá zavolat konstruktor.
Poznámka:Je možné také napsat kód, který se spustí při převzetí požadavku na zrod
objektu (zavolání konstruktoru), tj. třída ještě před vyplivnutím nové instance ven
spustí tento kód. Většinou se tohoto kódu využívá nastavení počátečních hodnot
nově vznikajícího se objektu.
Třída se skládá ze dvou částí: jedna část třídy je zavedena jako třída = objekt
(tj. zavádí atributy a metody třídy), druhá část třídy je předpisem pro nové
instance. Ve třídě zaveden tzv. konstruktor, který umožňuje třídě poslat
požadavek na vznik nové instance, při vzniku instance může být spuštěn
nějaký obslužný kód.
Definice třídy v C#
Otázkou je, jak třídu podporuje samotný jazyk C#. V tomto jazyce existuje
rezervované slovo class, které je určeno právě pro zavedení třídy v tom pojetí, jak
si zde třídu vysvětlujeme. Zavedeme ji pomocí syntaxe:
strana 27
Úvod do objektového programování s použitím jazyka C#
© Ilja Kraval, 2006
class <název třídy>
{
... <vnitřek definice třídy>
}
například takto prázdnou třídu s názvem CA:
class CA
{
// this is empty class.
}
Pro další výklad je dobré si uvědomit, že jednotlivé prvky v syntaxi třídy, které
můžeme zavést, se budou nacházet v první úrovni vnoření do složených závorek, tj.
podle tohoto schématu:
class <název třídy>
{
<něco 1>
...
<něco 2>
...atd.
}
V této části výkladu už víme, že těmito prvky mohou být konstruktor, metody a
atributy třídy a předpis pro novou instanci.
Atributy budoucích instancí
strana 28
Úvod do objektového programování s použitím jazyka C#
© Ilja Kraval, 2006
První částí předpisu pro instance jsou definice atributů budoucích instancí, které
vzniknou ze třídy voláním konstruktoru. Hovoříme tedy nyní o části předpisu pro
instance,tj. o té části „instance section“, která definuje atributy objektu, jinak řečeno
jeho vnitřní paměť (viz obrázek 11).
Atributy budoucích objektů se zavádějí ve třídě v jazyce C# podle schématu:
class <název třídy>
{
...
private <typ> <název atributu>;
...
}
Atributy objektu nebudou zvně objektu vidět, to nám zabezpečuje rezervované slovo
private. (vzpomeňme na úplný začátek s červeným kolečkem obrázek 1)
Otázkou je, co může reprezentovat <typ>. V jazyku C# existuje výčet tak zvaných
primitivních datových typů, které můžeme použít pro nejjednodušší atributy, což jsou
string, long atd. Jejich výčet najdete v helpu C#, budeme je postupně také používat
v dalším textu, není třebapro tento výklad je studovat podrobně. Datové typy jsou
vlastně již zavedené třídy, nejednodušší jaké mohou být. Objekty z nich se chovají
„velmi primitivně“, drží v sobě string, integer apod.
Příklad na atributy z primitivních datových typů:
...
class CA
{
private long myId;
private string myName;
//...atd
}
...
Tento zápis znamená následující: Až se narodí objekt ze třídy CA, bude mít v sobě
atributy myId, myName odpovídajících typů atd. Navíc tyto atributy zvenku nebudou
vidět, jak vyžaduje teorie OOP.
Atributem může být také objekt z nějaké složitější třídy, například ze třídy definované
námi anebo ze složitější třídy, kterou nám poskytuje prostředí (naprogramovali ji
dodavatelé Visual Studia). V těchto příkladech případě jako <typ> chápeme opět
jako třídu, ale složitější, název atributu chápeme jako název objektu.
Příklady atributů i s vlastními objekty:
strana 29
Úvod do objektového programování s použitím jazyka C#
© Ilja Kraval, 2006
class CA
{
private
private
private
private
//...atd
}
class CB
{
}
long myId;
string myName;
CA myNextObj;
CB myB;
Všimněte si dvou věcí na předešlém příkladu: Uvnitř objektů ze třídy CA se budou
vyskytovat (kromě jiného) dva objekty, jeden ze třídy CA a druhý ze třídy CB. Je
dovoleno
Požadavek na vznik objektu – konstruktor v C#
Konstruktor je součástí třídy, má stejný název jako třída a zavedeme jej podle
schématu:
class CA
{
public CA() //konstruktor
{
//kód konstruktoru
}
}
Poznámka: Konstruktor může mít uvedeny vstupní parametry v závorce za jeho
názvem. K této možnosti se ještě vrátíme, zatím ji nebudeme potřebovat.
Po zavedení konstruktoru může třídu požádat o novu instanci. Slouží k tmu
rezervované slovo new. Základní schéma je následující:
<název objektu> = new <název konstruktoru>();
například v běhu kódu takto:
strana 30
Úvod do objektového programování s použitím jazyka C#
© Ilja Kraval, 2006
...
myB = new CB();
...
V podstatě předešlý kód znamená: Třído CB, zroď nový objekt zavoláním
konstruktoru CB(), a až ho budeš mít hotový, vrať ho, my si ho dosadíme do
proměnné myB. Po tomto příkazu (pokud dopadne dobře ☺) máme v proměnné myB
umístěn žijící objekt ze třídy CB.
Poznámka: Nezapomínejte na závorky u volání konstruktoru! Je to jedno
z nejčastějších opominutí…
Zavolání konstruktoru proběhne v námi definovaném okamžiku v kódu tam, kde jsme
umístili toto volání. Pokud chceme použít předešlou konstrukci, tak musí být
proměnná myB zavedena, tj. deklarována. O jedné pozici pro možnou deklaraci již
víme – objekt může být v pozici atributu (viz předešlé vysvětlení, jak se zavádějí
atributy). Druhou možností (kterou jsme ještě nebrali) je, že objekt může být zaveden
jako proměnná v metodě objektu (zatím jsme nepoužili, vysvětlíme na příkladech).
Existuje druhý způsob, jak zavolat konstruktor a to přímo jako součást deklarace
atributu podle schématu:
...
private CB myB = new CB(); //volání konstruktoru v deklaraci
...
například v kódu i s třídou :
strana 31
Úvod do objektového programování s použitím jazyka C#
© Ilja Kraval, 2006
class CA
{
private long myId;
private string myName;
private CB myB = new CB(); //volání konstruktoru v deklaraci
//...atd
}
class CB
{
}
V tomto případě je volání konstruktoru pro zrod objektu zavedeno spolu s deklarací
(viz řádek s komentářem). Konstruktor tvořící objekt bude zavolán „někdy“ při startu,
což si systém udělá sám, můžeme se ale spolehnout, že určitě před prvním použitím
objektu.
Zavedení metod objektu
Již víme:
1. jak zavést třídu
2. jak se zavádí atributy budoucích objektů
3. jak se zavádí konstruktor
4. jak se volá konstruktor
Další věc, se kterou se musíme seznámit je, jak zavést metody budoucích objektů,
které definují interface objektu (tj. jeho množinu zpráv) a vnitřní metody objektu. Poté
si také řekneme, jak se tyto metody volají (tj. jak se posílá zpráva objektu)
Metody objektu se zavádějí ve třídě podle schématu:
Pokud metoda vrací návratový parametr (obdoba funkce), potom takto
strana 32
Úvod do objektového programování s použitím jazyka C#
© Ilja Kraval, 2006
public <typ> <název metody>(<vstupní parametry>)
{
//kód metody
}
kde typ <typ> je návratový typ.
Pokud metoda nevrací jako výsledek nic (obdoba procedury), namísto <typ> se
udává rezervované slovo void. Vstupní parametry se udávají ve tvaru <typ>
<název vstupního parametru>, oddělovačem je čárka.
Lépe to pochopme na příkladech:
class CA
{
private int x;
private int y;
private string name;
public CA() //konstruktor...
{
}
public void DoX(CB ab, string astr) //metoda...
{
// nějaký kód
}
public int Getx() //metoda...
{
//nějaký kód...
return x;
}
}
Metoda DoX má dva vstupní parametry, jeden je typu CB, druhý typu string. Tato
metoda nevrací žádnou hodnotu. Metoda Getx() vrací hodnotu typu int, nemá
vstupní parametry. Z kódu je zřejmé, že po určitém vykonaném kódu se vrací
hodnota, kterou v té chvíli drží atribut x.
strana 33
Úvod do objektového programování s použitím jazyka C#
© Ilja Kraval, 2006
Zavolání metody objektu
Pokud máme zavedeny metody objektu podle předešlého schématu, můžeme poté
objektu poslat zprávu, čemuž se také říká zavolat metodu objektu. Tento
mechanismus známe z teorie OOP, již jsme si jej uváděli hned v úvodu této knihy
(viz obrázek 4). Nyní si ukážeme, jak se tento mechanismus zavádí v jazyce C#.
Pro poslání zprávy objektu neboli zavolání metody se v jazyce C# používá tzv.
tečková syntaxe, kdy poslání právy se skládá z názvu objektu + tečka + název
metody, tj. podle schématu:
<název objektu>.<název metody>
Pokud má metoda vstupní parametry, jsou přiřazeny hodnoty do těchto parametrů,
pokud metoda vrací parametr, tak se tento výstupní parametr přiřazuje do jiné
proměnné jako výstupní parametr (viz dále v příkladech), přiřazení výstupní hodnoty
však není povinné. Pokud výstupní hodnotu nepřiřadíme do proměnné, potom
návratová hodnota se ztrácí a nepoužije se.
Je vcelku pochopitelné, že pokud chceme zavolat metodu objektu, tak tento objekt
musí být nejprve deklarován a musí být inicializován (tj. zavolán konstruktor).
Příklady volání metod, současně viz předešlý kód s definicí třídy (předpokládáme, že
konstruktor byl již zavolán a objekt myA je inicializován.
myA.DoX(myB, "hello");
…
int z = myA.Getx();
Tímto posíláme objektu myA zprávy takto: V prvém řádku příkladu posíláme objektu
myA zprávu DoX() i se vstupními parametry a nepřijímáme žádný výstupní parametr.
V druhém řádku tohoto příkladu témuž objektu posíláme zprávu Getx(), bez
vstupního parametru, výstupní přebíráme a vládáme do proměnné z.
Skladba objektu v teorii OOP a v jazyce C#
Vrátíme se úplně na začátek, kdy jsme si vysvětlili, jak podle teorie OOP vypadá
objekt, viz obrázek 4. Nyní se na něj podíváme znovu z pohledu konstrukce objektu
v jazyce C#.
Nechť je zavedena třída CA podle následujícího kódu:
strana 34
Úvod do objektového programování s použitím jazyka C#
© Ilja Kraval, 2006
class CA
{
private int mx;
private int my;
public CA() //konstruktor...
{
}
public void Setxy(int ax, int ay) //metoda...
{
mx = ax;
my = ay;
}
public int Getx() //metoda...
{
}
return mx;
public int Gety() //metoda...
{
}
return my;
}
Všimněme si, že ve třídě je definován konstruktor CA, dva atributy a tři metody.
Nechť jsme někde v kódu vytvořili instanci z této třídy, nazvěme ji například myA. To,
že jsme tuto vytvořili znamená, že jsme zavolali konstruktor a nově vzniklý objekt
přiřadili do této proměnné, např. jako pomocí inicializace atributu přímo v deklaraci:
private CA myA = new CA();
Namalujme si, jak vypadá objekt myA podle již probrané teorie OOP (pro názornost
vynecháváme vstupní parametry metod v obrázku):
strana 35
Úvod do objektového programování s použitím jazyka C#
© Ilja Kraval, 2006
int mx;
int my;
MyA.Setxy
Setxy Getx
Gety
MyA
obrázek 12 Struktura objektu ze třídy CA odpovídající předešlému kódu
Objekt MyA identifikuje zprávu Setxy, tuto zprávu nalezl v protokolu zpráv a spustil
kód, který této metodě odpovídá (na obrázku nejsou znázorněny vstupní parametry
metod). Uvnitř objektu jsou dva atributy, které jeho metody vidí a mohou s nimi
pracovat…
Jak psát zkušební programy v C#
Je na čase ukázat si, jak budeme náš kód zkoušet.
Poznámka: Jedna dobrá rada: Vždy, když píšete kód, nešetřete pomocným a
vedlejším kódem někde mimo váš projekt, tedy mimo kód, který budete odevzdávat
jako programátoři profesionálové. Vedlejším kódem musíte velmi dobře ověřit vaše
konstrukce a vaše předpoklady. Pokud tak neučiníte, hrozí, že zjistíte, že „hlavní kód“
nějak nechodí a nevíte kde je chyba a proč nechodí. Pokud si krok po kroku
ověřujete vaše konstrukce vedlejším kódem, toto nebezpečí nenadálých problémů,
proč to nechodí, se velmi zmenší (ale nezanikne nikdy ☺).
Tato kapitola se vám pokus dát radu, kam a jak umísťovat zkušební kód v C#.
Postup je následující:
1. Ve Visual Studio Express založte nový projekt typu Windows Application.
Objeví se první formulář.
2. Na formulář umístěte prvek button (tlačítko). Na jeho stisk budete spouštět
výkonnou část kódu. Pro napsaní tohoto kódu dvakrát poklepejte na tlačítko v
strana 36
Úvod do objektového programování s použitím jazyka C#
© Ilja Kraval, 2006
„design modu“ a objeví se odpovídající místo, kam psát kód. Takových tlačítek
si později můžete nasázet kolik chcete podle potřeby testů zkušebních kódů.
3. Pro výstupy budeme používat buď jiné prvky na formuláři anebo prostě tzv.
„Messagebox“. Jeho syntaxi si uvedeme později.
4. Při přepnutí do kódu uvidíte kód třídy formuláře, který začíná slovy
public partial class Form1 : Form atd.
(mimochodem jak vidět, tak formulář je také objektem se svou třídou, viz
slůvko class!) Za tuto třídu formuláře, tj. za její složené závorky budeme
umisťovat zkušební kódy zkoumaných tříd.
Ještě jednou upozorněme, že takto budeme umisťovat pouze zkušební kód a nikoliv
kód v hlavním projektu, který odevzdáváme jako výsledek! O skladbě a umístění
takového kódu jako hlavního výsledku si povíme později.
Úkol „prázdná třída“
Vyzkoušíme si, zda C# syntaxi třídy rozumí. Zavedeme prázdnou třídu podle
předešlé ukázky kódu a umístíme ji za definici třídy formuláře, tj. takto:
...
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
}
}
class CA
{
// this is empty class.
}
...
strana 37
Úvod do objektového programování s použitím jazyka C#
© Ilja Kraval, 2006
Proveďte a zkompilujte! (nic víc - program zatím nic neumí ☺). Pokud se kompilace
zdařila, je zavedení třídy korektní.
Úkol: Prázdná třída s konstruktorem
Doplňte předešlý kód z předešlé úlohy o konstruktor podle předešlého odstavce a
zkompilujte.
Úkol „část třídy Form1“
Podívejte se pozorně na kód třídy Form1, tj.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
}
}
I když jsou tam určitě věci, kterým nerozumíme, například co je to rezervované slovo
partial v definici třídy, tak na jednu věc bychom měli po krátkém uvažování umět
odpovědět. Soustřeďte se na tuto část kódu:
...
public Form1()
{
InitializeComponent();
}
...
Odpovězte, jaký význam má tato část kódu uvnitř třídy Form1?
Úkol „objekt jako atribut formuláře“
strana 38
Úvod do objektového programování s použitím jazyka C#
© Ilja Kraval, 2006
Zavedeme třídu CA a jeden objekt z této třídy deklarujeme jako část vnitřní paměti
objektu formuláře, tj. jako atribut formuláře. Současně s deklaraci zavedeme i
konstrukci. Zkušební kód pak vypadá takto:
public partial class Form1 : Form
{
private CA myA = new CA(); //vložená instance ze třídy CA
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
}
}
class CA
{
public CA() //konstruktor CA
{
//kód konstruktoru
}
}
proveďte a zkompilujte.
Úkol objekt i atributy a metodami
Dříve, než začneme tento úkol, tak se musíme zmínit ještě o jedné důležité
záležitosti. Zmiňovali jsme se, že existují tzv. datové typy (int, string atd.).
Tyto typy se v C# také chápou jako třídy, ale opravdu velmi jednoduché třídy
s malým počtem metod. Tento mechanismus má své velké výhody. Objekty z těchto
tříd vidíme a používáme stejně jako „klasická“ čísla nebo řetězce (jak jsme zvyklí
z klasického programování), tj. tyto proměnné v sobě dané drží dané hodnoty
(řetězce nebo čísla), jak jsme zvyklí, navíc jsou však také vybaveny metodami jako
objekty. To je velmi výhodné, protože například každé číslo „umí“ metodu
strana 39
Úvod do objektového programování s použitím jazyka C#
© Ilja Kraval, 2006
ToString(), tj. vydat ze sebe svou hodnotu ve tvaru řetězce. Pokud nám není
tento výklad úplně jasný, určitě jej pochopíme na následujícím příkladu.
Předešlý kód doplníme o atribut instancí typu int s názvem mx. Zavedeme metodu
Inc (jako „inkrementace“, tj. zvednutí o jedničku). Zavolání této metody způsobí
zvednutí atributu mx o jedničku. Vybavíme objekty z této třídy také metodou, která
vrací hodnotu z atributu mx. Zkušební program provede to, že na stisk tlačítka zavolá
metodu Inc a poté požádá objekt o hodnotu a tu zobrazí do nápisu tlačítka.
Zkušební kód vypadá takto:
public partial class Form1 : Form
{
private CA myA = new CA();
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
myA.Inc();
button1.Text = myA.Getx().ToString();
}
}
class CA
{
private int mx;
public CA() //konstruktor...
{
}
public void Inc() //
{
++mx;
}
public int Getx() //metoda...
{
}
return mx;
}
Poznámka: V příkladu jsme použili operátor ++. Prostudujte jeho funkcionalitu
v helpu Visual Studia.
strana 40
Úvod do objektového programování s použitím jazyka C#
© Ilja Kraval, 2006
Konec dokumentu, pokračování…
strana 41

Podobné dokumenty

CZ_lesson_san4_ T_Nibco

CZ_lesson_san4_ T_Nibco Během odinstalování programu je třeba provést deaktivaci na serveru InstalSoft, aby byla možná opětovná aktivace po jeho nainstalování. Proces odinstalování může probíhat z CD obsahující balík prog...

Více

Informační systémy - Katedra automatizační techniky a řízení

Informační systémy - Katedra automatizační techniky a řízení Chyby v dávkách zSyntaktické chyby – zastavení běhu, neprovede se nic. zChyby běhu – zastavení běhu, všechny provedené příkazy zůstávají v platnosti.

Více

Kallista, P. - systemic.cz

Kallista, P. - systemic.cz v systémickém marketingu až sekundární úkol. Vzájemně provázané vazby a zákon autopoiézy způsobí, že bude-li cokoliv ohrožovat přirozenou existenci prvků toho systému (nedostatek zboží, služby…), p...

Více

ve formátu (Adobe Acrobat)

ve formátu  (Adobe Acrobat) Velmi dobře . Dostáváme se k poslednímu 3. typu rychlosti. Vy si také můžete pronajmout letadlo nebo můžete mít i vlastní vrtulník.

Více

Programován´ı v C++ Prvn´ı kroky

Programován´ı v C++ Prvn´ı kroky 2. Přidejte do programu proměnnou, která bude obsahovat řetězec ”jablek”a vypište jej na obrazovku hned za čı́slem v předchozı́m úkolu. 3. Přepište program vizitka z předchozı́ lekce ta...

Více

Historické pojednání o Válce s Lesem a jejími dopady na vesnici

Historické pojednání o Válce s Lesem a jejími dopady na vesnici Buď jak buď, co bohové nechtěli, ze svazku se narodilo dítě. Chlapec, který vynikal silou po otci a dobrotou po matce. Vesnice byla zděšena vzhledem toho stvoření a žádala jeho smrt. Izbellah ale c...

Více

Citáty - Adventurista

Citáty - Adventurista farmě došlo k úmrtí desítek mláďat. Ukázalo se, že malá Luciana stíhá vysát všechno mlíčko pro sebe. 1965. Luciana sice stále ještě neumí chodit, ale už dlouho umí mluvit. Plazí se po farmě, ukazuj...

Více

Slovníček NetLogo

Slovníček NetLogo Vrací podmnožinu dané množiny agentů tvořenou pouze agenty na políčkách, kteří se nacházejí v dané vzdálenosti od volajícího agenta. Vzdálenosti jsou určeny seznamem skládajícím se z dvoupoložkovýc...

Více

Přijímací zkouška 2011 Identifikační údaje: Označte správnou

Přijímací zkouška 2011 Identifikační údaje: Označte správnou [ ] d) WiFi zařízení a kabelem připojený přístupový bod. 20. Který z postupů vytváření objektů v objektově orientovaném programování je správný: [ ] a) Konstruktor, zavolaný proměnnou, vytváří inst...

Více