Osnova předmětu IB053 Část A: Efektivita práce při tvorbě programu 1. Snížení chybovosti při tvorbě programu 2. Snížení doby potřebné k odstraňování chyb 3. Využití dříve napsaných částí programu 4. Nezávislost programu na pozdějších úpravách 5. Přenositelnost do jiných prostředí Část B: Efektivita programu 1. Optimalizace algoritmu 2. Mechanismus přístupu k datům 3. Implementace programových struktur 4. Rozdíl v interpretovaných a překládaných jazycích Historie předmětu IB053 Autor předmětu se od roku 1990 komerčně zabývá tvorbou software a z této praxe čerpá většinu zkušeností použitých v předmětu Metody efektivního programování. Protože původně na fakultě informatiky vyučoval programování v jazycích C/C++, využívá též zkušeností získaných na cvičeních k tomuto předmětu. Výuka C/C++ byla postupně obohacována o doporučení či metody jak (ne)programovat. Po několika letech autor přestal zcela vyučovat programovací jazyk a začal učit předmět Metody efektivního programování. Obsah tohoto předmětu se také postupně vyvíjí s novými zkušenostmi z praxe, ale též od studentů, kteří se na přednáškách zapojují do diskusí a vnášejí tak na problematiku nový pohled. Cvičení ● Napsání programu porušujícího co nejvíce zásad ● Srovnání efektivity C++ Win/Linux, Java, C#, PHP na shodném HW (dvě úlohy: výpočet nad daty v paměti, zpracování textového souboru) ● Vytvoření týmového díla (C++ Win/Linux, Java) – každý tým bude mít jednoho koordinátora (rozděluje práci, organizuje týmové porady, analýzu), účast v týmu = podmínka zápočtu Organizační pokyny ● rezervovány jsou 3 hodiny namísto dvou, ale počet hodin za semestr je vždy počet týdnů semestru krát dvě a tedy výuka v některých týdnech se neuskuteční -> letos neplatí ● rozdělení přednášek a cvičení určuje vyučující průběžně Část A: Efektivita práce při tvorbě programu K čemu efektivita: Čas = peníze 1. Snížení chybovosti při tvorbě programu ● Důkladná analýza ​(shora dolů) ○ funkční ​(rozdělení celého systému na komponenty, jejich rozdělení na moduly/packages, moduly na třídy + interface, třídy na metody) ○ datová​ (objekty, DB-tabulky, UML ​ <- víme všichni co to je?​) S využitím programových prostředků pro zápis analýzy/modelování dat (Enterprise Architect, Papyrus, ArgoUML), některé umí generovat zdrojový kód, některé ze zdrojového kódu rekonstruovat datový model. Model na papíře je mnohem cennější než jen v počítači nebo jen v hlavě - je do něho vidět a dá se do něho čmárat. ● Dostatek času​ (podvědomí pracuje za nás) ● Práce v týmu​ (Brainstorming) ● Programování ​(zdola nahoru ​<- to se hezky řekne, ale špatně udělá​nebo spíše odprostřed dolů a pak odprostřed nahoru → aby se dalo co nejdříve ladit) K ladění jakékoli funkce/metody potřebuji vstupní data, která nemám, pokud programuji odspodu -> nutnost psát kód, jehož jediným smyslem je testování a poté se zahodí. S využitím programových prostředků JUnit, TestNG (Java), Arquillian (Java EE), NUnit (.NET) ​<- vždy platí, že kvalita testu je přímo úměrná úplnosti sad testovacích dat ● Dobrá znalost programovacího jazyka ​(chyby z neznalosti jazyka se špatně hledají) char * strcpy ( char * destination, const char * source ); char * rezetec; strcpy(retezec, “AHOJ”); ● Dobrá znalost vývojového prostředí a využívání jeho možností ○ refaktoring – přejmenování, přesunutí ○ Prostředky pro sestavování aplikace (MAKE, ANT, MAVEN) ○ přesunutí kurzoru na definici funkce/třídy ○ vyhledání všech výskytů (volání funkce, užití proměnné) ● Soustředěnost při práci ​(kolegové v kanceláři, hudba, TV, maily, prohlížení webu) 2. Snížení doby potřebné k odstraňování chyb (ladění) (Analýza cizího programu, analýza vlastního programu s časovým odstupem) (Člověk by neměl psát programy tak, aby se stal obětí své vlastní lenosti!) ● Čitelný zápis programu bez „hutných“ pasáží s mnoha vedlejšími efekty for (int i = 0; suma(pocet = index++) < max; new_line(i++) ) {…} v jazyce C: int i; … if (!i) { … } ​správně je:​ if (i != NULL) ... - složité logické podmínky rozdělit na jednodušší - zamezit negaci negovaného - boolean metody vracející pozitivní stav (hasRestriction x hasNoRestriction) - nesnažit se za každou cenu zjednodušit logický výraz de Morganovými pravidly - zápis pak neodpovídá logice věci např. !(a && !b) → !a || b v zadání je: nesmí platit, že (​a​ a současně neplatí ​b​) špatně by se pak kontrolovalo, že !a || b odpovídá zadání ● Výpočty prováděné překladačem 2 * 3.14 6.28 #define POCET_ZNAKU_ADRESY (40 + 30 + 5) #define POCET_ZNAKU_ADRESY 75 ● Symbolické konstanty ​(správná interpretace, hromadná změna; nevyužívat k jiným účelům) jmeno[POCET_ZNAKU_JMENA + 1]; prijmeni[POCET_ZNAKU_JMENA + 1];​- použití k nesprávnému účelu jmeno[POCET_ZNAKU + 1];​- nejednoznačná konstanta ● Parametry procedur, proměnné ​(výstižné, stručné, jednoznačné, pravdivé názvy, používat pouze k jednomu účelu) promennaObsahujiciPocetPrvku pocetPrvku pocet poc p p q r x y i j k ​– ​za určitých okolností lze (kdy?) příklady použití velkých a malých písmen: proměnná; Metoda(); PublicMetoda(); privateMetoda(); KONSTANTA víceslovnáProměnná x víceslovná_proměnná iPocet, fVelikost, sJméno ● Globální proměnné ​(nepřehlednost) ● Komentáře (stručné, jasné, pravdivé, neplýtvat, nekomentovat jasné věci (i++; apod.)), a = findMax(array); // najdeme největší prvek a = findMax(array); // tady v tomto místě musíme najít // prvek, který je v tom poli největší a = findMax(array); // největší a = findMax(array); // vytiskneme největší prvek i++; // i zvýšíme o 1 i++; // jak by zněl smysluplný komentář? jak se dostane do programu nepravdivý komentář ? co komentovat: ○ obtížné pasáže ○ jednotlivé bloky kódu provádějící ucelenou činnost ○ interface (hlavičky metod, rozhraní tříd, modulu) ● Odstraňovat příčiny chyb, nikoli eliminovat jejich důsledky ● Použití Exceptions → zpřehlednění kódu (ale používat opravdu jen k řešení výjimečného stavu, nikoli jako řešení stavu, který je sice význačný, ale žádoucí, např. tabulka neobsahuje žádnou větu) ● Styl zápisu ​(jednotný – graficky, odlišení proměnných, metod, konstant) (často je řízen IDE - lze konfigurovat) (zejména při práci v týmu je potřeba jednotnost stylu zápisu) Není až tak podstatné, jaký styl zápisu zvolíme, podstatné je ho pak dlouhodobě dodržovat. Příklady pro inspiraci (nikoli doporučení pro styl zápisu): grafický styl bloku (odsazování): záhlaví { } záhlaví { } záhlaví { } záhlaví { } Mezery (​každá neobvyklost musí mít své opodstatnění​): for​​(​​int​​i​​=​​0​​;​​i​​<​​5​​;​​i++​​)​​třída.metoda​​(​​x​​); Přechody na nový řádek příkaz 1; příkaz 2; příkaz 3; příkaz 1; příkaz 2; příkaz 3; ● Práce v týmu (je potřeba tým umět vést a je potřeba umět být členem týmu) - síla řetězu závisí na každém článku v praxi: ​nepracuje-li jeden člen kvalitně, snižuje to kvalitu práce i dalších členů​, např. když dám týmu neodladěné zdroj. texty funkce, kterou volá funkce jiného člena týmu, tak mu to nebude fungovat, ale nebude vědět, jestli je chyba v jeho kódu, zejména ve způsobu volání té funkce a nebo chyba až ve funkci - ke kvalifikovanému rozhodnutí je často potřeba jít až do zdroj. textu té funkce nebo v debuggeru funkci prokrokovat 3. Využití dříve napsaných částí programů Motivace: použití vlastního (resp. týmového) zdrojového kódu vícekrát použití cizího zdrojového kódu napsání zdrojového kódu, který může využít cizí osoba ● vhodné rozčlenění programu (třídy/moduly aplikačně nezávislé, částečně závislé, závislé) ● při psaní jakéhokoli programu je potřeba neustále myslet na to, jestli se právě psaný kód nebude ještě někdy hodit a tomu přizpůsobit způsob psaní ● obecný algoritmus vždy vyčlenit z méně obecného (aplikačně závislého kódu) – zejména u často používané funkčnosti lišící se jen např. zpracovávanými daty (​nejhorší je hotový zdrojový kód okopírovat a mírně upravit​) ● neslučovat do třídy/modulu/knihovny nesouvisející funkčnost ● OOP: používat abstraktní metody, interface ● tvořit knihovny (zdokumentované) ● využití částí programů napsaných cizími osobami (získané z internetu, např. www.sourceforge.net​, ​apache.org​, github, maven-repository) ● využití cizích knihoven (volně šířených i placených) - např. pro konfigurační soubory (vyskytuje se prakticky v každém programu) - pro uložení dat (nejlépe SQLite, rovněž prakticky každý program potřebuje ukládat data) - XML (v Javě JaXB) ● zdrojové texty nekopírovat do jiných projektů, ale sdílet v rámci možností, které dává použitý jazyk a vývojové prostředí ● při úpravách kódu, který je sdílen mezi více projekty, dodržovat zpětnou kompatibilitu 4. Nezávislost programu na pozdějších úpravách ● symbolické konstanty ​(zde za účelem parametrizace programu) ● dobře rozdělený program do modulů a funkcí nebo tříd (změna v jedné části programu se nesmí projevit jako vedlejší efekt v úplně jiné části programu) ● prozíravost; odhad, co se reálně může v zadání později změnit (nedůvěra je vysoce pozitivní vlastnost tvůrce software) 5. Přenositelnost programu do jiných prostředí Myšleno: použití jiného kompilátoru nebo dokonce překlad pod jiným operačním systémem. ● orientace na standardní prvky jazyka, případně dané verze/standardy jazyka (C++ 1998, C++ 2003, C++11 apod., v Javě 1.5 - 1.8, v jiných jazycích existuje spousta kompilátorů, které více nebo méně dodržují standardy, v C++ např. pozor na Templates – nejsou všude) ● použití pouze standardních knihoven, tříd a funkcí (v Javě je prakticky všechno standard, u C/C++ bývají rozdíly větší) ● vyčlenit platformově (co to je? → Windows/Linux, KDE/GNOME atd.) nebo jinak závislé části ○ podmíněný překlad (co to je?, v Javě není – řeší se pomocí interface a tříd implementující daný interface, tyto třídy se případně umístí do různých jar-souborů) ○ konstanty pro podmíněný překlad v jednom souboru nebo případně ve vlastnostech projektu – závisí na vývojovém prostředí, některá prostředí mohou v projektu mít víc konfigurací lišící např. právě předdefinovanými konstantami ○ moduly ve více verzích ○ rozdělené UI-dialogy: návrhový vzor Model-View-Controller Server | Klient | | ​View (layout) ​platformově závislá část | -------------------------------------------------------------------------------------------------------------------------- | Model | Controller​ ​(chování – reakce na události) ​platformově nezáv. část (zobrazená data) ● znalost implementačních detailů (např. ​soubor s binárními daty) ○ implementace reálných čísel nemusí být vždy stejná ○ rozsahy datových typů (integer, long apod.) ● spolupráce mezi částmi programů napsaných v různých prostředích/jazycích (kompilovaných) – další implementační detaily Programm – DLL (pozdní vazba – řeší OS) Modul – Modul (volání – řeší kompilátor) ○ pořadí parametrů ○ implementace datových typů (reálná čísla, rozsah integeru) ○ umístění prvků ve struktuře ● používat konstrukce nezávislé na implementačních detailech nebo případně závislé konstrukce předdefinovat na jednom místě (zač. modulu, hlavičkový soubor) (počet bitů v int, short, long apod.) ~7 6. Další doporučení ● Pořádek ve zdrojovém textu i po dokončení → využitím refaktoringu (pokud vývojové prostředí umožňuje) přejmenovat proměnné, metody i třídy, přesunout metody mezi třídami nebo dokonce moduly nebo projekty ● Používat návrhové vzory (Singleton, Object-pool, Lazy-initialization, Observer, Model-View-Controller, DAO) ● Jakmile něco nefunguje podle očekávání, hledat pomoc na internetu vhodně formulovaným dotazem na google (málokdy se stane, že jsme první, kdo danou situaci řeší). Vysoká pravděpodobnost nalezení řešení je na ​stackoverflow.com​. ● Použití sofistikovaných prostředků k úpravě zdrojových textů (editor Sublime) ● Ve zdrojovém textu využívat prostředky k tomu, k čemu jsou určeny např. For-cyklus: for (​​inicializace proměnné cyklu​ ​;​​podmínka pro další iteraci​ ​; přechod na další iteraci​) for (int i = 0; i < počet; i++) for (String str = inputString; !str.isEmpty(); str = str.substring(pozice)) ​← ​(toto je ještě na hranici únosnosti) for (File f = new File(name); osoba.getPlat() > 20000.0; osoba.hledej(hledOsoba)) ● Využití enumerace v cyklu (pokud jí jazyk disponuje – Java od 1.6, C# nebo např. PHP for (int i = 0; i < seznamOsob.size(); i++) { Osoba osoba = seznamOsob.get(i); … } for (Osoba osoba : seznamOsob) { … } ● Každá větev ve ​switch​(Java, C/C++, C#, PHP aj.) má mít svůj ​break pokud nemá, je potřeba okomentovat switch (druh) { case POCITAC: … break; case NOTEBOOK: ​(zde nebude komentář, protože to není samostatná větev) case NETBOOK: … break; case TABLET: … // dále stejně jako u smartphone: ​(okomentováno nepoužití ​break​) case SMARTPHONE: … break; case MOBIL: … } ​(poslední větev ​break​nepotřebuje, ale měl by se uvést! Proč?) ● Parametrizace programu pomocí konstant v projektu – případně s využitím podmíněného překladu, pokud jsou požadovány i odchylky ve funkčnosti (ideálně pokud vývojové prostředí umožňuje definovat více konfigurací projektu) – použije se v případě, že je potřeba udržovat více verzí jednoho programu (projektu) např. pro více zákazníků, kteří mají specifické požadavky (​nejhorší alternativou je okopírovat všechny zdrojové texty do jiného adresáře a tam upravit​) ● Umět negovat podmínku (​!a && !b → a || b a nikoli a && b) ​(pokud už to z nějakého důvodu je potřeba) ● Udržování aktuálností databáze, struktury konfiguračního souboru - databáze, konf. soubor apod. má v sobě jako jednu z položek aktuální verzi a v programu musí být kód, který porovná tuto verzi s verzí programu a v případě nesouladu ví, co všechno musí aplikovat, aby výsledná struktura byla aktuální - tools pro databáze: liquibase, flywaydb ● Goto? (existuje, ale používat v opodstatněných případech) (každý program s lib. množstvím goto lze přepsat, tak aby neobsahoval jediné)