# Objektově orientované programování

### Úvod

V této kapitole si ukážeme, jak vytvářet a pracovat s objekty.

### Třídy

Jsou základním stavebním kamenem objektového programování, kde jsou definované části, které se soustředí na konkrétní úkoly. Výhodou objektového programování je přehledné rozdělení programu na menší celky, jejich možnou recyklace napříč různými projekty (pokud budu mít třídu, která dokáže spočítat objem těles pro program pro Matematiku, mohu tuto třídu recyklovat i pro program pro Fyziku), program se zároveň stává přehlednější a tím se dodržují programovací konvence.

Třída je obecný předpis nějakého celku, například člověk je tvořený různými vlastnostmi (má nějakou výšku, hmotnost, barvu vlasů a nějaký den se narodil) a má nějaké funkce (umí mluvit, rozpoznat barvy). Z takového předpisu lze tvořit objekty / instance, což jsou již konkrétní lidé vycházející z obecného předpisu.

<div class="el-tabs__header is-top" id="bkmrk-zde-je-uk%C3%A1zka-p%C5%99edpi"><div class="el-tabs__nav-wrap is-top"><div class="el-tabs__nav-scroll"><div class="el-tabs__nav is-top" role="tablist"><div aria-controls="pane-0" aria-selected="true" class="el-tabs__item is-top is-active" id="bkmrk-zde-je-uk%C3%A1zka-p%C5%99edpi-1" role="tab" tabindex="0">Zde je ukázka předpisu člověka s jeho vlastnostmi a funkcemi v programovacím jazyce C#</div></div></div></div></div>```c#
class Human
{
  public string name;
  public DateTime dateOfBirth;
  public void Mluv()
  {
    Console.WriteLine("Ahoj");
  }
}
```

<div class="el-tabs__content" id="bkmrk-"><div aria-labelledby="tab-0" class="el-tab-pane" id="bkmrk--1" role="tabpanel"></div></div>V ukázce výše je vidět jak se třída vytváří. V jejím těle je několik proměnných a jedna funkce.

Aby bylo možné využít proměnné a funkce, je potřeba vytvořit z této třídy objekt.

<div class="el-tabs__header is-top" id="bkmrk-t%C3%ADmto-zp%C5%AFsobem-jsme-"><div class="el-tabs__nav-wrap is-top"><div class="el-tabs__nav-scroll"><div class="el-tabs__nav is-top" role="tablist"><div aria-controls="pane-0" aria-selected="true" class="el-tabs__item is-top is-active" id="bkmrk-t%C3%ADmto-zp%C5%AFsobem-jsme--1" role="tab" tabindex="0">Tímto způsobem jsme vytvořili objekt "adam" ze třídy Human</div></div></div></div></div>```c#
Human adam = new Human();
```

<div class="el-tabs__content" id="bkmrk--2"><div aria-labelledby="tab-0" class="el-tab-pane" id="bkmrk--3" role="tabpanel"></div></div>Naše třída Human obsahuje i několik proměnných, které jsou v současné době nenaplněné. Pro každou proměnnou je důležité vytvořit pravidlo na způsob ukládání takových dat.

<p class="callout info"><span style="background-color: rgb(241, 196, 15);">**Třída**</span> - předpis, <span style="background-color: rgb(241, 196, 15);">**Instance**</span> - objekt vytvořený z předpisu</p>

**\# Konstruktor**

Jedním ze způsobů jak uložit do proměnných hodnoty je použití konstruktoru, konstruktor je předpis událostí, které se provedou v případě vytváření objektu. Chová se a vypadá jako metoda, ale nedokáže vracet žádnou hodnotu. Pokud chceme využít konstruktor, musí se jmenovat jako jeho třída ve které je použitý.

<div class="el-tabs__header is-top" id="bkmrk-vyu%C5%BEit%C3%AD-konstruktoru"><div class="el-tabs__nav-wrap is-top"><div class="el-tabs__nav-scroll"><div class="el-tabs__nav is-top" role="tablist"><div aria-controls="pane-0" aria-selected="true" class="el-tabs__item is-top is-active" id="bkmrk-vyu%C5%BEit%C3%AD-konstruktoru-1" role="tab" tabindex="0">Využití konstruktoru pro získání jména a následně nastavení data narození na aktuální čas</div></div></div></div></div>```c#
class Human
{
  public string name;
  public DateTime birthDay;
  public Human(string name)
  {
    this.name = name;
    birthDay = DateTime.Now;
  }
}
```

<div class="el-tabs__content" id="bkmrk--4"><div aria-labelledby="tab-0" class="el-tab-pane" id="bkmrk--5" role="tabpanel"></div></div>Pokud teď budeme vytvářet instanci třídy, budeme muset vložit argument.

<div class="el-tabs__header is-top" id="bkmrk-nyn%C3%AD-je-ulo%C5%BEeno-v-ob"><div class="el-tabs__nav-wrap is-top"><div class="el-tabs__nav-scroll"><div class="el-tabs__nav is-top" role="tablist"><div aria-controls="pane-0" aria-selected="true" class="el-tabs__item is-top is-active" id="bkmrk-nyn%C3%AD-je-ulo%C5%BEeno-v-ob-1" role="tab" tabindex="0">Nyní je uloženo v objektu adam v proměnné name hodnota "Adam" a nastaven datum na aktuální čas vytvoření</div></div></div></div></div>```c#
Human adam = new Human("Adam");
```

<div class="el-tabs__content" id="bkmrk--6"><div aria-labelledby="tab-0" class="el-tab-pane" id="bkmrk--7" role="tabpanel"></div></div>**\# Vlastnosti**

Je člen který se chová podobně jako proměnná, ale na rozdíl od proměnné se vlastnost definuje pomocí bloku, který obsahuje proceduru *Get* nebo *Set*, případně jednu z nich. Používají se jako veřejné datové členy, u kterého chceme ovlivnit možnost čtení, nebo zápisu.

<p class="callout warning">Vlastnost se vždy označuje velkým písmenem nebo CamelCase </p>

<div class="el-tabs__header is-top" id="bkmrk-deklarace-vlastnosti"><div class="el-tabs__nav-wrap is-top"><div class="el-tabs__nav-scroll"><div class="el-tabs__nav is-top" role="tablist"><div aria-controls="pane-0" aria-selected="true" class="el-tabs__item is-top is-active" id="bkmrk-deklarace-vlastnosti-1" role="tab" tabindex="0">Deklarace vlastnosti: 1. řádek vlastnost umožňuje jak zápis, tak čtení 2. řádek vlastnost navenek umožňuje pouze čtení a uvnitř třídy je možné nastavit</div></div></div></div></div>```c#
class Human
{
  public string Name { get; private set }
  public string Data { get; set; } 
}
```

<div class="el-tabs__content" id="bkmrk--8"><div aria-labelledby="tab-0" class="el-tab-pane" id="bkmrk--9" role="tabpanel"></div></div>**\# Get &amp; Set**

Jedná se o proceduru vlastnosti, nebo také přistupující objekty vlastnosti. Tvoří blok pro inicializaci vlastnosti. Díky těmto objektům je možné provádět různé úkony v průběhu čtení, nebo zápisu dat do této vlastnosti, nebo je možné tyto přístupy omezit.

<div class="el-tabs__header is-top" id="bkmrk-v-tomto-p%C5%99%C3%ADkladu-se-"><div class="el-tabs__nav-wrap is-top"><div class="el-tabs__nav-scroll"><div class="el-tabs__nav is-top" role="tablist"><div aria-controls="pane-0" aria-selected="true" class="el-tabs__item is-top is-active" id="bkmrk-v-tomto-p%C5%99%C3%ADkladu-se--1" role="tab" tabindex="0">V tomto příkladu se zaznamenává čas přistupu a nastavení hodnoty vlastnosti "Data"</div></div></div></div></div>```c#
class Human
{
  public DateTime lastView;
  public DateTime lastEdit;
  public string data;
  public string Name { get; private set; }
  public string Data
  {
    get
    {
      lastView = DateTime.Now;
      return data;
    }
    set
    {
      lastEdit = DateTime.Now;
      data = value;
    }
  }
}
```

<div class="el-tabs__content" id="bkmrk--10"><div aria-labelledby="tab-0" class="el-tab-pane" id="bkmrk--11" role="tabpanel"></div></div>Pro fungování tohoto řešení je potřeba, aby vedle vlastnosti byla vytvořená totožná proměnná. Procedura *Get* musí vracet nějakou hodnotu a procedura *Set* musí přijmout a uložit hodnotu. Pokud by jsme použili pouze jednu proměnnou, došlo by k zacyklení. Pokud budu zapisovat nějaká data, zavolá se *Setter*, v tomto settru je ale právě zápis do proměnné a tedy znovu se bude volat *Setter* a tímto způsobem dojde k zacyklení.

<div class="el-tabs__header is-top" id="bkmrk-chybn%C3%BD-zp%C5%AFsob-pou%C5%BEit"><div class="el-tabs__nav-wrap is-top"><div class="el-tabs__nav-scroll"><div class="el-tabs__nav is-top" role="tablist"><div aria-controls="pane-0" aria-selected="true" class="el-tabs__item is-top is-active" id="bkmrk-chybn%C3%BD-zp%C5%AFsob-pou%C5%BEit-1" role="tab" tabindex="0">Chybný způsob použití vlastnosti a její procedury Get &amp; Set, která vede k zacyklení</div></div></div></div></div>```c#
public string Data
{
  get
  {
    lastView = DateTime.Now;
    return Data;
  }
  set
  {
    lastEdit = DateTime.Now;
    Data = value;
  }
}
```

<div class="el-tabs__content" id="bkmrk--12"><div aria-labelledby="tab-0" class="el-tab-pane" id="bkmrk--13" role="tabpanel"></div></div>### Dědičnost

Vedle zapouzdření a polymorfismu je dědičnost dalším z pilířů OOP a slouží k recyklaci starých datových struktur, díky kterým vytvoříme nové podřazené datové struktury. Díky dědičnosti se také ušetří spousta repetitivní práce. Dědičnost v programování je podobná, jako dědičnost například u lidí. Naši předci měli nějaké společné rysy a ty dědíme, vycházíme tedy z nějakého předpisu a během života získáváme vlastní zkušenosti a vlastnosti, které mohou zdědit naši potomci.

Pokud budeme chtít vytvořit třídy různých typů uživatelů v případě bez možnosti dědičnosti vypadal by kód zhruba takto

<div class="el-tabs__header is-top" id="bkmrk-ani-to-nemus%C3%ADte-%C4%8D%C3%ADst"><div class="el-tabs__nav-wrap is-top"><div class="el-tabs__nav-scroll"><div class="el-tabs__nav is-top" role="tablist"><div aria-controls="pane-0" aria-selected="true" class="el-tabs__item is-top is-active" id="bkmrk-ani-to-nemus%C3%ADte-%C4%8D%C3%ADst-1" role="tab" tabindex="0">Ani to nemusíte číst stačí se kouknou na délku kódu</div></div></div></div></div>```c#
class User
{
  public string name;
  public string password;
  public string position;
  public bool Log = false;
  public int age;
  public bool LogIn(string password)
  {
    if (this.password == password)
    {
      Log = true;
      return true;
    }
    else { return false;}
  }
  public void WriteFile()
  {
    ///
  }
}
class Admin
{
  public string name;
  public string password;
  public string position;
  public bool Log = false;
  public int vek;
  public bool LogIn(string password)
  {
    if (this.password == password)
    {
      Log = true;
      return true;
    }
    else { return false; }
  }
  public void WriteFile()
  {
    ///
  }
  public void DeleteFile()
  {

  }
  public void AddFile()
  {

  }
}
```

<div class="el-tabs__content" id="bkmrk--14"><div aria-labelledby="tab-0" class="el-tab-pane" id="bkmrk--15" role="tabpanel"></div></div>}  
Ale "naštěstí" C# umí i dědičnost a tím dokážeme takovéto věci řešit mnohem efektivněji. Jak jsme si řekli, dědičnost je vlastně sdílení podobností a to přesně můžeme použít zde, kdy každý administrátor je zároveň i uživatelem, jen **Admin** umí trochu více věci.

Dědičnost v programování děláme pomocí znaku ' : ' (dvojtečka)

<div class="el-tabs__header is-top" id="bkmrk-zde-je-vid%C4%9Bt-zna%C4%8Dn%C3%A1-"><div class="el-tabs__nav-wrap is-top"><div class="el-tabs__nav-scroll"><div class="el-tabs__nav is-top" role="tablist"><div aria-controls="pane-0" aria-selected="true" class="el-tabs__item is-top is-active" id="bkmrk-zde-je-vid%C4%9Bt-zna%C4%8Dn%C3%A1--1" role="tab" tabindex="0">Zde je vidět značná úspora programování a tedy i času a zároveň se kód stává kratší (nemusí se stát přehlednějším)</div></div></div></div></div>```c#
class User
{
  public string name;
  public string password;
  public string position;
  public bool Log = false;
  public int age;
  public void LogIn(string password)
  {
    if (this.password == password)
    {
      Log = true;
      return true;
    }
    else{ return false; }
  }
  public void WriteFile()
  {
    ///
  }
}
class Admin : User
{       
  public void DeleteFile()
  {

  }
  public void AddFile()
  {

  }
}
```

<div class="el-tabs__content" id="bkmrk--16"><div aria-labelledby="tab-0" class="el-tab-pane" id="bkmrk--17" role="tabpanel"></div></div>V ukázkách výše je vidět jasná úspora místa a program dělá to samé. V případě druhé ukázky **Admin** získal schopnosti a vlastnosti od **User**, tedy přihlásit se a psát do souboru, navíc má další vlastnosti které však **User** nemá. Vytváří se tím i určitá hierarchie.

<p class="callout info">Každý **Admin** je **User**, ale né každý **User** je **Admin**. </p>

Díky této hierarchii je možné **Admina**, přetypovat na **Usera**, ale nikoliv naopak. Při přetypování však **Admin** přijde o vše co uměl navíc. Jelikož každý vytvořený objekt, je zároveň referenčním datovým typem, je možné ho přetypovat, ale je potřeba dodržovat pravidla nadřazených a podřazených tříd.

V případě dědění může dojít k určité nejasnosti a to v případě konstruktoru nadřazené třídy. I na to je však myšleno, využívá se k tomu klíčové slovíčko již nám známá ' : ' a nové klíčové slovíčko **base**, které odkazuje na nadřazenou třídy. Je tedy nutné, aby oba měli konstruktor.

<div class="el-tabs__header is-top" id="bkmrk-t%C3%ADmto-zp%C5%AFsobem-zajis"><div class="el-tabs__nav-wrap is-top"><div class="el-tabs__nav-scroll"><div class="el-tabs__nav is-top" role="tablist"><div aria-controls="pane-0" aria-selected="true" class="el-tabs__item is-top is-active" id="bkmrk-t%C3%ADmto-zp%C5%AFsobem-zajis-1" role="tab" tabindex="0">Tímto způsobem zajistíme, aby vše proběhlo v pořádku a třídy si mezi sebou předali data pro konstruktor</div></div></div></div></div>```c#
class User
{
  public string name;
  public User(string name)
  {
    this.name = name;
  }
}
class Admin : User
{           
  public Admin(string name) : base(name)
  {
    this.name = name;
  }
}
```

<div class="el-tabs__content" id="bkmrk--18"><div aria-labelledby="tab-0" class="el-tab-pane" id="bkmrk--19" role="tabpanel"></div></div>**\# Zapouzdření**

V některých případech však budu chtít omezit přístup k proměnným, vlastnostem nebo funkcím třídy, včetně podřazených tříd, nebo jen umožnit přístupu dědicům. K tomu je potřeba znát **modifikátory přístupu**.

<p class="callout warning">Public - veřejná (přístup mimo třídu včetně dědiců)</p>

<p class="callout warning">Private - soukromá (přistup pouze uvnitř třídy)</p>

<p class="callout warning">Protected - chráněná (přístup pouze uvnitř třídy a dědicům)</p>

<p class="callout warning">Static - statická (přístup pouze statickým prvkům a nelze je volat z instance)</p>