Úvod
Úvodem bych chtěl podotknout, že tento článek neslouží jako nějaký tutoriál k jazyku C++, čili nebude zde vysvětleno, co dělají některá klíčová slova apod. Pokusím se klást důraz na vysvětlení způsobu návrhu, což znamená, že ten, kdo umí programovat v nějakém jiném jazyce, bude v tomto jazyce schopen program se stejným návrhem napsat. Jinak řečeno, mým cílem je vysvětlit logiku celého problému.
Struktura v paměti
Předpokládám, že jste si již přečetli zadání a víte tedy, co se od programu očekává. Jen pro připomenutí, jedná se o virtuální souborový systém (dále jen VSS), který pracuje s adresáři a textovými soubory. Celý systém je reprezentován pouze v paměti, čili nepracuje s pevným diskem počítače. Znamená to tedy, že v paměti se bude vytvářet jakási struktura, lépe řečeno stromová struktura. Vzhledem k tomu, že počet položek v nějakém adresáři není omezen, nelze tedy použít například binární strom. Nelze vlastně použít žádný strom, kde dopředu známe počet potomků nějakého uzlu. Nabízí se tedy tzv. n-ární strom.
Návrh tříd
Jak tedy začít? Již jsem se zmínil, že souborový systém pracuje s adresáři a textovými soubory, proto se nabízí vytvoření tříd Directory a File (nebo chcete-li Adresář a Soubor). Co se týče členských proměnných a metod jednotlivých tříd, dostanu se k nim za chvíli. Další otázkou je, jak zajistit, aby bylo možné s adresáři a soubory zacházet jednotným způsobem. Odpověď je snadná - stačí ke třídám Directory a File vytvořit nějakou bázovou třídu, v mém případě jde o třídu s názvem BaseObject. Tato třída obsahuje všechny metody, které adresář a soubor potřebují (např. přidání nového adresáře, vypsání obsahu souboru atd.). Metody této třídy jsou čistě virtuální, což má za následek, že není možné vytvořit objekt této třídy, je však možné vytvořit pointer (ukazatel). To je velmi důležité, protože celý program funguje na základě ukazatelů. Ve funkci Main je vytvořen kořenový adresář, na který po spuštění programu ukazuje ukazatel s názvem currentPath. Tento ukazatel je velmi důležitý, neboť po celou dobu ukazuje na aktuální adresář, ve kterém se právě nacházíme. Při startu programu je to tedy kořenový adresář, ale při zadávání jednotlivých příkazů se tento ukazatel mění dle potřeby.
Třída Directory
Zaměřme se nyní blíže na třídu Directory. Každý adresář má mít nějaké svoje jméno, datum a čas vytvoření. Je tedy jasné, že toto budou členské proměnné. Vzhledem k tomu, že soubor také má své jméno, datum a čas vytvoření, je možné tyto proměnné přidat do třídy BaseObject a třídy Directory a File je zdědí. Další věcí, kterou je nutno vyřešit, je způsob, jak zajistit, aby si každý adresář pamatoval, co obsahuje. Tento problém jsem vyřešil třídou List, která představuje seznam položek, uložených v adresáři. Je samozřejmé, že objekty této třídy má každý adresář, počínaje kořenovým.
Třída List
Třída List obsahuje celkem čtyři ukazatele - next, previous, parrent, item. Ukazatel parrent ukazuje na adresář, ve kterém se daná instance třídy List nachází, jinak řečeno, ukazuje na nadřazený adresář. Toho se využívá při zjišťování plné cesty nějakého adresáře, kdy se rekurzivně zjišťuje nadřazený adresář až do doby, než se dojde ke kořenovému adresáři. Ukazatele next a previous ukazují na další, respektive předchozí objekt třídy List. Zbývá ukazatel item - ten ukazuje buď na adresář, nebo soubor. Nově vytvořený adresář tedy obsahuje jednu instanci třídy List, přičemž ukazatele next, previous a item mají hodnotu NULL, jelikož právě vytvořený adresář je prázdný. Při přidání první položky (položkou se rozumí adresář nebo soubor) se inicializuje ukazatel item, čili ukazuje na právě přidanou položku. Ukazatele next a previous mají stále hodnotu NULL. Pokud dojde k přidání další položky, vytvoří se nová instance třídy List, na kterou ukazuje ukazatel next. Tato nová instance opět obsahuje všechny čtyři ukazatele, kde item ukazuje na druhou přidanou položku, previous na předchozí objekt třídy List a next má zatím hodnotu NULL (v adresáři jsou teprve dvě položky). Řekneme-li to velmi laicky a jednoduše, třída List je jakási krabička, ve které je právě jedna položka a každá krabička ví, kde je další a předchozí krabička. Všechny tyto krabičky jsou v jedné velké krabici, čímž je samozřejmě myšlen nadřazený adresář.
Metody třídy List
Práce s celým VSS probíhá na základě uživatelem zadaných příkazů. Každému příkazu odpovídá nějaká metoda třídy List. Třída Directory má také tyto metody, ty však nedělají nic jiného, než že volají patřičné metody třídy List. Většinu metod zde vysvětlovat nebudu, neboť si myslím, že nejsou tak složité, abyste je nedokázali pochopit sami.
Za zmínku stojí snad jen metoda ListChangePath, která umožňuje pohyb v celém VSS (volá se při příkazu cd). Tato metoda přebírá jako parametry ukazatel na adresář, ve kterém se právě nacházíme, a samozřejmě relativní cestu, kam se chceme dostat. Z relativní cesty si metoda zjistí první adresář (cesta je např. Texty/Dopisy/Soukromé, první adresář je tedy ten s názvem Texty) a vyhledá jej v daném adresáři. Pokud nebude hledání úspěšné, metoda vypíše chybovou hlášku a vrátí NULL. V opačném případě vrátí ukazatel na adresář Texty a celá metoda se volá rekurzivně, kde prvním parametrem bude právě onen ukazatel na Texty a relativní cesta bude už jen Dopisy/Soukromé. Není těžké si domyslet, že rekurze končí, pokud jsme úspěšně našli všechny adresáře v relativní cestě, tudíž metoda vrátí ukazatel na poslední vyhledávaný adresář (v našem případě Soukromé), čímž je dokončen pomyslný přesun v celém souborovém systému.
Třída File
Nyní zbývá jen krátký popis třídy File. Tato třída obsahuje všechny metody, které má adresář, protože je dědí z třídy BaseObject. Tyto metody je nutné přepsat, protože jinak by třída File byla abstraktní, čili by nebylo možné vytvořit žádný objekt této třídy. Samozřejmostí je, že se souborem není možné pracovat jako s adresářem, proto jsou metody přepsány tak, aby nic nedělaly. K jejich volání v programu však nikdy nedojde, proto to nevadí.
Závěr
Pokud jste si kód sami prošli, určitě jste si všimli, že jsem zde nezmínil úplně všechno. Jak jsem ale již v úvodu psal, šlo hlavně o způsob návrhu. Netvrdím, že kód je napsán úplně přesně podle mých představ, některé věci by ještě určitě šly vylepšit, proto budu rád za každou připomínku.