Objektově orientované myšlení

Bez objektů už si dnes programování nedokážu ani představit. Tento článek je jen rychlým úvodem k objektovému programování. Dle mého názoru u objektového programování jde především o způsob myšlení.


Důležitý je především způsob myšlení a přístup k řešení problémů. Základem je abstrakce konkrétních věcí, tedy jejich zjednodušení na samou podstatu. Vymezení okruhu vlastností a funkčnosti, která je pro daný objekt charakteristická a pro náš účel stěžejní.

Některé úkoly, které má aplikace řešit mohou být pro člověka na pochopení příliš složité. Každá aplikace řeší nějakou reálnou funkčnost a pokud nevím kudy do toho, snažím se rozebrat danou úlohu na dílčí části, ty abstrahovat a definovat jejich dílčí funkčnosti a vlastnosti. Pokud respektuji funkčností a vlastnostmi objektu reálný svět, tak jak to doopravdy funguje, nemohu udělat chybu a nakonec jednotlivé části budou tvořit funkční celek.

V objektovém programování takový abstraktní prvek nazýváme třída. Třída má své vlastnosti a metody. Vlastnosti mohou být např. velikost, barva, váha, objem apod. Metody pak mohou s těmito vlastnostmi manipulovat, provádět výpočty, tisknout sestavy atd.

Příklad: Mým úkolem je vytvořit modul na kreslení grafů.

Modul můžeme chápat jako množinu prvků (tříd, komplexů tříd, podsystémů, částí aplikace atd.), které zajišťují určitou funkčnost. Graf je reálný objekt a je tvořen dílčími součástmi, v našem případě budeme řešit bodový graf. Skládá se logicky z těchto objektů: bodový graf, body, osy, barvy a text.

Nejmenším objektem je bod, jeho vlastnosti jsou souřadnice na osách x a y, případně barva bodu (vysvětlím v dalším příkladu) atd. Třídu která ho reprezentuje vytvoříme takto:

  1. class Point {
  2. public $x, $y, $color;
  3. /* Základní metodou je konstruktor, je to funkce označená __construct, nebo funkce pojmenovaná stejně jako název objektu Point, tato funkce vytváří instanci objektu */
  4. public function __construct($x, $y, $color=new Color(0,255,0)) {
  5. $this->x=$x;
  6. $this->y=$y;
  7. $this->y=$color;
  8. }
  9. }
  10. $a=new Point(1,5); // vytvoření instance a
  11. $b=new Point(2,10); // vytvoření instance b

Třída je jakýsi vzor objektu, na základě něho můžeme vytvářet instance třídy, což je už konkrétní objekt. Konkrétních objektů můžeme mít kolik chceme. V příkladu jsou vytvořeny dvě instance a, b. První z nich a je bod se souřadnicemi na ose x=1 a na ose y=5. V našem příkladu je důležité použití slova $this, znamená to, že přistupujeme k instanci, tedy že hodnotu $x přirazujeme instanci, která funkci volala, tedy ke konkrétnímu objektu. Další použití jak přistupovat k vlastnostem třídy je self, tím přistupujeme ke třídě jako celku, nezávisle na instancích, tedy ke statické vlastnosti. Pokud nastavíme vlastnost pomocí self, např.: self::$staticVariable = 1;, pak bude vlastnost staticVariable stejná pro všechny instance i pro ty v budoucnu vytvořené. Přistupujeme pomocí operátoru dvojité dvojtečky ::, který se nazývá Paamayim Nekudotayim. Pomocí self přistupujeme uvnitř třídy, ale můžeme použít např. Point::$staticVariable=1;.

Dalším objektem, který si vytvoříme je barva, definujeme ji v konstruktoru a to zadáním podle modelu RGB, celočíselný typ vyjadřující červenou, zelenou, modrou.

  1. class Color {
  2. public $red, $green, $blue;
  3. public function __construct($red, $green, $blue) {
  4. $this->red=$red;
  5. $this->green=$green;
  6. $this->blue=$blue;
  7. }
  8. public function getColor() {
  9. ...
  10. }
  11. }
  12. $color1=new Color(150,200,250);
  13. $color2=new Color(100,115,200);

 

Funkce getColor pak vrací definici barvy pro GD knihovnu na kreslení obrázků, pokud je výstupem grafu obrázek, ale můžeme volit i jiné výstupy, například výstup pro javascript, který graf vykreslí nebo xml, pak by se nám mohla hodit funkce která vrací barvu ve tvaru #000000, tedy převádí RGB hodnoty na hex.

Podobně bychom mohli stavět i další třídy jako je Chart, Axis, Text a PointChart. Metodou Chart bude například vypočítat vhodné rozmezí popisků (jejich vzdálenost od sebe) pro osy, cílem PointChart bude především fungovat jako zásobník, tedy bude obsahovat pole a funkci, která přiřadí jednotlivé body do tohoto pole, zároveň bude dědit všechny metody a vlastnosti od Chart. Dále metodu která vytvoří a uloží obrázek grafu, vygeneruje xml nebo data potřebná pro javascript, který graf vykreslí.

  1. class PointChart extends Chart {
  2. public $points;
  3. public function __construct() { }
  4. public function addPoint($point) {
  5. $this->points[]=$point;
  6. }
  7. ...
  8. ...
  9. }

Konečná práce s modulem pak může vypadat např. takto:

  1. $chart = new PointChart();
  2. for ($i=1; $i<10; $i++) {
  3. $chart->addPoint(new Point($i, rand(0,100)));
  4. }
  5. $chart->setTitle("Popisek grafu");
  6. $chart->save("graf.gif");

Na první pohled spatřuji výhodu OOP v čitelnosti kódu a ve změně způsobu myšlení na rozdíl od procedurálního programování.

Přednosti OOP

1. Čitelnost kódu

Výše zmíněný příklad mi příjde přirozenější a pro člověka čitelnější než změť desítky funkcí a kódu, který by musel programátor kopírovat a znovu vkládat do všech souborů, které se snaží vytvořit nějaký graf. Kód je zapsán tak, že programátor dokáže intuitivně pochopit jak kód funguje aniž by musel hledat další souvislosti nebo pročítat rozsáhlý manuál.

2. Způsob myšlení

Každý člověk má nějakou mentální hranici k pochopení složitého komplexu funkčnosti. OOP přístup umožňuje rozložit komplex na dílčí části a umožnit tak řešit je odděleně, usnadňuje tak mnohem více práci v týmu, kdy se každý člověk může podílet na jiné části, aniž by byli na sobě významně závislí. Stačí jen dopředu definovat objekty, které budeme používat a co od nich chceme.

3. Znovupoužitelnost

Spousty programátorů nedoceňuje tuto vlastnost. Nějak zbastlit programový kód, aby fungoval jak chci je možná zprvu rychlejší a méně náročné než vytvořit objektový model, ale z dlouhodobějšího hlediska se může vyplatit druhý přístup. Už jen příklad s grafy mluví sám za sebe, vykreslit graf může být otázka 5 řádků, a implementace do jiných aplikací nebo jiných skriptů je velice snadná.

4. Skládání objektů

Odkazovat na jiné objekty a využívat jejich funkce se nazývá skládání objektů. Umožňuje nám to kooperaci různých objektů a tím vznikají celky objektů. Objekt Chart bude nejspíše obsahovat referenci na objekt Text, který umí psát do obrázku. Pomocí této reference mohu přikázat objektu text, aby vepsal titulek grafu na horní okraj obrázku. Výhodou je, že objekt Chart vůbec nemusí umět psát do obrázku a snadno tak můžeme přejít na generování grafů do pdf, pokud změníme metody objektu Text a nemusíme se starat, kdo všechno provádí zápis do obrázku.

5. Dědičnost (generalizace)

Probíhá zápisem class PointChart extends Chart { ... }, znamená to, že PointChart je potomkem třídy Chart a tak dědí všechny její metody i vlastnosti. Například třída Chart by měla mít funkce co kreslí osy a ty budou společné jak pro bodový graf tak pro sloupcový. K rodiči můžeme přistupovat pomocí parent::.

 

  1. class HashColor extends Color {
  2. public function __construct($hash="000000") {
  3. $red = hexdec(substr($hex,0,2));
  4. $green = hexdec(substr($hex,2,2));
  5. $blue = hexdec(substr($hex,4,2));
  6. parent::__construct($red, $green, $blue);
  7. }
  8. }

V tomto příkladě jsme použili dědičnosti a přetížení. Nově tedy můžeme používat barvy v hex zápisu, aniž by to mělo vliv na chod celého systému, rozšířili jsme systém o použití hex barev. Například bílý bod v grafu můžeme zapsat takto new Point(10, 20, new HashColor("FFFFFF"));. Třída HashColor udělá nejprve to, že zdědí od třídy Color všechny metody a vlastnosti, poté převede hex barvu na RGB a volá konstruktor rodiče (třídu Chart) s parametry, které rodič očekává.

6. Překrytí (overriding)

Překrytí využíváme při dědičnosti. Potomek sdědí všechny metody a vlastnosti, ale můžeme si vybrat kterou překryjeme, tedy vyměníme její obsah a nadále se bude nová metoda vykonávat podle potomka. Jako v předchozím příkladě s dědičností.

7. Viditelnost

Tento termín znamená, že můžeme jednotlivým funkcím nebo metodám definovat jejich viditelnost pro okolní svět (ostatní objekty). Tedy metody jsou buď dostupné jen uvnitř třídy nebo jsou dostupné navenek. Definujeme to klíčovými slovy private, protected, public (viditelná pro své okolí), více se dozvíte v manuálu. Umožňujeme tak objektům spravovat jejich práva a role uvnitř systému, zoodpovědnost za procesy a odstínění od vlivů jiných objektů.

8. Zapouzdření

Znamená, že funkčnost a způsob provedení procesů je uzavřen uvnitř třídy. Objekty nám poskytují jakési rozhraní, API, které umožňuje jiným objektům komunikovat mezi sebou, aniž by věděli co se děje uvnitř. Definujeme funkce objektu a říkáme ostatním co daný objekt umí a jak mu říci, aby to či ono udělal, už však nemusíme řešit jakým způsobem to udělá. Např. objekt Chart umí přidat objekt Point na osy x a y nebo umí říci objektu Text, aby vypsal na určené místo nějaký text. Objekt Chart neví a nestará se o to jak to Text udělá, nestará se o to jestli píše text do pdf nebo obrázku.

Závěrem

Tento článek neměl být návodem jak se naučit OOP, v tomto případě doporučuji řadu článků na intervalu nebo se inspirovat přímo z manuálu, ale reakcí na časté dotazy typu: proč používat OOP, k čemu je to dobré, vždyť se dá bez toho obejít, jen to zbytečně zdržuje jak lidi tak stroje.

podobné články

07.03.2008Nedocenitelná funkce __autoload v OOP(8%)