Vloženo:

Úkol 08 - Vzor Repository

Pokračujte v appce z minulého úkolu a zapracujte do řešení třídu podle návrhového vzoru Repository, která bude poskytovat data pro HlavniController. Připravte si různé druhy Repository. Přinejmenším jednu, která bude udržovat data v paměti (například v Listu), a jednu, která bude uchovávat data v souboru (například ve formátu .csv).

Úkol bude rozdělen do několika částí.

  1. (Nepovinně) dokončení mazání a přidávání nových záznamů
  2. (Nepovinně) tvorba vlastní Repository
  3. Adaptování existující paměťové Repository z dema z lekce
  4. Adaptování existující souborové Repository z dema z lekce

Části, kde můžete experimentovat, jsou označené jako nepovinné. Pokud tedy nemáte chuť objevovat nebo nemáte dost času, lze domácí úkol projít relativně snadno.

Stejně tak, pokud nemáte hotovou předchozí domácí úlohu, ale chcete si vyzkoušet práci s Repository, můžete vyjít z mého řešení minulé domácí úlohy.

Detailní popis částí

  1. (Nepovinně) dokončení mazání a přidávání nových záznamů

    Budeme potřebovat, aby vaše aplikace uměla přidávat a mazat záznamy. Ne jen editovat. Pokud jste v minulé domácí úloze nenaprogramovali mazání a přidávání nového záznamu (protože to bylo označeno jako nepovinné), můžete buď:

    • dokončit tuto funkcionalitu v svojí úloze sami
    • nebo adaptovat kód z mého řešení předchozího domácího úkolu
    • nebo začít rovnou s mým řešením předchozího domácího úkolu. To se vám může hodit obzvlášť tehdy, pokud nemáte čas nebo nechcete experimentovat.

    Moje řešení předchozího domácího úkolu najdete buď v odevzdávárně nebo zde: Ukol-07-Kamil_Sevecek.zip

  2. (Nepovinně) tvorba vlastní Repository

    V minulém domácím úkolu jsme data ukládali v seznamu (java.util.List) přímo ve třídě HlavniController. V reálných aplikacích se používá návrhový vzor Repository, jak jsme si ho ukazovali v lekci. Cílem je mít třídu/objekt, který zapouzdřuje operace pro vyhledání záznamů, update, mazání a přidávání. Tento objekt není nijak vázaný na to, zda je součástí webové aplikace, desktopové aplikace, konzolové aplikace nebo třeba androidové aplikace. Funguje vždycky univerzálně. Zároveň jsou metody navrženy tak, aby nebyly nijak specifické pro žádný druh uložení dat. To znamená, že data mohou být uložena v paměti, v souboru, v databázi, přes REST API nebo třeba zašiforvané na čipové kartě.

    Zkuste si tedy zrefaktorovat vaši minulou aplikaci a vytvořit vlastní třídu KontaktRepository, ve které implementujete tyto metody:

     public class KontaktRepository {
    
         public synchronized List<Kontakt> findAll();
         public synchronized Kontakt findById(Long id);
         public synchronized Kontakt save(Kontakt zaznamKUlozeniNeboPridani);
         public synchronized void delete(Long id);
    
     }
    

    Neprogramujte žádný clone(…) ani kopírování dat z vnitřního seznamu, jak jsem to měl já ve vzorových Repositories. Prostě jen napište metody nejjednodušším způsobem, aby to fungovalo. V metodě findAll() bohatě stačí, když vrátíte seznam záznamů z vnitřního Listu (bez kopírování dat).

    Tato část je nepovinná a je určena pouze pro ty, kteří si chtějí zaprogramovat (takže například já sám :-). Je ale velmi doporučená, protože refaktorováním se získává programátorská zručnost.

  3. Adaptování existující paměťové Repository z dema z lekce

    Bez ohledu na to, zda jste programovali předchozí část, podívejte se do demo appky z lekce. Uvidíte tam 2 repository. Uvažujme zatím jen tu paměťovou. Úkolem bude adaptovat takovouto repository do vašeho programu. Tedy aby pracovala s vaším typem Kontakt (namísto Clanek).

    UPDATE: V demu v lekci byla chyba. Prosím, stáhněte si z webu novou verzi Lekce08.zip

    Pokud jste programovali předchozí část, zkuste jen vylepšit svoje už existující metody tak, aby používaly metodu clone(…) a při vracení dat vždycky dělali kopii vnitřně udržovaných dat. Pokud jste předchozí část neprogramovali (a tedy nevytvářeli svoji vlastní KontaktRepository), použijte moji třídu z demo appky a upravte ji.

    Proč se používá metoda clone(…) a veškerá data, která se vracejí z KontaktRepository, se kopírují? Říká se tomu defenzivní programování a je to technika, která se používá, pokud máte mezi objekty vztah nadřízený-podřízený (HlavniController-KontaktRepository) a podřízený vrací odkaz na nějaká data, která managuje (objekty typu Kontakt). Pokud totiž podřízený (KontaktRepository) vrátí odkaz na datový objekt (Kontakt), který má měnitelné vlastnosti, mohl by nařízený (HlavniController) změnit objekt (voláním setterů) a podřízený (KontaktRepository) by se o tom nedozvěděl. Když ale podřízený (KontaktRepository) nikdy nevrátí odkaz na “živý” objekt (Kontakt), ale jen jeho kopii (clone(…)), nemůže nadřízený (HlavniController) způsobit podřízenému (KontaktRepository) v jeho privátně držených datech (objektech Kontakt) žádný nepořádek.

  4. Adaptování existující souborové Repository z dema z lekce

    Podobně jako v minulé části, nyní adaptujte souborovou repository z demo appky. Můžete ji sice zkusit naprogramovat sami, je to ale trochu dlouhé, proto očekávám, že spíš použijete moje metody a upravíte je. Souborová repository z demo appky z lekce používá k ukládání soubor data.csv ve formátu:

     100,"Nazev clanku","Autor clanku",2019-12-31
    

    Vaše souborová repository asi bude potřebovat zhruba tento formát souboru:

     100,"Jmeno","Telefonni cislo","Email"
    

    Je zcela zásadní, aby i souborová repository měla nachlup stejné hlavičky metod, jako paměťová repository, protože pak v HlavniController půjde jednoduše zaměnit paměťovou a souborovou repository. Až bude souborová repository hotová, ověřte, že to tak ve vaší appce lze udělat.

    Budete potřebovat metodu na převod řádku ze souboru (String) na objekt Kontakt. Když se podíváte na demo appku z lekce (SouborovaRepositoryProClanky.java; řádky 13 a 21-28), najdete tam parsování (rozdělování) řádku pomocí regulárního výrazu (tzv. regex) a vyzvednutí hodnot v tomto regulárním výrazu pomocí skupin (group). Téma regulárních výrazů je poněkud komplexní, proto nepožaduji, abyste uměli sami sestavit takový regulární výraz, který správně rozdělí řádek ze souboru na jednotlivé skupiny znaků, které se potom dají předat do objektu Kontakt. Abyste se tím nemuseli zabývat, zde je metoda, která prostě převede řádek na Kontakt. V demo appce by bylo vhodné řádky 21-28 nahradit voláním takovéto metody. Pokud byste měli jiný formát dat, napište mi a já vám regulární výraz připravím na míru.

         public static final Pattern REGEX_RADKU = Pattern.compile("([0-9]+)[,;]\"(.*?)\"[,;]\"(.*?)\"[,;]\"(.*?)\"");
    
         private Kontakt prevedRadekNaKontakt(String radek) {
             Matcher regularniAutomat = REGEX_RADKU.matcher(radek);
             if (!regularniAutomat.find()) return null;
    
             Kontakt jedenKontakt = new Kontakt();
             jedenKontakt.setId(Long.parseLong(regularniAutomat.group(1)));
             jedenKontakt.setJmeno(regularniAutomat.group(2));
             jedenKontakt.setTelefonniCislo(regularniAutomat.group(3));
             jedenKontakt.setEmail(regularniAutomat.group(4));
             return jedenKontakt;
         }
    

Odevzdání domácího úkolu

Nejprve appku/appky zbavte přeložených spustitelných souborů. Zařídíte to tak, že zastavíte IntelliJ IDEA a smažete podsložku target v projektu. Nesmíte mít IntelliJ IDEA zapnutou, protože projekt má nastaven automatický překlad a hned by se tam zase vytvořila. Následně složku s projektem zabalte pomocí 7-Zipu pod jménem Ukol-CISLO-Vase_Jmeno.7z. (Případně lze použít prostý zip, například na Macu). Takto vytvořený archív nahrajte na Google Drive do Odevzdávárny.

Pokud byste chtěli odevzdat revizi úkolu (např. po opravě), zabalte ji a nahrajte ji na stejný Google Drive znovu, jen tentokrát se jménem Ukol-CISLO-Vase_Jmeno-verze2.7z.

Termín odevzdání je dva dny před další lekcí, nejpozději 23:59. Tedy pokud je další lekce ve čtvrtek, termín je úterý 23:59. Pokud úkol nebo revizi odevzdáte později, prosím pošlete svému opravujícímu kouči/lektorovi email nebo zprávu přes FB.