Domů » Informatika » Objektové programování

Objektové programování

Elementárním prvkem programu vytvořeného pomocí objektového programování je objekt. Objekt je zpravidla koncepčně ucelený soubor proměnných a funkcí pro práci s nimi. Proměnné objektu se nazývají atributy a jeho funkce metody. Programovací jazyk lze považovat za objektový, když podporuje datové abstrakce (1), tvorbu typů (2) a jejich hierarchií (3). Přesněji řečeno:

  1. obsahuje objekty, které jsou datovými abstrakcemi s rozhraním pojmenovaných operací a skrytým vnitřním stavem
  2. každý tento objekt má datový typ
  3. datové typy mohou tvořit hierarchie a své vlastnosti dědit od nadtypu

Objektově orientované programování umožňuje práci v různých úrovních abstrakce. Také dramaticky zvyšuje znovupoužitelnost kódu díky modularitě na úrovni objektů. Každý objekt nebo skupina objektů může být považována za samostatný modul, který lze použít i bez znalosti její vnitřní struktury (ta se může dokonce i v průběhu času nezávisle měnit).

Historie

Historie objektově orientovaného programování začala v šedesátých letech 20. století. Prvním programovacím jazykem s třídami a objekty byla Simula 67, za jejímž vznikem stáli dva norští vědci (Kristen Nygaard, Ole-Johan Dahl). Tento jazyk byl původně určen k simulaci interakcí několika lodí.

Simula 67 inspirovala několik vědců z firmy XEROX, kteří následně vytvořili vlastní jazyk Smalltalk a začali jako první používat termín „objektově orientované programování“. Simula 67 silně ovlivnila i Bjarne Stroustrupa při návrhu jazyka C++.

Cíle

Robustnost

Robustní software by se měl chovat korektně za všech okolností, tedy vracet správné výstupy pro správné vstupy a rozumně reagovat i na ty nesprávné. Program by také pokud možno měl být připraven i na neočekávané události – nekorektní ukončení, ztrátu spojení, nenalezený soubor, nedostatek paměti, apod. Softwarové chyby mohou mít i smrtelné následky. Například v letech 1985 – 1987 ozářil lékařský přístroj Therac-25 šest pacientů, ze kterých tři zemřeli na přímé následky. Všech šest selhání bylo způsobeno nedostatečnou robustností programu.

Přizpůsobitelnost

Některé programy jsou používané nepřetržitě i několik let. Jsou to například bankovní systémy, operační systémy, prohlížeče, ovladače a firmware. Proto musí být možné tento software neustále modifikovat a vylepšovat s minimálním úsilím a rizikem. Přizpůsobitelný software je také možné jednodušeji přenášet na jiné platformy.

Znovupoužitelnost

Stejný kód použitý v různých částech systému snižuje jeho složitost, a tak i náklady na jeho vývoj. Stejně tak je možné kód přenášet i mezi projekty. Často přenášený kód bývá dobře otestovaný v praxi. Touto cestou jdou různé frameworky, které si kladou za cíl usnadnit vývoj nového softwaru a přispět k vyšší disciplinovanosti vývojářů. Projekty na nich postavené ale na druhou stranu přebírají i všechny jejich nedostatky a zvyšují závislost na třetí straně.

Základní pojmy

Objekt (object)

Objekt je základní a nedělitelnou jednotkou objektově orientovaného programování. Každý objekt může představovat skutečný objekt z reálného světa, ale není to podmínkou. Obecně lze však říci, že se jedná o ucelený soubor proměnných a funkcí pro práci s nimi. Každý objekt obsahuje přesně definované veřejné rozhraní, pomocí kterého s ním může komunikovat vnější svět – například zasíláním zpráv nebo voláním metod.

Třída (class)

Třída je schéma, které formálně popisuje vnitřní strukturu objektu a jeho vnější rozhraní. Některé jazyky mají schopnost modifikovat třídy za běhu nebo číst jejich strukturu. Tato schopnost se nazývá reflexe.

Instance (instance)

Instance třídy je datová struktura v paměti počítače, vytvořená na základě dané třídy. Jednotlivé instance se od sebe liší umístěním (adresou) v paměti a hodnotami svých atributů (proměnných). Adresu aktuální instance mívá programátor v objektově orientovaných jazycích k dispozici pod speciálním klíčovým slovem (např. this nebo self).

Typ (type)

Typ je velmi abstraktní pojem, který se v objektovém programování používá jako prostředek pro omezení množiny hodnot, kterých může nabývat určitá proměnná . Toto dobrovolné omezení slouží ke kontrole korektnosti programu a jeho dokumentaci. Ve většině programovacích jazyků jsou některé základní typy (řetězec, číslo, objekt, atd.) předdefinovány a nové typy se vytváří jejich rekurzivním skládáním.

Vzájemné vztahy objektů
Zobecnění

Zobecnění potlačuje rozdíly mezi třídami a zdůrazňuje společné vlastnosti.

generalizace versus specializace

Klasifikace

Klasifikace potlačuje detaily instancí a zdůrazňuje vlastnosti třídy jako celku.

klasifikace versus instanciace

Kompozice

Kompozice potlačuje detaily komponent a zdůrazňuje rysy celku.

kompozice versus dekompozice

Seskupování

Seskupování potlačuje rozdíly jednotlivých objektů ve skupině a zdůrazňuje skupinu jako celek.

seskupování versus individualizace

Dědičnost

Dědičnost je prostředek pro znovupoužití kódu. Umožňuje vytvářet hiearchie mezi třídami. Třída výše v hierarchii se nazývá předek (nadřída, superclass) a třída níže v hierarchii potomek (podtřída, subclass). Potomek může mít i více předků, což někdy vede na tzv. problém diamantu. Proto se ve většině jazyků používá dědičnost jednoduchá. Řešením jsou i tzv. traity, které zjemňují granularitu objektového kódu.

Úspora kódu při vhodném použití dědičnosti je zajištěna tak, že se v potomkovi (C) uvádí jen změny (delta P), které jej odlišují od předka (P).

€€ C = P + \delta P €€

Dědičnost umožňuje programátorovi:

  • bez zbytečné práce a duplicity kódu zachovat to, co bylo v předkovi dobré
  • dodat to, co předkovi chybí
  • změnit to, co mu v předkovi nevyhovuje

Použitím dědičnosti by neměl být porušen tzv. substituční princip Liskové (Liskov substitution principle).

Objektový návrh

Objektový návrh je proces převedení konkrétní úlohy na množinu tříd. Převod vzniká na základě modelování daného problému pomocí objektů. Tato úloha se ukázala jako natolik obtížná, že se vyvinula v samostatný obor. Při objektovém návrhu se často využívají různé obecné postupy a principy, které mají za cíl pomoci k dosažení vyšší kvality výsledného návrhu.

Praktické příklady

Následující příklady ilustrují některé zmíněné pojmy v jazyce Java.

Dědičnost a polymorfizmus
Příklad 1

Nejprve vytvoříme třídu Car (auto). U auta budeme sledovat jeho značku a typ (type) a rychlost (velocity). Dále se u aut chceme dozvědět, zda uvezou náklad o dané hmotnosti (metoda canCarryCargo()). Obyčejné auto obecné neuveze nic, a tak vždy vrátíme false.

kód v jazyce Java - Zobrazit jako TXT, HTML, HTML (okno)

  1. public class Car
  2. {
  3.   private String type;
  4.   private double velocity;
  5.   private int passengers;
  6.  
  7.   public Car (String type)
  8.   {
  9.     this.type = type;
  10.     this.velocity = 0;
  11.     this.passengers = 0;
  12.   }
  13.  
  14.   public boolean canCarryCargo (int weight)
  15.   {
  16.     return false;
  17.   }
  18. }

Nyní využijeme dědičnosti a na základě třídy Car vytvoříme potomka Lorry (dodávka). Tak vytvoříme speciální druh automobilu, který uveze jakýkoliv náklad až do maximální povolené hmotnosti (maxWeight).

kód v jazyce Java - Zobrazit jako TXT, HTML, HTML (okno)

  1. public class Lorry extends Car
  2. {
  3.   private int maxWeight;
  4.  
  5.   public Lorry (String type, int maxWeight)
  6.   {
  7.     super (type);
  8.     this.maxWeight = maxWeight;
  9.   }
  10.  
  11.   @Override
  12.   public boolean canCarryCargo (int weight)
  13.   {
  14.     return (weight <= this.maxWeight);
  15.   }
  16. }
Příklad 2

Nejprve nadefinujeme nejobecnější třídu Person (osoba). Každá osoba bude mít jméno (name) a příjmení (surname). Mohli bychom ještě přidat například datum narození, trvalé bydliště, zemi, zájmy… zkrátka vše, co potřebujeme a co lze pozorovat u každé osoby. Není však vhodné přidávat proměnné jako je plat (salary), protože ne každá osoba pracuje – například některé děti nebo křováci.

kód v jazyce Java - Zobrazit jako TXT, HTML, HTML (okno)

  1. public class Person
  2. {
  3.   public String name;
  4.   public String surname;
  5. //...
  6. }

Pro vytvoření seznamu zaměstnanců nás zajímá pracovní pozice (job), plat (salary) a oddělení (department). Každý zaměstnanec by měl tyto údaje mít. Zároveň je každý zaměstnanec nepochybně osobou – alespoň do doby, než začneme zaměstnávat roboty nebo mimozemšťany.

kód v jazyce Java - Zobrazit jako TXT, HTML, HTML (okno)

  1. public class Employee extends Person
  2. {
  3.   public String job;
  4.   public String department;
  5.   public int salary;
  6. //...
  7. }

Speciálním druhem zaměstnance je vedoucí či manažer (manager). Ten se od obyčejného zaměstnance vyznačuje tím, že má pod sebou nějaké podřízené. Zároveň je však každý vedoucí i obyčejným zaměstnancem.

kód v jazyce Java - Zobrazit jako TXT, HTML, HTML (okno)

  1. public class Manager extends Employee
  2. {
  3.   public List<Employee> subordinates;
  4. //...
  5. }

Nyní vytvoříme seznam všech pracovníků a ukážeme si práci s polymorfizmem. Ten umožní se zaměstnanci (Employee) pracovat jako s osobami (Person) a s vedoucími (Manager) jako zaměstnanciosobami. Všimněte si, že třída A, která dědí od třídy B, od ní zároveň přebírá i veškerou dědičnost.

kód v jazyce Java - Zobrazit jako TXT, HTML, HTML (okno)

  1. public static void main (String[] args)
  2. {
  3.   // vytvořit seznam zaměstnanců
  4.  
  5.   List<Employee> company = new ArrayList<Employee> ();
  6.  
  7.   // přidat několik ukázkových zaměstnanců
  8.   // (do seznamu zaměstnanců NELZE přidávat osoby, ale obráceně to jde)
  9.  
  10.   company.add (new Employee ("Josef", "Novák", "fakturant", "finanční", 1000));
  11.   company.add (new Employee ("Miriam", "Lahodná", "nákupčí", "finanční", 900));
  12.   company.add (new Employee ("Karel", "Vosáhlo", "dělník", "výroba", 600));
  13.   company.add (new Employee ("Ivo", "Starý", "dělník", "výroba", 620));
  14.   company.add (new Manager ("František", "Omáčka", "mistr", "výroba", 800));
  15.   company.add (new Manager ("Tomáš", "Vlček", "majitel", "vedení", 2000));
  16.  
  17.   // vypsat jména všech zaměstnanců
  18.   // (polymorfizmus: zde vystupují zaměstnanci i vedoucí jako osoby)
  19.  
  20.   for (final Person temp: company)
  21.   {
  22.     System.out.println (temp.surname + ", " + temp.name);
  23.   }
  24.  
  25.   // vypsat platy všech zaměstnanců
  26.   // (polymorfizmus: zde vystupují vedoucí jako zaměstnanci)
  27.  
  28.   for (final Employee temp: company)
  29.   {
  30.     System.out.println (temp.salary + "€");
  31.   }
  32.  
  33.   // pár dalších ukázek
  34.  
  35.   Person karel = company.get (2); // zaměstnanec v roli osoby
  36.   Employee frantisek_z = company.get (4); // vedoucí v roli zaměstnance
  37.   Person frantisek_o1 = company.get (4); // vedoucí v roli osoby
  38.   Person frantisek_o2 = frantisek_z; // zaměstnanec v roli osoby
  39. }
Abstraktní třída

Abstraktní třída je speciální třída, která je navržena jako společný základ pro jiné třídy a není možné vytvářet její instance. Navíc je v ní možné definovat tzv. abstraktní metody, což jsou jen hlavičky metod bez implementace. Neabstraktní potomci abstraktní třídy musí tyto metody povinně implementovat.

Nejprve vytvoříme abstraktní třídu Shape (rovinný útvar), který si bude pamatovat svou pozici (x, y). U každého klasického rovinného útvaru lze bezpochyby spočítat obvod (metoda getPerimeter()) a obsah (metoda getArea()). Útvar lze i vykreslit na obrazovku (metoda draw()). U těchto metod však nemůžeme napsat přesnou implementaci, protože se pro každý útvar může lišit. Proto je deklarujeme jako abstraktní (modifikátor abstract).

kód v jazyce Java - Zobrazit jako TXT, HTML, HTML (okno)

  1. abstract public class Shape
  2. {
  3.   protected int x;
  4.   protected int y;
  5.  
  6.   abstract public double getPerimeter ();
  7.   abstract public double getArea ();
  8.   abstract public void draw ();
  9.  
  10.   public Shape (int x, int y)
  11.   {
  12.     this.x = x;
  13.     this.y = y;
  14.   }
  15. }

Nyní implementujeme třídu Rectangle (obdélník). Ta již abstraktní není a proto musí implementovat všechny abstraktní metody, které zdědila. K výpočtu a vykreslení obdélníku musíme znát šířku (width) a výšku (height).

kód v jazyce Java - Zobrazit jako TXT, HTML, HTML (okno)

  1. public class Rectangle extends Shape
  2. {
  3.   private int width;
  4.   private int height;
  5.  
  6.   public Rectangle (int x, int y, int width, int height)
  7.   {
  8.     super (x, y);
  9.     this.width = width;
  10.     this.height = height;
  11.   }
  12.  
  13.   @Override
  14.   public double getPerimeter ()
  15.   {
  16.     return 2 * (this.width + this.height);
  17.   }
  18.  
  19.   @Override
  20.   public double getArea ()
  21.   {
  22.     return this.width * this.height;
  23.   }
  24.  
  25.   @Override
  26.   public void draw ()
  27.   {
  28.     // vykreslit čtyři čáry
  29.   }
  30. }

Dále naimplementujeme třídu Circle (kružnice). Podobně jako obdélník také musí implementovat všechny zděděné abstraktní metody. K výpočtu a vykreslení kružnice musíme znát její poloměr (r).

kód v jazyce Java - Zobrazit jako TXT, HTML, HTML (okno)

  1. public class Circle extends Shape
  2. {
  3.   private int r;
  4.  
  5.   public Circle (int x, int y, int r)
  6.   {
  7.     super (x, y);
  8.     this.r = r;
  9.   }
  10.  
  11.   @Override
  12.   public double getPerimeter ()
  13.   {
  14.     return 2 * Math.PI * this.r;
  15.   }
  16.  
  17.   @Override
  18.   public double getArea ()
  19.   {
  20.     return Math.PI * this.r * this.r;
  21.   }
  22.  
  23.   @Override
  24.   public void draw ()
  25.   {
  26.     // vykreslit body kružnice
  27.   }
  28. }

Krátká ukázka použití, například v grafickém editoru.

kód v jazyce Java - Zobrazit jako TXT, HTML, HTML (okno)

  1. public static void main (String[] args)
  2. {
  3.   // vytvořit seznam útvarů
  4.   // (polymorfizmus: se všemi útvary se pracuje obecně)
  5.  
  6.   List<Shape> shapes = new LinkedList<Shape> ();
  7.  
  8.   // přidat několik ukázkových útvarů
  9.  
  10.   shapes.add (new Circle (0, 0, 10));
  11.   shapes.add (new Rectangle (50, 30, 20, 20));
  12.   shapes.add (new Rectangle (70, 30, 20, 20));
  13.  
  14.   // vykreslit všechny útvary
  15.   // (metodu "draw" tu lze zavolat i na abstraktní třídu, protože každý její potomek obsahuje její implementaci)
  16.  
  17.   for (Shape temp: shapes)
  18.   {
  19.     temp.draw ();
  20.   }
  21. }

Reference