Package shell

Motiv

Balíček má posloužit jako základní pracovní rámec vývoje aplikací pro textové prostředí s ohledem na začínající programátory. Vzhledem k tomu, že nepoužívá žádné grafické uživatelské rozhraní, je možné aplikace na tomto základě provozovat s použitím pouhého textového terminálu. Totéž platí i o vývoji, kdy není nezbytné používat ani složité integrované, vývojové editory, které pro nezkušené začátečníky mohou zbytečně rozptylovat od podstaty programovacího jazyka a vlastního kódu. Vývoj je tak možný i s velice nenáročným systémem, ideálně postaveném na jednoduché linuxové distribuci. To vše vytváří ideální a nenákladnou pracovní základnu pro široké spektrum programátorů, vhodnou i pro školní prostředí a výuku programování. Textová forma uživatelského rozhraní vede k lepšímu pochopení podstaty programu, jeho abstrakce, bez odvádění pozornosti k oblasti grafického designu, který s grafickým rozhraním úzce souvisí. Zároveň, způsob ovládání aplikací pomocí příkazů zadaných na terminálu může navést zájem začínajících programátorů k více systémovému přístupu, který tvoří zásadní základnu všech IT aplikací. Motivací k používání příkazové řádky, která mnoho lidí odrazuje, je jednoduchost a rychlost, s jakou lze pomocí tohoto balíčku vytvořit první aplikaci zábavnou formou, kdy ten, kdo určuje, jaké příkazy bude uživatel do příkazové řádky psát je programátor. Přístup k ovládání příkazové řádky systémové, které se mnoho začátečníků obává, pak může být přirozenější a přívětivější. Balíček zároveň uvádí do metodiky, která je pro IT systémy přirozená. Pracuje se stromovými strukturami, cestami, odkazy a podporuje tak abstraktní myšlení, pro IT svět tolik důležité. Účelem textového ovládání programu není nahradit grafické rozhraní. Je to jen alternetiva, která má pomoci začínajícím programátorům ujasnit si hranici mezi (mnohem podstatnější) logikou programu a podružným (ale náročným) designem používaným k jeho ovládání. Aplikaci postavenou v tomto pracovním rámci je možné v budoucnu navázat na jakýkoli jiný způsob ovládání, kdy se datové struktury a logika programu nemusí vůbec měnit. Změnit je možné jen design ovládání, na který může být zaměřena pozornost, až po zvládnutí stavby aplikace. Pro zevrubné pochopení balíčku je k dispozici příklad kompletní aplikace v souboru Main.java. V příkladu je použita většina metodiky balíčku.

Rychlý návod vytvoření aplikace

Příkladová aplikace je v com/hrubec/java/shell/Main.java, kde je možné ji také zkompilovat a spustit (více v dokumentu). Pro její úpravy je vhodné udělat kopii a postupovat podle návodu níže. K vytvoření vlastní aplikace není nezbytné mít nainstalováno vývojové prostředí (IDE). Stačí mít nainstalován JDK. Lze se obejít i bez grafického desktopu, pouze s příkazovou řádkou. V následujícím návodu jsou uvedeny příkazy linuxového terminálu (na jiných OS použijte analogické), ale způsob je na uvážení. Máte-li nainstalován nějaký IDE, použijte jej. Doporučený postup vytvoření vlastní aplikace:

  • spuštění terminálu příkazové řádky
  • přepnutí pracovního adresáře na složku, kde jsou Vaše projekty: cd složkaSProjekty
  • vytvoření vlastní složky projektu s názvem aplikace (například "mojeAplikace" do složky, kde jsou Java projekty): mkdir mojeAplikace
  • přepnutí pracovního adresáře: cd mojeAplikace
  • vytvoření "linku" do nové složky na složku "com"; link musí mít také název "com" 1: ln -s složkaSBalíčky/com com
  • zkopírování šablony aplikace (např. Main.java) do vytvořené složky "mojeAplikace": cp com/hrubec/java/shell/Main.java .
  • úprava zkopírovaného souboru Main.java podle potřeb v jakémkoli editoru prostého textu: nano Main.java
  • kompilace: javac Main.java
  • spuštění: java Main
1 nebo jiného způsobu směrování src, kde jsou umístěné balíčky, včetně com.hrubec.java.shell

Zevrubný popis balíčku

V bloku metody Main.main(java.lang.String[]) má být postavena celá aplikace ze stavebnicových objektů/uzlů vytvořených podle tříd/šablon balíčku popsaných dále. Posledním příkazem bloku je vytvoření kořenového uzlu: new App(args,cmds,(Node)help,(Node)info,null); který spouští hlavní smyčku aplikace. Jeho druhým paramatrem je odkaz na seznam/kontejner příkazů, které budou aplikaci obsluhovat.

Aplikační stavebnice je skládána z objektů kmenu Node (jednotlivé objekty /analogie řádku - záznamu/), nebo Nodes (seznamy/kontejnery jednotlivých objektů /analogie tabulky/). Node a Nodes jsou základní "nosné" typy/třídy, na kterých aplikace stojí. Obsahují základní atributy a metodiku potřebnou pro tvorbu jednoduchých, tabulkových struktur. Node představuje jeden konkrétní objekt/uzel/datový záznam. Nodes je seznam (kontejner) více objektů nebo potomků právě typu Node.

Stavebním kamenem metodiky balíčku je tedy třída Node, od které se odvíjí všechny další konstrukční třídy aplikace. Množina datových atributů potomků, se pak rozšiřuje s kmenem tříd, i když ve výchozím nastavení jsou atributy většinou vypnuty vyhrazenými hodnotami (u objektů je to null, u celočísel -1), kdy i komplexnější třídy "přepnou" na základní metodiku. Odkazy/linky na zkonstruované objekty jsou zpravidla umístěny do více kontejnerů, nebo proměnných, pokaždé v odlišném kontextu. Vícenásobné odkazy je praktické vyrábět metodami kontejnerů Nodes.a(Node), které jen přidají prvek do kontejneru a stejný prvek vrátí, takže je možné v jednom řádku (výrazu) prvek vložit i do více kontejnerů naráz a zjednodušit tak konstrukci celé aplikace.

Kontejnery třídy Nodes mají jen sdružující význam. Třídu ArrayList, jejíž jsou dědici, rozšiřují jen nepatrně a neposkytují metodiku, potřebnou k postavení složitějších struktur. K tomu je potřeba další silné třídy a tou je Branch, potomek kmenu Node,Command. Třída je rozšířena o odkaz na seznam uzlů nižší úrovně, datový atributu Branch.nodes). Instancemi kmenu Branch s linky/odkazy na instance Nodes je již možné stavět celé stromové struktury (dále jen "stromy","větve") bez omezení.

Zkonstruované stromy je pak možné jednoduše procházet základními metodami go() jednotlivých uzlů (Node,Command,Branch,...), které ve výchozím stavu jen tisknou svou textovou identifikaci. Celý strom/větev je možné projít zavoláním metody Branch.go() kořenového uzlu. Při procházení se udržuje v Node.level úroveň zanoření. Standardně se uzly jen tisknou, případně i s odsazením, pokud mají Branch uzly někde v konfiguraci textových atributů přítomen znak "\n". V Branch.nodesBegin nebo Branch.nodesDelimiter je to výchozí hodnota. Procházení stromem je ve výchozím stavu možné kompletně, bez omezení (s vyjímkou rekurze), ale může být také omezeno zadáním cesty. Dobře názorná je analogie se stromem operačního systému (root,složky,"tvrdé" linky), kde jsou uzly jednoznačně identifikovány řetězci názvů oddělených lomítky. V tomto balíčku je to podobné, ale slovní identifikace uzlů mají být oddělené "bílými místy"/mezerami (je možné konfiguračně změnit). Cesty je nutné aplikaci předávat textovým vstupem. Každá cesta končí znakem "\n" O to se stará instance třídy Prompt (většinou jako Branch.prompt), která je navržena tak, aby nejlépe vyhověla potřebám zpracování řetězce příkazové řádky. Instanci Prompt není nutné iniciovat v Main.main(java.lang.String[]) přímo, protože si ji vytvoří třída App sama (popsána níže).

Od třídy Command mohou mít uzly nastaven atribut Command.levelActive, který říká, na jaké úrovni se má uzel přepnout do Command.active() módu a od třídy Branch i atribut Branch.levelNodePath, který říká, na jaké úrovni je uvádět identifikátor/cestu povinné. V módu Command.active(), krom prostého tisku své identifikace, může uzel provádět i nějakou další akci, uvedenou v metodě Command.action(), resp. Branch.nodeAction(). Zpravidla bude žádoucí spolu s Command.levelActive u příkazů/instancí kmene Branch nastavit i Branch.levelNodePath u jejich ovladače (nadřazeného uzlu), aby mohl provádět akci jen jeden konkréní uzel/příkaz. Standardně metoda Branch.go() při nesplnění povinnosti volit konktétní uzel, pouze lokálně přepne mód Command.active() na false a všechny uzly se pak jen tisknou, čímž sdělí uživateli všechny možnosti. To je možné vypnout atribudem Branch.noPathNoAll, kdy se tiskne jen zpráva o povinnosti konktétní uzel zvolit. Metoda Command.action() (resp. Branch.nodeAction()) je standardně ještě "zabalená" do obálky Command.cover() (resp. Branch.nodeCover()), která provedení akce případně podmíní specifickými podmínkami v Command.condition() (resp. Branch.nodeCondition()) a doplní tiskem potvrzovací/chybové zprávy.

Další potomci třídy Node, krom výše popsané základní metodiky (stromy, procházení, akce), poskytují i další, specifičtější metodiku. Instance Detail nese informaci o tom, do jaké kategorie (Detail.master) uzel patří. Detail.master je odkaz na uzel kategorie, který zároveň v některých aspektech, uzel třídy Detail, nahrazuje. Třída Command implementuje přepínání uzlu do aktivního módu (Command.active()), kdy se, namísto prostého tisku, provádí Command.action(), a obálku vykonání akce Command.cover() s testem na splnění podmínky Command.condition() a tiskem odpovědi Command.done, resp. Command.error. Třída Test, potomek Command, upřesňuje action() jako změnu příkazové řádky z klávesnice na soubor, aby bylo možné jednoduše testovat různé situace aplikace, bez nutnosti vše ručně zadávat. Třída Flag, také potomek Command, má svojí Flag.action() nastavenu na přepínání logického atributu Flag.flag. Toho je například využito u hlavní smyčky aplikace v konstruktoru App. Další krok ve smyčce je podmíněn hodnotou !App.stop.flag a po přepnutí příznaku smyčka skončí.

Asi nejpraktičtější jsou potomci rodu Branch, které s kontejnerem Branch.nodes pracují již nadstandardním způsobem. Například Mover přidává metodiku vyjmutí uzlu z jednoho kontejneru Branch.nodes a vložení do jiného Mover.targetNodes. Instance této třídy budou umět přemisťovat uzly mezi kontejnery. Třída Place nese informace o objektech umístěných v místnosti a průchodech do jiných. V Branch.nodes obsahuje větev pro standardní procházení (tisk větve) a dva přímé odkazy na kontejnery, umístěné v této větvi (Place.things,Place.gates). To je užitečné pro přímý přístup ke kontejnerům třídou Placer, která při průchodu do jiné místnosti kontejnery v metodě Placer.placeDependencies() používá. Třída Placer pracuje s několika statickými atributy (Placer.sPlace,Placer.pick,Placer.put,Placer.place). Při průchodu aktualizuje tyto atributy v kontextu aktuální místnosti. Další statický atribut Placer.endPlace třída používá k testování cíle. Její metody Branch.nodeCondition() a Placer.nodeAction() předpokládají, že pracovní atribut Branch.node obsahuje odkaz na průchod (instanci třídy Gate) do jiné místnosti se kterým umí pracovat. Instance Gate implementuje metodu Gate.met() jako splnění podmínky Gate.condition, která podmiňuje průchod do jiné místnosti Gate.place.

Třída App je určena ke konstrukci kořenového uzlu aplikace na konci Main.main(java.lang.String[]) bloku. Na procházení jejích uzlů Branch.nodes jsou aplikována standardní pravidla - pokud nebude některý uzel aktivován, pouze tiskne svou identifikaci, což je využito pro tisk různě komplexních stromových struktur/nápověd atd.. Ostatně i proto je většina datových atributů objektů paralelně umisťována i do Branch.nodes kontejneru, čímž je udržována možnost tisknout různé informační větve. Proto je většina tříd potomkem Branch.

Třídy Prompt a Console jsou vytvořeny pro účely tohoto balíčku a momentálně jsou jeho součástí, ale primcipielně mohou mít i širší, obecnější význam.

Další vývoj

Pokud bude pořeba implementovat novou metodiku, je vhodné zvážit míru obecnosti a případně iniciovat integraci metodiky do stávající struktury tříd nové verze balíčku. Pokud půjde o více specifické chování s nepravděpodobným obecným významem, bude patrně nejefektivnější vytvoření dědice některé třídy a jeho umístění do vlastního projektu. Je pak na zvážení a dohodě s garantem balíčku, zda by byla nová metodika případně vhodná k integraci do základu pracovního rámce.