Znalost návrhových vzorů je jednou ze základních vlastností dobrého programátora v objektově orientovaném jazyce. V krátkém seriálu se pokusím přiblížit ty nejzákladnější z nich. V tomto díle se podíváme na utility class (knihovní třídu), singleton (jedináčka) a messenger (přepravku).
Co jsou návrhové vzory?
Toto je první otázka, kterou jste si (možná) položili. Návrhový vzor je způsob postupu, dle kterého upravíme náš postup při tvorbě programu. Je to, jako když stavíte dům. Můžete to udělat mnoha způsoby, ale který si vyberete? Ten který je lety ověřený a funkční. Návrhové vzory jsou ověřené programovací "styly" nebo "postupy".
Proč bych měl nějaký návrhový vzor používat?
Toto je pravděpodobně druhá nejčastější otázka. Kdykoliv něco děláme, můžeme použít dva způsoby. První způsob je ten, který funguje, a druhý je ten, kterým je to správně. Použití návrhových vzorů nám umožňuje napsat kód, ve kterém je pro jiného člověka snazší se orientovat. Jelikož jsou to ověřené způsoby, je menší šance, že uděláme chybu. Takovýchto důvodů je více. Teď si možná říkáte, že není třeba je využívat, ale až si o nich něco přečtete, uvidíte, že to má svou logiku.
Pokud si při čtení seriálu řeknete například: "Tohle dělám běžně, jen mě nikdy nenapadlo tomu říkat messenger," tak je vše v naprostém pořádku. Většina návrhových vzorů jsou velmi jednoduché a standardní postupy, jen je někdo sepsal, dal jim název a určil jim nějaká pravidla.
Pro ukázky zdrojových kódů budu používat programovací jazyk Java, protože je mi nejbližší. Správně ale tušíte, že návrhové vzory nemají s Javou vůbec nic společného a jedná se o věc, která je víceméně jazykově nezávislá.
Utility class (knihovní třída)
Začínáme trošku paradoxně. Knihovní třída návrhový vzor tak nějak je a tak trošku není. Je to osvědčený a používaný postup, který je ale lehce proti objektově orientovanému programování.
Utility class je třída, která má všechny atributy a metody statické. Ve třídě uvádíme privátní konstruktor, abychom předešli možnosti vytvoření instance. Dále také znemožňujeme od třídy dědit, což uděláme například klíčovým slovem final v Javě nebo sealed v C#. Více viz zdrojový kód.
final class KnihovniTrida{
private KnihovniTrida(){} // privátní konstruktor
public static int secti(int a, int b){
return a + b;
}
public static double umocniOdmocni(double a){
return Math.sqrt(a*a);
}
}
public class Main{
public static void main(String[] args) {
KnihovniTrida.secti(1, 1);
KnihovniTrida.umocniOdmocni(-5.0);
}
}
Metody z utility class budeme tedy vždy volat staticky a objekty či proměnné, se kterými budou metody pracovat, jí musíme předat jako formální parametry. Typickým Javovským příkladem knihovní třídy je třída Math viz Java Core API.
Messenger (přepravka)
Messenger je druh třídy, která má přesně tolik atributů, kolik v ní potřebujeme "přenést" hodnot. To sice může vypadat jako úplně normální třída, přeci jen se ale v něčem liší.
Jelikož instance messengeru jsou tzv. immutable objects, atributy jsou veřejné konstanty. Tyto konstanty jsou nastaveny v konstruktoru a pak už je nelze změnit, což nám zajišťuje bezpečné předání dat. Díky tomu, že jsou konstanty veřejné, k nim lze jednoduše přistupovat. Přesto se však k těmto atributům někdy dělají gettery, ze zvyku.
Kdy messenger využijeme? Laicky řečeno, pokud potřebujeme donutit metodu, aby vracela více hodnot najednou (vrátí sice stále jen jeden messenger, ale ten má více atributů), nebo když potřebujeme předat více hodnot jedním parametrem. Typické použití: puntík se souřadnicemi. Více nám opět poví zdrojový kód.
class Puntik{ // přepravka
public final int x;
public final int y;
public Puntik(int x, int y){
this.x = x;
this.y = y;
}
}
class Platno{
/**
* nějaké metody, definice, konstruktory
*/
public nakresliPuntik(Puntik p){
/* nějaká implementace kreslení */
}
public nakresliPuntik(int x, int y){
/* nějaká implementace kreslení */
}
}
public class Main{
public static void main(String[] args) {
Platno pl = new Platno(); // vytvoříme instanci plátna
Puntik puntik = new Puntik(5,5); // budeme kreslit na souřadnice 5,5
pl.nakresliPuntik(puntik); // s využitím přepravky
pl.nakresliPuntik(puntik.x,puntik.y); //s podivným využitím přepravky
pl.nakresliPuntik(5,5); // špatně, bez využití přepravky
}
}
Myslím, že z ukázkového zdroje musí být jasné, k čemu slouží přepravka. A po přečtení další části uvidíte, že třída Platno v tomto příkladu může být klidně jedináčkem.
Singleton (jedináček)
Singleton je třída, která se vyznačuje tím, jak už název napovídá, že má právě jednu instanci. Zdá se to zbytečné? Opak je pravdou. Navrhujeme-li například systém pro restauraci, restaurace bude pravděpodobně jedináček. Takovýchto případů bychom mohli vymyslet spoustu.
Jediné instance docílíme tak, že třída bude mít jako atribut statickou konstantu dané třídy, která bude ihned inicalizována voláním privátního konstruktoru. Dále stačí vytořit k této instanci, samozřejmě statický, getter, zpravidla pojmenovaný getInstance(). Tato možnost se lehce překrývá s návrhovým vzorem static factory method (statická tovární metoda), který si popíšeme v některém z dalších dílů seriálu. Nyní se však zaměříme na příklad zdrojového kódu našeho singletonu.
class Programujte{ // jedináček, pravé Programujte je jenom jedno
private static final Programujte INSTANCE = new Programujte(); // naše jediná instance Programujte
private Programujte(){} // privátní konstruktor, zamezuje vytvoření další instance
public static Programujte getInstance(){ // k té jediné se dostaneme tímto gettrem
return INSTANCE;
}
public void vypisClanky(){
System.out.println("Vypisuji články...");
}
}
public class Main{
public static void main(String[] args) {
Programujte.getInstance().vypisClanky();
}
}
Tím jsme u konce tohoto dílu seriálu. Doufám, že pro vás byl zajímavý. V příštím díle se můžete těšit na static factory method (statickou tovární metodu) a servant (služebníka). Máte-li nějaké připomínky, náměty či poznámky, neváhejte se podělit v komentářích.