Diskuze: Lekce 14 - Interface (rozhraní v PHP)
Zpět
Komentáře

Člen

Zobrazeno 10 zpráv z 21. Zobrazit vše
Komentáře
Tady jde o to, že ty nemusíš vůbec vědět, o jakou třídu jde, ale stačí ti, že implementuje dané rozhraní (které říká, že ta třída musí mít danou metodu/y).
Zkusim trochu jiný příklad. Představ si objekt, který bude někam ukládat data. Můžeš je ukládat např. do databáze MySQL, do textového souboru, do paměti atd. Pak to zjednodušeně může vypadat třeba takhle
interface IDataStorable
{
public function save($key, $value);
public function delete($key);
public function load($key);
}
// jelikož třídy implemetují rozhraní, musí obsahovat všechny tři metody
class MySQLStorage implements IDataStorable
{
public function save($key, $value)
{
mysql_query("INSERT INTO tabulka (...)");
}
public function delete($key)
{
mysql_query("DELETE FROM tabulka ...");
}
public function load($key)
{
mysql_query("SELECT data FROM tabulka WHERE $key ...");
}
}
class FileStorage implements IDataStorable
{
private $file;
public function __construct()
{
$this->file = fopen("nějaký soubor");
}
public function save($key, $value)
{
fwrite($this->soubor, $value);
}
public function delete($key)
{
// něco na smazání ze souboru
}
public function load($key)
{
// něco na získání dat ze souboru
}
}
class MemoryStorage implements IDataStorable
{
private $data = array();
public function save($key, $value)
{
$this->data[$key] = $value;
}
public function delete($key)
{
unset($this->data[$key]);
}
public function load($key)
{
return $this->data[$key];
}
}
define("DEBUG", TRUE);
if (DEBUG) {
// pro testovací účely
$storage = new MemoryStorage();
$storage->save("test", "nějaká hodnota");
} else {
// ostrá DB
$storage = new MySQLStorage();
}
// zde v kódu už vůbec nemusím řešit, jestli se jedná o nějaký debug nebo ne
// protože vím, že ať je to objekt pracující s databází MySQL nebo jen s pamětí,
// musí obsahovat tři výše uvedené metody
// takže nemusim dávat hromadu dalších podmínek a snadno použít
echo $storage->load("test");
Je to jen malý hloupý příklad, v reálu by to bylo trochu jinak, ale pro pochopení je to snad dostačující.
V podstatě je to šablona pro třídy co musí obsahovat za metody. Co jsem se dočetl tak atributy rozhraní neobsahuje. V praxi to podle mě jde i bez rozhraní. Možná je to pro přehlednost ve třídách je lepší když ve třídě kde je více metod(abych na metodu rozhraní nezapomněl). Jde prostě o to aby se neopomněla nějaká metoda.
Mám takový dotaz, jestli to chápu dobře, tak interface musí mit metody vždy public. Když jsem dal private tak mi to vyhubovalo.
Ano musí Základní
myšlenka rozhraní je, že se k němu bude přistupovat zvenčí, privátní
metody nemají smysl.
Tak tady jsme se fakt kousnul , jak určím z jaké třídy je metoda "programuj" ?
interface Programator
{
public function programuj();
}
Jak z jaké třídy? Třída je nad rozhraním. První definuješ rozhraní, potom teprve třídu. Tebe nemusí zajímat, jakou třídu máš, pro tebe je důležité, že implementuje požadované rozhraní.
... impementací interface do třídy zajistím to že daná třída bude obsahovat metody přdepsané v interface ? Všechny třídy které implementují interface "IProgramator" budou mít (až je tam dopíšu) stejné metody (např. "pracuj" , "programuj"), je to tak?
TL:DR:
Rozhraní má i v PHP své místo a může vám v reálných aplikacích dost
pomoci. Bohužel ukázat takové použití bez předvádění složitějších
projektů je celkem nemožné, a tak se k využívání rozhraní musíte dostat
sami. Zkrátka si pamatujte, co rozhraní jsou a jak se používají a třeba ve
správný okamžik odhalíte, že ho na daném místě máte použít. 😉
Plná verze:
Při programování webových aplikací v PHP se mi docela osvědčilo vytvořit
si rozhraní DatabaseItem
, které implementovaly všechny modely
(viz MVC) reprezentující položky v databázi
(Uzivatel
, Komentar
, Clanek
) a
předepisovalo metody jako:
save()
- Vloží nebo aktualizuje danou položku v
příslušné databázové tabulce podle vlastnosti $id
load()
- Načte vlastnosti instance z příslušné
databázové tabulky podle vlastnosti $id
delete()
- Odstraní položku z databáze a
unset
ne všechny vlastnosti instancePozději se rozhraní změnilo na abstraktní třídu a všechny výše
zmíněné metody již nepotřebovaly mít pro správnou funkci známé ID
položky, avšak danou akci zkrátka provedly na jakémkoli řádku, u nějž se
hodnoty ve sloupcích shodovali s hodnotami vyplněných vlastností instance.
Šlo poté například vytvořit objekt Clanek
jenom s vyplněnou
vlastností URL a poté buďto načíst ostatní vlastnosti pomocí
$clanek->load()
, nebo článek smazat pomocí
$clanek->delete()
. URL sice v tomto případě byla unikátní
hodnota, avšak při vyplnění většího množství vlastností, které nebyly
pro každou položku unikátní to také fungovalo.
Jak jsem toto rozhraní a později abstraktní třídu vyvíjel se můžete podívat zde (rozklikněte si budoucí revize toho souboru). Nejnovější verze, která se rozrostla na 524 řádků dobře okomentovaného kódu máte tady: https://www.itnetwork.cz/…lighter/1498.
Jenom jsem poté narazil na problém, jak odlišovat hodnoty, které nejsou
vyplněné/načtené z databáze od hodnot, které jsou v databázi uloženy
jako NULL
. Vyřešil jsem to tak, že jsem si udělal tuto
třídu:
final class undefined {}
a její instance ukládal do vlastností, které nemám načtené.
null
jsem poté ponechal vlastnostem, které tak byly uložené i v
databázi. Přišlo mi to jako docela elegantní řešení, i když odpověď na
StackOverflow, která mi toto poradila dostala docela dost downvotů.
Zobrazeno 10 zpráv z 21. Zobrazit vše