Přidej si svou IT školu do profilu a najdi spolužáky zde na síti :)

Základní best practices pro návrh softwaru

Návrh Best practices Základní best practices pro návrh softwaru

ONEbit hosting Unicorn College Tento obsah je dostupný zdarma v rámci projektu IT lidem. Vydávání, hosting a aktualizace umožňují jeho sponzoři.

V dnešní lekci designu softwaru se budeme věnovat tzv. best practices. To jsou obecně známé dobré praktiky v návrhu softwaru, které se během let osvědčily a pokud je budeme znát, ušetří nám spoustu nepříjemností. Všichni jste se určitě někdy setkali se špatně napsaným softwarem, připomínajícím domeček z karet. Jakákoli změna v takovéto aplikaci je pracná a nebezpečná, protože může způsobit další chyby a ohrozit i budoucnost projektu. Abychom se takových návrhovým chybám vyvarovali, nemusíme vynalézat kolo, stačí se poučit ze známých chyb ostatních a předejít jim. Dobrých praktik existuje pro návrh softwaru poměrně velké množství, my si dnes představíme ty nejzákladnější.

KISS

Pravidlo KISS ve vývoji softwaru

Začněme těmi nejjednoduššími a postupně přejdeme ke komplexnějším poučkám. Pravidlo KISS je akronym pro "Keep It Simple, stupid!". Česky tedy "Dělej to jednoduše, hlupáku!" KISS naznačuje, že často existuje řešení, které je jednoduché, relativně málo pracné a přinese uspokojivý výsledek. Tendenci ke komplikování aplikací mají zejména zákazníci, kteří nerozumí vnitřní struktuře aplikací a požadavky si vymýšlejí jak je to napadne. Bývá dobrým zvykem prodiskutovat nutnost nebo podobu alespoň některých požadavků. Často zjistíte, že zákazník vlastně potřebuje něco jiného, co umíte vyřešit elegantněji.

Příklad

Jako příklad si uveďme např. soukromé zprávy zde na ITnetwork. V době jejich programování měl Facebook u svých soukromých zpráv nějaký javascriptový mechanismus pro načítání dalších zpráv směrem nahoru a udržování formuláře pro psaní nové zprávy dole pod nimi. Zeptali jsme se sami sebe, zda toto opravdu potřebujeme programovat a záhy nás napadlo otočit pořadí zpráv. Formulář na novou zprávu byl byl nahoře a pro načítání nových zpráv se použil již existující skript, který načítá data AJAXem směrem dolů. Najednou jsme získali stejnou funkcionalitu za zanedbatelný vývojový čas, jen proto, že se změnil směr řazení. Toto jsou přesně situace, kdy se vyplatí promluvit se zákazníkem, pokud neprogramujete software pro sebe, případně zadání ještě zvážit.

Řešení soukromých zpráv od Facebooku

Řešení soukromých zpráv od Facebooku

Řešení soukromých zpráv od ITnetwork

Řešení soukromých zpráv od ITnetwork

Z vlastní zkušenosti mohu potvrdit, že software je stále složitějším a složitější, časem budete zjišťovat, že ve své aplikaci potřebujete další a další funkčnosti. Když je udržíte jednoduché, získáte konkurenční výhodu nad firmami, které vše bastlí přesně podle představy někoho, kdo IT nerozumí, a ve svém kódu se již téměř nevyznají.

SRP

Single Responsibility Principle, zkráceně SRP, říká, že každý kus kódu, např. třída, by měl být zodpovědný za jednu konkrétní věc. Když složíme aplikaci ze součástí, kdy se každá součást zaměřuje na jednu funkčnost a tu dělá dobře, máme poměrně vysokou šanci na úspěch.

SRP úzce souvisí s přidělením odpovědnosti. Tomu jsme se do hloubky věnovali ve skupině návrhových vzorů GRASP.

Příklad

Praktikování SRP je např. tvorba manažerů pro různé entity v aplikaci, svůj kód rozdělujeme mezi třídy SpravceZakazniku, SpravceFaktur, SpravceSkladu a podobně. Každá třída je odpovědná za své entity. Nemáme jen jednoho Spravce.

SRP můžeme praktikovat i na metody, i když to není základní princip této poučky. Každá metoda by měla provádět jednu věc a její činnost bychom měli být schopni popsat bez spojky "a". Pokud si odpovíme něco ve smyslu "Tato metoda načte, vyfiltruje a zobrazí výsledky", měla by být metoda rozdělena na více metod.

SoC

Separation Of Concerns, česky Rozdělení zájmu, je princip podobný SRP. Zde se ovšem zpravidla zaměřujeme na širší oblasti. Zatím, co SRP odděluje např. práci s fakturami od práce se zákazníky, SOC odděluje typicky aplikační logiku od prezentace. To znamená, že logické operace by měly být soustředěné v jiné části aplikace, než např. výpis dat uživateli. Určitě víte, že se hovoří o rozdělení aplikace do vrstev, o kterém si můžete blíže přečíst v kurzu Softwarové architektury a depencency injection. Známé architektury jako MVC, MVP, MVVM a podobně jsou vše implementace SOC.

DRY

Don't Repeat Yourself, česky Neopakujte se, je jeden z nejdůležitějších principů v programování. Zejména začátečníci mají tendenci kopírovat kód z jednoho místa programu na druhý. Jakmile se ve vaší aplikaci vyskytují kdekoli 2 stejné bloky kódu nebo třeba i 2 podobné kusy kódu, je automaticky špatně.

Příklad

Tato chyba je tak častá, že využijeme část jedné hry, která nám byla do redakce zaslána. Konkrétně jde o přepínání obrázků (spritů) podle směru a velikosti postavičky.

// smer doprava
if (smer == 0) {
        if (obrazek == 1)
                div.className += "spriteLiveRightL";
        if (obrazek == 2)
                div.className += "spriteLiveRightM";
        if (obrazek == 3)
                div.className += "spriteLiveRightS";

        div.style.left = -200 + "px";
}

// smer doleva
if (smer == 1) {
        if (obrazek == 1)
                div.className += "spriteLiveLeftL";
        if (obrazek == 2)
                div.className += "spriteLiveLeftM";
        if (obrazek == 3)
                div.className += "spriteLiveLeftS";

        div.style.left = sirkaObrazovky + 100 + "px";
}

Na první pohled vidíme, že se 2 bloky kódu liší minimálně. Kód je jistě možné minimálně zkrátit jen na:

if (smer == 0) {
        let nazevSmeru = "Right";
        div.style.left = -200 + "px";
}
else {
        let nazevSmeru = "Left";
        div.style.left = sirkaObrazovky + 100 + "px";
}

if (obrazek == 1)
        div.className += "spriteLive" + nazevSmeru + "L";
if (obrazek == 2)
        div.className += "spriteLive" + nazevSmeru + "M";
if (obrazek == 3)
        div.className += "spriteLive" + nazevSmeru + "S";

Kód výše funguje úplně stejně, ale má mnohem méně duplicit. Představte si, že by se změnilo pojmenování obrázku z "..Right..." a "...Left..." na "R" a "L". Podívejte se, kolik kódu by se muselo přepsat u příkladu porušujícího DRY a kolik u opraveného příkladu. Kód ovšem stále není ideální.

DRY není jen o duplicitním kódu, ale o opakování jako takovém. Bezduchá dlouhá větvení bývají téměř vždy nahraditelná chytřejšími konstrukcemi, obvykle cykly nebo polem. Udělejme další úpravu:

if (smer == 0) {
        let nazevSmeru = "Right";
        div.style.left = -200 + "px";
}
else {
        let nazevSmeru = "Left";
        div.style.left = widthOfScreen + 100 + "px";
}

let obrazky = [1: "L", 2: "M", 3: "S"];
div.className += "spriteLive" + nazevSmeru + obrazky[obrazek];

V našem případě jsme si pojmenování velikosti postavičky "L", "M" a "S" uložili do slovníku k náhodnému číslu, kterým se velikost (obrázek) vybírá. Není poté nic jednoduššího, než vybrat písmenko velikosti podle klíče slovníku.

Nyní na kód aplikujme také pravidlo KISS. Obrázky můžeme totiž jednoduše pojmenovat číselně, aby směr a velikost v jejich názvu souhlasily s reprezentací těchto hodnot v kódu. Proč to dělat složitě? Obrázky stejně nejsou určené pro uživatele. Výsledek:

div.style.left = smer ? (sirkaObrazovky + 100 + "px") : (-200 + "px");
div.className += "spriteLive" + smer + "_" + obrazek;

Kód se zkrátil na 2 řádky z původních 18(!) a dělá úplně to samé. To vše jen za pomoci KISS a DRY. Představte si, co se stane, když Best practices aplikujete na celou aplikaci. Najednou nemusíte psát desítky tisíc řádků kódu a převálcujete svou konkurenci za několik měsíců. Dobré praktiky určitě nepodceňujte :)

Ukázkou DRY by mohly být carousely na ITnetwork, které lze různě nastavovat:

Carousel na ITnetwork

Carousel s fotografiemi

Carousel s jiným nastavením na ITnetwork

Carousel s HTML obsahem

To jistě ještě není nic velkolepého. Vnitřně ale navíc tyto carousely dědí z komponenty, která přepíná záložky:

Tabcontrol jako předek pro ITnetwork carousel

Tabcontrol se zdrojovými kódy algoritmu pro různé jazyky

Mnoho programátorů by napadlo napsat carousel a záložky zvlášť, ale když se blíže zamyslíte, dělají v podstatě to samé, jen vypadají jinak. Jedna komponenta lze vytvořit jen menší obměnou té druhé. Dalšími příklady DRY by mohlo být např. vytvoření kvalitních obecných CSS stylů, které nejsou omezené na konkrétní prvky na stránce a lze je parametrizovat, jako to má např. CSS framework Bootstrap:

<table class="table table-striped">

Výše uvedený kód nastaví tabulce základní styl a zároveň pruhování.

Shy

Možná znáte poučku:

Keep it DRY, shy, and tell the other guy.

Česky volně přeloženo jako "Neopakuj se, piš stydlivě a řekni to ostatním." DRY a opakování jsme si již vysvětlili, ale jak je to se stydlivým kódem? Stydlivý kód se chová úplně stejně, jako stydliví lidé. Komunikuje s ostatním kódem tedy jen když je to nezbytně potřeba a nemá ani více informací o ostatních, než sám nezbytně potřebuje. SHY je vlastně jiné označení pro zákon Deméter, viz dále.

LoD

Law of Demeter, tedy zákon Deméter, řecké Bohyně úrody, je v podstatě o low/loose coupling, tedy o udržování nejmenšího možného počtu vazeb mezi komponentami. Je definován třemi pravidly, kde zavádí pojem jednotka pro zapouzdřenou část kódu, tedy pro třídu:

  • Každá jednotka by měla mít omezenou znalost o ostatních jednotkách, pouze jednotkách, které jsou dané jednotce blízké.
  • Každá jednotka by měla "mluvit" pouze se svými "přáteli", nemluvte s cizími.
  • Mluvte pouze se svými přímými přáteli.

Z pravidel je vidět, že každý objekt by měl být univerzální a naprosto odstíněný od zbytku aplikace. Znát minimum informací o celku a sdílet minimum informací o sobě celku.

Příklad

Praktický příklad si určitě dokážete představit. Jedná se o třídy s dobře zapouzdřenou vnitřní logikou a obecnou funkčností, aby nemusely znát detaily konkrétního systému a bylo je možné použít kdekoli. Např. vytvoříte generátor objednávek tak, aby jej bylo možné 100% přizpůsobit a nebyl závislý na potřebách jednoho informačního systému. Možná vás napadá, zda není plýtvání vývojovým časem a penězi vytvářet funkce, které v daném projektu nepotřebujete. To by plýtvání určitě bylo, s těmito funkcemi stačí jen počítat a navrhovat komponenty tak, aby do jich šly v budoucnu snadno dopsat. LoD se týká také předávání závislostí, kterých by mělo být co nejméně a třída by měla záviset na abstrakcích, ne konkrétních třídách. O tom hovoří Dependency Inversion Principle, který je součástí pouček SOLID.

IoC

Princip Inversion of Control byste měli dobře znát. Jedná se o skupinu návrhových vzorů, jejíž součástí je i populární Dependency Injection, jediný správný způsob jak v objektových aplikacích závislosti předávat. IoC tvrdí, že objekty v aplikaci by měly být řízené nějakým vyšším mechanismem, který vytváří instance tříd a předává jim potřebné závislosti. To je opačný přístup ke staršímu způsobu práce se závislostmi, kdy si třídy své závislosti vytahovali sami ze systému, např. přes Singletony nebo jiné antipatterny. Jelikož se jedná o extrémně důležitou problematiku, připravili jsme pro vás o DI samostatný kurz Softwarové architektury a depencency injection, kde je podrobně vysvětlena na reálných příkladech.

Mezi populární dobré praktiky patří také poučky SOLID a STUPID, které jsou akronymy pro další praktiky a proto jim je věnován samostatný článek.

Máte nějaké poučky, které s oblibou rádi dodržujete? Máte zajímavou zkušenost s best practices? Podělte se s ostatními do komentářů pod článek :)


 

 

Článek pro vás napsal David Čápka
Avatar
Jak se ti líbí článek?
11 hlasů
Autor pracuje jako softwarový architekt a pedagog na projektu ITnetwork.cz (a jeho zahraničních verzích). Velmi si váží svobody podnikání v naší zemi a věří, že když se člověk neštítí práce, tak dokáže úplně cokoli.
Unicorn College Autor se informační technologie naučil na Unicorn College - prestižní soukromé vysoké škole IT a ekonomie.
Miniatura
Všechny články v sekci
Best practices pro návrh softwaru
Miniatura
Následující článek
Praktiky SOLID
Aktivity (2)

 

 

Komentáře

Avatar
Daniel Vítek
Tým ITnetwork
Avatar
Daniel Vítek:28. září 20:43

Jsi hledal nejbezpečnější zprávu, kterou můžeš zveřejnit, a dal jsi tam tu moji? :D Nechci vidět ostatní chaty :D

 
Odpovědět  +1 28. září 20:43
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na Daniel Vítek
David Čápka:28. září 20:58

Tak trochu :P

Odpovědět 28. září 20:58
Miluji svou práci a zdejší komunitu, baví mě se rozvíjet, děkuji každému členovi za to, že zde působí.
Avatar
Pavol Hejný
Autoredaktor
Avatar
Odpovídá na David Čápka
Pavol Hejný:4. října 1:58

Skvělý článek, ještě aby ten js kód byl perfektní, mohl bys odstranit opakování toho +'px' a zrušit dynamické přetypovávání.

Takhle to je v článku:

div.style.left = smer ? (sirkaObrazovky + 100 + "px") : (-200 + "px");

Podle mě lepší řešení:

div.style.left = ( smer ? sirkaObrazovky + 100  : -200 ).toString() + 'px';
 
Odpovědět 4. října 1:58
Avatar
Taskkill
Šéfredaktor
Avatar
Odpovídá na Pavol Hejný
Taskkill:4. října 10:27

Já si myslím že je lepší použít string template literál.

div.style.left = `${smer ? sirkaObrazovky + 100  : -200}px`

Proč? Protože je ihned vidět, že výsledný typ je string a je to kratší. => méně kódu => méně prostoru pro bugy

Editováno 4. října 10:29
 
Odpovědět 4. října 10:27
Avatar
Pavol Hejný
Autoredaktor
Avatar
Odpovídá na Taskkill
Pavol Hejný:4. října 12:41

Určitě, string literals jsou skvělé featura. Pouzival bych ji ale pouze, pokud máš v projektu nějaký transpilator.

 
Odpovědět 4. října 12:41
Avatar
Taskkill
Šéfredaktor
Avatar
Odpovídá na Pavol Hejný
Taskkill:4. října 15:02

Tohle je diskuze na trochu jiné téma. Nicméně se na to dívám jinak. Záleží na support politice. Jsou appky, co prostě podporují last stable verze browserů. Pak transplantovat netřeba. K tématu článku - tohle je ukázka best practices, mezi best practices patří i používat nové featury jazyka, které zvyšují přehlednost a čistotu kódu i celkově jeho kvalitu. Takže tady bych to prostě použil bez váhání.

 
Odpovědět  +1 4. října 15:02
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 6 zpráv z 6.