Objektově orientované programování I. – Úvod
V prvním článku ze seriálu věnovanému programování obecně jsme si popsali různé typy programovacích jazyků a ukázali jsme si, jak přibližně vypadají programy v nich napsané. Také jsme došli k tomu, že C++, ve kterém se běžně Arduino programuje, je objektově orientovaný jazyk. A právě objektově orientovaným programováním se dnes budeme zabývat.
Některé z vás jsem teď možná zmátl. Tvrdím, že Arduino se běžně programuje v C++. V předchozích článcích jsem ale tvrdil, že Arduino programujeme v jazyce Wiring. Skutečnost je taková, že Wiring je knihovna pro jazyk C++, která zjednodušuje programování Arduina. Jelikož běžný uživatel Arduina většinou neví, jaká konstrukce pochází z C++ a jaká je specifická pro Wiring, ujalo se v Arduino komunitě pojmenování jazyk Wiring. To je sice z technologického hlediska nesprávné označení, ale pro naše účely není nutné mezi C++ a Wiring přesně rozlišovat.
Objektově orientované programování
Základním prvkem objektově orientovaných (dále OO) jazyků je objekt. Objekt si můžeme představit jako konkrétní věc – například psa Alíka. Abychom mohli takovéto objekty vytvářet, potřebujeme nějaký vzor – řekněme vzor, který si nazveme pes. Takovému vzoru se v OO programování říká třída. Ta nám definuje vlastnosti (atributy) a schopnosti (metody) objektu. Podle třídy pes je možné vytvořit libovolné množství psů – psa Alíka, psa Žeryka, psa Punťu… Těmto konkrétním objektům se říká také instance třídy pes. Jak by mohla vypadat třída pes? Třeba takto:
class Pes{ private: String jmeno = ""; int vyska = 0; public: Pes(String, int); void stekej(); }; //tady opravdu musí být středník!
Odbočka do terminologie. Funkcím, které náleží objektu se říká metody. Proměnným, které náleží objektu se říká atributy.
Máme tedy objekt pes, který má atributy jmeno a vyska a umí štěkat pomocí metody stekej. Všimněte si, že jmeno a vyska jsou uvozené klíčovým slovem private. Tyto proměnné jsou označeny za „soukromé“. Bude k nim možné přistupovat pouze uvnitř metod objektu. Naopak klíčové slovo public označuje atributy a metody, které jsou dostupné i vně objektu – tedy je možné je volat v rámci programu. Metoda Pes() (která má stejný název, jako objekt samotný) je speciální a říká se jí konstruktor. Pomocí konstruktoru dochází k vytváření nových instancí třídy Pes. V rámci konstruktoru můžeme například nastavit hodnoty privátních atributů podle parametrů konstruktoru a podobně. Konstruktor se definuje takto:
Pes::Pes(String jm, int vy){ jmeno = jm; vyska = vy; }
Atributy vytvořené instance třídy Pes se nastaví na zadané hodnoty. Ještě nám zbývá vytvořit metodu stekej.
void Pes::stekej(){ Serial.print(jmeno); Serial.print(" steka "); Serial.println("Haf!"); }
Nyní tedy máme šablonu, podle které můžeme vytvořit různé konkrétní psy.
Pes alik("Alik", 100); //pes Alik vysoký 100cm Pes zeryk("Zeryk", 60); //pes Zeryk vysoký 60cm
A ještě zbývá psy trochu popíchnout, aby zaštěkali.
alik.stekej(); zeryk.stekej();
Celý program, který nechá psy zaštěkat, tedy vypadá následovně:
class Pes{ private: String jmeno = ""; int vyska = 0; public: Pes(String, int); void stekej(); }; //tady opravdu musí být středník! Pes::Pes(String jm, int vy){ jmeno = jm; vyska = vy; } void Pes::stekej(){ Serial.print(jmeno); Serial.print(" steka "); Serial.println("Haf!"); } Pes alik("Alik", 100); Pes zeryk("Zeryk", 60); void setup() { Serial.begin(9600); Serial.println("Smecka"); alik.stekej(); zeryk.stekej(); } void loop() { }
Praktický příklad
Možná si říkáte, že je sice hezké, že máme vytvořenou třídu Pes, ale k čemu nám je u Arduina platná? Ukažme si tedy nějaký jednoduchý praktický příklad.
class LED{ private: int pin; boolean stav = LOW; //výchozí stav LED je vypnuto void nastav(boolean); public: LED(int); void zapni(); void vypni(); void prepni(); boolean vratStav(); }; LED::LED(int p){ pin = p; pinMode(pin, OUTPUT); digitalWrite(pin, stav); } void LED::zapni(){ nastav(HIGH); } void LED::vypni(){ nastav(LOW); } void LED::prepni(){ nastav(!stav); //nastaví LED na obrácenou hodnotu (0->1, 1->0) } void LED::nastav(boolean s){ stav = s; Serial.print("Nastavuji "); Serial.print(stav); Serial.print(" na pinu "); Serial.println(pin); digitalWrite(pin, stav); } boolean LED::vratStav(){ return stav; } LED L13(13); void setup() { Serial.begin(9600); L13.zapni(); delay(1000); L13.vypni(); delay(1000); for(int i = 0; i <= 10; i++){ L13.prepni(); delay(500); } Serial.print("Led zustala ve stavu "); Serial.println(L13.vratStav()); } void loop() { }
Jedná se o velice jednoduchou třídu sloužící k ovládání LED diody. Její public metody jsou zapni, vypni, prepni a vratStav (jejich název je snad dostatečně popisný 🙂 ). Všimněte si, že třída LED obsahuje i private metodu nastav, která slouží k nastavení stavu LED a zároveň k výpisu stavu na sériovou linku. Metoda nastav je volána uvnitř jiných metod třídy LED. Také je zde předvedena technika, která se používá k získání hodnot privátních atributů objektů. Přímo k atributu stav bychom se totiž pomocí L13.stav nedostali, proto musíme využít nějakou public metodu.
To byl letmý úvod do OO programování. O jeho dalších principech více zase příště.
- Sledovač slunce s Arduinem - 23.3.2022
- Programovatelný kytarový pedál s Arduinem - 26.2.2020
- Arduino infračervený teploměr vytištěný na 3D tiskárně - 11.2.2020
BorgMcz
20.6.2016 at 21:03Já bych se dovolil zeptat, na jednu věc, která by mohla být dnešnímu tématu blízká.
Potřeboval bych v jednom projektu v následujícím kódu měnit v posloupnosti jméno :
RBD::Button jméno (pin);
toto je základní vytvoření funkce, mohu ji tvořit ručně ale protože kód bude složitější a bude se mnohokrát opakovat a tak hledám jak jinak na to: ručně to udělám takto:
RBD::Button jmeno0 (30);
RBD::Button jmeno1 (31);
RBD::Button jmeno2 (32);
ale potřeboval bych to tvořit automaticky za pomoci cyklu, něco jako asi takhle:
for (int x=0; x<15; x++) {
RBD::Button jmeno(x) (y);
}
nevím, jak správně to zapsat, aby IDE pochopilo co chci. Dá se to nějak zapsat?
Jde mi pouze o to jméno plus číslo v X , Y je jasné.
Zbyšek Voda
20.6.2016 at 21:13Dobrý den. Toto budu probírat v jednom z dalších článků. Zatím vas nasměruji, kde hledat – podívejte se, jak se vytváří v C++ pole objektů a uložte si jednotlivá tlačítka do pole 🙂
BorgMcz
21.6.2016 at 6:19To jsem zkoušel, zjevně ale nesprávným zápisem. U toho Y kde dosazuji přiřazené piny (tedy jen čísla) mi to funguje, ale ne pro ten název. Nemohl bych poprosit o pomoc třeba na mailu? mm.svet(zavi.nač)centrum.cz
Diky.
Zbyšek Voda
21.6.2016 at 13:06Dobrý den, odpověděl jsem vám na fóru. Tady by to byl moc dlouhý post 🙂
http://bastlirna.hwkitchen.cz/forum/tema/jak-na-nazev-s-automatickym-posunem-jmena/#post-7390
BorgMcz
21.6.2016 at 21:10Rekl bych, že ted v tom mam jeste vetsi zmatek nez predtim :-)) Mohl bych poprosit o kontakt pres mail?
Zbyšek Voda
22.6.2016 at 8:28zbysekvoda@gmail.com
tribal.cz
20.6.2016 at 16:49Zbyšku nádhera, ale nemohu se ubránit dojmům jako překvapení a zmatenost. Jak dlouho lze arduino programovat pomocí OOP? Jedná se o funkci přidanou někdy v poslední době? ani ne před půl rokem jsem se o to zajímal na jednom fóru (pravda nikdy jsem to nezkusil), a odpovědí mi byl imaginární výsměch nejméně tří až čtyř členů daného fóra. Prakticky mi bylo řečeno že pokud chci programovat objektově mám zapomenout na arduino a vrátit se k vyšším jazykům.
Zbyšek Voda
21.6.2016 at 10:33Dobrý den, nerad bych plácnul nějakou blbost, ale myslím, že Arduino toto podporuje od začátku – knihovna Wiring je na C++, které objekty přímo podporuje. C++ nakonec stejně skončí v podobě nul a jedniček, takže nevidím důvod, proč by to nemělo jít 🙂
Objekty jsou většinou dostupné v knihovnách pro obsluhu všeho možného (ethernnet, motory, senzory…), takže se uživatel většinou s definováním objektů nesetká, ale jen je používá.
Melas
19.6.2016 at 20:06Zábavnou formou vysvětlená problematika. Je pravda, že jsou funkce, které používám, ale ve skutečnosti jim nerozumím a tohle mi je osvětluje. Děkuji.
posjirka
19.6.2016 at 11:03Zbyšku …. tleskám. Takhle hezky vysvětlení tříd a metod jsem nikde jinde neviděl.
Fakt super.
Zbyšek Voda
20.6.2016 at 15:06Děkuji. To bylo cílem, tak jsem rád, že se mi to snad i povedlo 🙂
Tomáš
19.6.2016 at 10:48Těším se na pokračování.