4. díl - Upomínač narozenin - Logická vrstva

C# .NET Windows Forms Upomínač narozenin - Logická vrstva

V minulém dílu seriálu tutoriálů o tvorbě formulářových aplikací v C# .NET jsme navrhli kompletně formuláře pro naši aplikaci. V tomto dílu se budeme zabývat návrhem logické vrstvy, tedy tříd, které obsahují logiku aplikace.

Osoba

V naší aplikaci budou zcela jistě figurovat osoby, vytvořme jim tedy třídu. Před třídu nezapomeňte dát modifikátor public.

Vlastnosti

Osoba bude mít 2 vlastnosti: jméno a narozeniny. Jméno bude string, Narozeniny budou typu DateTime. Tyto vlastnosti nastavíme pomocí parametrického konstruktoru.

public class Osoba
{
        public string Jmeno { get; set; }
        public DateTime Narozeniny { get; set; }

        public Osoba(string jmeno, DateTime narozeniny)
        {
                Jmeno = jmeno;
                Narozeniny = narozeniny;
        }

}

Metody

Kromě konstruktoru bude třída disponovat metodami SpoctiVek() a ZbyvaDni().

SpoctiVek

Metoda vypočítá a vrátí aktuální věk osoby v celých letech. Výpočet bohužel není jen o tom odečíst dva datumy, jelikož TimeSpan neumí zjistit počet let, pouze počet dní. Výpočet věku tedy provedeme následujícím způsobem:

  1. Získáme si aktuální datum (bez času) pomocí DateTime.Today.
  2. Věk spočítáme jako rozdíl roků v aktuálním datu a datu narozenin. Asi je vám jasné, že tento věk není přesný. Pokud jsme se narodili 1.2.1990 a je 1.1.2010, není nám 20 let, ale jen 19. Z toho důvodu provedeme korekci.
  3. V případě, že je aktuální datum menší (dříve) než datum narození po přidání námi vypočítaných let, nastal výše uvedený případ a my věk o rok snížíme.
  4. Hotový věk vrátíme.

Kód metody bude následující.

public int SpoctiVek()
{
        DateTime dnes = DateTime.Today;
        int vek = dnes.Year - Narozeniny.Year;
        if (dnes < Narozeniny.AddYears(vek))
                vek--;
        return vek;
}
ZbyvaDni

Metoda nám vrátí kolik dní zbývá do narozenin osoby. To zjistíme následujícím způsobem:

  1. Získáme si aktuální datum (bez času).
  2. Získáme datum dalších narozenin přičtením věku + 1 k datu narození.
  3. Data odečteme a celkový rozdíl ve dnech vrátíme. Jelikož je rozdíl typu double, musíme ho zkonvertovat na int.
public int ZbyvaDni()
{
        DateTime dnes = DateTime.Today;
        DateTime dalsiNarozeniny = Narozeniny.AddYears(SpoctiVek() + 1);

        TimeSpan rozdil = dalsiNarozeniny - DateTime.Today;

        return Convert.ToInt32(rozdil.TotalDays);
}
ToString

Jelikož budeme osoby vypisovat, přepíšeme metodu ToString() tak, aby vracela jméno osoby:

public override string ToString()
{
        return Jmeno;
}

Správce osob

Další logickou komponentou aplikace bude správce osob. Třída se bude starat o osoby, bude je umět přidávat, odebírat a jejich seznam ukládat do souboru a opětovně načíst. Konečně bude umět mezi osobami vyhledat tu, která má nejbližší narozeniny.

Přidejte si k projektu tedy třídu SpravceOsob a učiňte ji veřejnou.

Vlastnosti a atributy

Jedinou veřejnou vlastností třídy je seznam osob. Seznam je typu BindingList. S touto kolekcí jsme se v seriálu ještě nesetkali. Jedná se o chytřejší list, který na sobě umí vyvolat událost změny v případě, že se změní jeho obsah. Díky tomuto mechanismu se automaticky obnoví všechny kontrolky na formuláři, které mají nastavený jako zdroj dat právě tento BindingList. Asi si dokážete představit, že obnovovat ručně desítky kontrolek na formuláři v případě změny může být velice nepřehledné. Jakmile v naší aplikaci přidáme novou osobu, bude ihned vidět v seznamu osob aniž bychom ho obnovovali, obnoví se sám. BindingList inicializujeme v konstruktoru.

Pokud bychom chtěli implementovat i editace osob, musela by třída Osoba implementovat rozhraní INotifyProper­tyChanged. Jakákoli změna (např. jména) by se poté automaticky projevila ve všech kontrolkách všech formulářů, kde osoba figuruje. Tímto se však pro zjednodušení zabývat nebudeme. Třída zatím vypadá takto:

public class SpravceOsob
{
        public BindingList<Osoba> Osoby { get; set; }

        public SpravceOsob()
        {
                Osoby = new BindingList<Osoba>();
        }

}

Metody

Kromě přidávání a odebírání bude třída umět i nalézt osobu s nejblížšími narozeninami. Ukládání a načítání osob do/ze souboru necháme na později.

Pridej

Metoda přidá novou osobu do BindingListu. Jelikož osobu přidáváme formulářem, hodí se nám, aby metoda brala v parametrech vlastnosti osoby a na jejich základě vytvořila novou instanci. Z data narození uložíme pouze část s datem, tedy bez času.

Před přidáním zkontrolujeme, zda jméno není příliš krátké nebo není zadané datum v budoucnosti. Pokud nějaká z těchto situací nastane, vyhodíme výjimku. Právě výjimky jsou správným způsobem, jak s chybami v objektových aplikacích pracovat.

Výjimky jsou detailněji popsané v sekci Práce se soubory v C# .NET. Pokud jste se s nimi ještě nesetkali, bude vám stačit vědět, že výjimka se vyvolá pomocí klíčového slova throw, za kterým následuje instance výjimky. Výjimek máme několik typů včetně možnosti vytvořit si vlastní. V našem případě se nám hodí ArgumentException (chyba v argumentu). Do konstruktoru výjimky zadáváme text chyby. Jakmile je výjimka vyvolána, metoda již dále nepokračuje. Jak na chybu reagovat si ukážeme až budeme metodu volat z formuláře.

public void Pridej(string jmeno, DateTime datumNarozeni)
{
        if (jmeno.Length < 3)
                throw new ArgumentException("Jméno je příliš krátké");
        if (datumNarozeni.Date > DateTime.Today)
                throw new ArgumentException("Datum narození nesmí být v budoucnosti");
        Osoba osoba = new Osoba(jmeno, datumNarozeni.Date);
        Osoby.Add(osoba);
}
Odeber

Metoda odebere osobu z BindingListu. Protože vždy budeme chtít odebírat již hotovou osobu, bere metoda v parametru právě tu.

public void Odeber(Osoba osoba)
{
        Osoby.Remove(osoba);
}
NajdiNejblizsi

Metoda najde a vrátí osobu, která má nejbližší narozeniny. K vyhledání osoby v listu použijeme LINQ metodu OrderBy(), která osoby uspořádá podle toho, kolik dní zbývá do jejich narozenin. Výsledek uložíme do kolekce, jejíž typ neuvedeme a použijeme místo toho klíčové slovo var, jak je u LINQu zvykem. Následně vrátíme první osobu. Metodu bychom měli volat jen tehdy, když jsou v listu nějaké osoby. Ačkoli by z kódu mělo být jasné, co metoda dělá, můžete se samozřejmě podívat na tutoriály v sekci Kolekce a LINQ v C# .NET, kde je technologie LINQ detailně popsána.

public Osoba NajdiNejblizsi()
{
        var serazeneOsoby = Osoby.OrderBy(o => o.ZbyvaDni());
        return serazeneOsoby.First();
}

Pokračovat budeme příště, kdy aplikaci zprovozníme. Dosavadní zdrojový kód je ke stažení níže.


 

Stáhnout

Staženo 738x (342.7 kB)
Aplikace je včetně zdrojových kódů v jazyce C#

 

  Aktivity (1)

Článek pro vás napsal David Čápka
Avatar
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.

Jak se ti líbí článek?
Celkem (3 hlasů) :
55555


 



 

 

Komentáře
Zobrazit starší komentáře (31)

Avatar
wolf661
Člen
Avatar
wolf661:

Nazdar. Na BindingList potrebujem pridat nejaky using ... Existuje nato nejaky jednoduchy sposob(okrem googlenia) ako ho pridat vo Visual Studiu 2010 express? V jave pri pouziti NetBeans mi import-y navrhne automaticky vo VS using-y nie, je to dost flustrujuce.

Odpovědět 2.7.2014 0:50
Situácia normálna, všetko v hajzli.
Avatar
Jan Vargovský
Redaktor
Avatar
Odpovídá na wolf661
Jan Vargovský:

Napsat to přesně jak se jmenuje a pak ti to samo VS nabídne. Najedeš na to myší a tam ti to nabídne. Nebo verze pomocí klávesnice ctrl + tečka a pak enter.

 
Odpovědět  +2 2.7.2014 1:30
Avatar
Michal Štěpánek:

Jak by se řešil ten BindingList, resp. přidávání záznamů v případě použití databáze, kde by byly osoby identifikovány IDčkem (autoincrement), které překvapivě v době ukládání neznám?

Odpovědět 18.3.2015 11:34
Nikdy neříkej nahlas, že to nejde. Vždycky se totiž najde blbec, který to neví a udělá to...
Avatar
Napal
Člen
Avatar
Napal:

Ahoj, chci se zeptat, proč v metodě ZbyvaDni vytváříme tuto proměnou.

DateTime dnes = DateTime.Today;

Nikde dále tuto proměnou nevyužíváme, není tam zbytečná?

 
Odpovědět 11.12.2015 22:28
Avatar
Napal
Člen
Avatar
Napal:

Můj komentář byl asi trochu zapomenut, dokázal by mi někdo odpovědět? :) děkuji

 
Odpovědět 16.12.2015 21:03
Avatar
Odpovídá na Napal
Michal Štěpánek:

V podstatě by to šlo i bez této proměnné, ale hned v dalším řádku je použita pro výpočet věku...

public int SpoctiVek()
{
        DateTime dnes = DateTime.Today;
        int vek = dnes.Year - Narozeniny.Year;
        if (dnes < Narozeniny.AddYears(vek))
                vek--;
        return vek;
}
Odpovědět 16.12.2015 21:50
Nikdy neříkej nahlas, že to nejde. Vždycky se totiž najde blbec, který to neví a udělá to...
Avatar
Napal
Člen
Avatar
Odpovídá na Michal Štěpánek
Napal:

Tam ano, tam to chápu, ale já se ptal na metodu ZbyvaDni(), kde je taky vytvořená, ale není nijak využitá. (podle mě)

public int ZbyvaDni()
{
        DateTime dnes = DateTime.Today;
        DateTime dalsiNarozeniny = Narozeniny.AddYears(SpoctiVek() + 1);

        TimeSpan rozdil = dalsiNarozeniny - DateTime.Today;

        return Convert.ToInt32(rozdil.TotalDays);
 
Odpovědět 17.12.2015 20:17
Avatar
Odpovídá na Napal
Michal Štěpánek:

Ano, tam se vytváří zbytečné. Myslím si, že to chtěl David udělat přehledné a pak zapomněl tu proměnnou použít.

Odpovědět  +2 18.12.2015 9:05
Nikdy neříkej nahlas, že to nejde. Vždycky se totiž najde blbec, který to neví a udělá to...
Avatar
Peter Kováčik:

Môže niekto poslať celý kód programu?

 
Odpovědět 20. listopadu 11:06
Avatar
Odpovídá na Peter Kováčik
Michal Haňáček:

Vždyť jsou zdrojáky ke stažení ...

Odpovědět 20. listopadu 11:27
Každé rozhodnutí a každý krok v životě nás někam posune. Bohužel jen některé nás posouvají dopředu.
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 10 zpráv z 41. Zobrazit vše