\


 Friday, 19 May 2006
Sloupec v GridView s potvrzením vymazání záznamu (na klientovi)

Jestliže chcete mít v GridView sloupec, který uživatele požádá na klientovi (s využitím JavaScriptu) o potvrzeni smazání záznamu, je doporučeno používat TemplateField, jehož šablona ItemTemplate obsahuje ve vlastnosti OnClientClick tlačítka klientský kód.

<asp:TemplateField ShowHeader="False">

   <ItemTemplate>

      <asp:ImageButton ID="ImageButton1" runat="server" CausesValidation="False" CommandName="Delete" ImageUrl="~/Images/delete.gif" OnClientClick="javascript:return confirm('Opravdu smazat záznam?');" />

   </ItemTemplate>

</asp:TemplateField>

ButtonField by byl sice vhodnější, ale tvůrci ASP.NET zadání kódu v JavaScriptu pro ButtonField nepodporují. Třída ButtonField nenabízí ani přístup k vlastnostem prvku Button, který je vyrenderován na každém řádku GridView.

Chceme-li napsat svůj vlastní typový sloupec pro mazání záznamů, který budeme používat ve všech svých projektech, použijeme třídu ButtonField alespoň jako předka svého nového sloupce a lehce "dirty" kódem v v přepsané metodě InitializeCell přidáme tlačítku vytvořenému v "našem" sloupci pro každý řádek GridView jednoduchý kód v JavaScriptu, jenž bude vyžadovat od uživatele potvrzení smazání záznamu.

    /// <summary>
    /// Nový typ sloupce pro GridView obsahující tlačítko pro smazání záznamu s potvrzením na klientovi (JS)
    /// </summary>
    public class DeleteButtonField : ButtonField
    {
        #region Private constants
        private const string DEFAULT_CONFIRM_MESSAGE = "Opravdu smazat záznam?";
        private const string DELETE_COMMAND = "Delete";
        private const string DELETE_JS = "javascript: if (!confirm('{0}')) return false;";
        #endregion Private constants

        public DeleteButtonField() : base()
        {

        }

        #region Public properties
        /// <summary>
        ///Text varování, které se má zobrazit uživateli při pokusu vymazat záznam
        /// </summary>
        public string ConfirmMessage
        {
            get
            {
                string confirmMessage = (string)ViewState["ConfirmMessage"];

                if (confirmMessage == null)
                {
                    return DEFAULT_CONFIRM_MESSAGE;
                }

                return (confirmMessage);
            }

            set
            {
                ViewState["ConfirmMessage"] = value;
            }
        }
        #endregion Public properties

        #region Public properties

        /// <summary>
        /// Metoda volaná při incializaci buňky Gridu
        /// </summary>
        /// <param name="cell">Inicializovaná buňka</param>
        /// <param name="cellType">Typ buňky</param>
        /// <param name="rowState">Stav řádku buňky</param>
        /// <param name="rowIndex">Index řádku buňky</param>
        public override void InitializeCell(DataControlFieldCell cell, DataControlCellType cellType, DataControlRowState rowState, int rowIndex)
        {
            base.InitializeCell(cell, cellType, rowState, rowIndex);
            if (cell.Controls.Count > 0)
            {
                IButtonControl control = cell.Controls[0] as IButtonControl;

                if (control != null)
                {
                    control.CommandName = DELETE_COMMAND;
                  
                    LinkButton lb = control as LinkButton;
                    Button btn = control as Button;
                    ImageButton iBtn = control as ImageButton;

                    string fullConfirmMessage = String.Format(DELETE_JS, ConfirmMessage);
                    if (lb != null)
                    {
                        lb.OnClientClick = fullConfirmMessage;
                    }

                    else if (btn != null)
                    {
                        btn.OnClientClick = fullConfirmMessage;
                    }
                    
                    else if (iBtn != null)
                    {
                        iBtn.OnClientClick = fullConfirmMessage;
                    }
                }
            }
        }
        #endregion Public methods
    }

Kód není nijak složitý. Kromě konstant, jejichž význam je dostatečně zřejmý z jejich názvu, máme v našem novém sloupci vlastnost ConfirmMessage, která obsahuje text zobrazovaný v dialogu pro potvrzení smazání záznamu. V metodě InitializeCell nejdříve zavoláme implementaci InitializeCell třídy ButtonField. Poté zjistíme, jestli buňka gridu (cell) obsahuje nějaké ovládací prvky. Jestliže ne, byla metoda volána pro řádek reprezentující záhlaví nebo zápatí gridu, a v ní žádné tlačítko nebude. Když buňka obsahuje alespoň jeden ovládací prvek, zjistíme zda první prvek v kolekci1 je typu IButtonControl (rozhraní IButtonControl implementují třídy LinkButton, Button i ImageButton) a nastavíme jeho vlastnost CommandName na text Delete. Jak asi víte, řetězec "Delete" je jedním z příkazů, které GridView rozeznává a speciálně ošetřuje, takže při bublání událost Click tlačítka s CommandName nastaveným na "Delete" je vyvolána událost RowDeleting (a případně další) místo generické události RowCommand.

Protože rozhraní IButtonControl nenabízí vlastnost OnClientClick pro zadání potvrzujícího kódu v Javascriptu, musíme přetypovat na konkrétní třídu Button a poté teprve můžeme použít vlastnost OnClientClick. Další "dirty" kód :-) - byl bych raději, kdyby byla vlastnost OnClientClick přímo součástí rozhraní IButtonControl, anebo by mohl existovat společný abstraktní předek s vlastností OnClientClick pro třídy ImageButton, LinkButton a Button.

Nový sloupec ve svém projektu použijete takto:

Zaregistrujete nový sloupec na stránce jako běžný serverový ovládací prvek zadáním jména jeho assembly, jmenného prostoru a rezervací prefixu značek pro své prvky v direktivě @Register.

<%@ Register Assembly="Rstein.Web.Ui" Namespace="Rstein.Web.Ui" TagPrefix="custom" %>

Sloupec přidejte do kolekce Columns prvku GridView a nastavte dle svých požadavků vlastnosti sloupce. 

<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" OnRowDeleting="GridView1_RowDeleting">

   <Columns>

         <custom:DeleteButtonField Text="Smazat" ButtonType="Button" ConfirmMessage="Smazat záznam?"/>

   </Columns>

</asp:GridView>

Příště si ukážeme, jak podobné sloupce pro Grid udělat lépe s využitím šablon vytvořených v kódu a také doplníme pro své sloupce kompletní podporu pro design time.

 

  1. Zde je ten zmiňovaný "dirty" postup. I když jsem si ověřil Reflectorem, že prvním prvkem kolekce je vždy IButtonControl vytvářený třídou ButtonColumn, jde o typický případ svazování svého kódu s kódem, jehož  změny nemáte pod kontrolou a přitom se nespoléháte jen na jeho "typově bezpečné" veřejné API, takže se v dalších verzích můžete dočkat nepříjemných překvapení  :( 


Friday, 19 May 2006 17:44:04 (Central Europe Standard Time, UTC+01:00)       
Comments [0]  .NET Framework | ASP.NET


 Thursday, 11 May 2006
První zkušenosti s navigačním programem iGO

Program iGO má nejen exotický maďarský původ, ale také jde v poslední verzi o slušně zpracovaný navigační systém, který vás bez větších problémů dovede tam, kam máte namířeno. A hlavně jako u všech dobrých navigací dorazíte do cílového místa, aniž byste předtím museli vytvářet itinerář cesty hledáním v tištěném Autoatlasu nebo internetové mapě, a k rodinné pohodě přispěje i tím, že už nemusíte za jízdy manželce v roli navigátora po několikerém špatném odbočení nervózně a zvýšeným hlasem vysvětlovat, jak se pozná na papírové mapě, jestli máte odbočit vpravo či vlevo. ;-)

I když jsem k novému programu přistupoval s despektem, musím říci, že moje obavy z práce neznámých maďarských programátorů byly pouhé předsudky a samotné iGO se může i přes drobné chybky v kvalitě navigace směle měřit s programy jako jsou Dynavix či TomTom.

Všechny možné grafické a funkční serepetičky navigace oceníte jen a pouze při práci s navigací v křesle domova, protože za jízdy těžko budete obdïvovat krásné textury objektů na dispĺeji. Plech vašeho vozu by totiž mohl být za chvíli ilustrací fyzikálního zákona pojednávajícího o následcích srážky s pohyblivými i statickými objekty v reálném světě. Proto mám na navigační programy tyto tři hlavní požadavky.

  1. Nejdůležitější součástí každého navigačního programu je intuitivní hlasová navigace - ideální hlasová navigace po celou dobu jízdy přichází s tak přesnými instrukcemi, že se uživatel nemusí ani jednou podívat na displej, aby věděl, jak má na křižovatce zabočit. To znamená, že hlasová navigace by dnes již neměla mít jen jednoduché instrukce ve stylu "zabočte vlevo", "zabočte vpravo" nebo "po 150 m zabočte doprava", ale měla by navigovat uživatele například i v křižovatkách, kde po sobě rychle následují dvě odbočky doprava a jasně mu říci, kterou odbočku má zvolit. Při jízdě po hlavní silnici, kdy dodatková tabulka 'opravdový tvar křižovatky' u značky 'Hlavní silnice' sděluje, že hlavní silnice pokračuje doleva či doprava, musí hlasová navigace v dostatečném předstihu sdělit "jeďte po hlavní silnici" či "pokračujte po hlavní silnici - mírně vpravo", abyste se opět nemuseli o dalším směru jízdy ("rovně po vedlejší" nebo "dále po hlavní") rozhodovat podle toho, co je momentálně zobrazeno na displeji. 
  2. Žádný navigační program zatím s ideální hlasovou navigací nepřišel, a proto se občas při složitějších křižovatkách kouknutí na displej PDA nevyhneme. Abychom zorientováním se v informacích na displeji netrávili zbytečně mnoho času, je nutné, aby navigační program zobrazoval všechny podstatné informace, perfektně vykreslil schéma následujího manévru a nastavil měřítko (zoom) mapy tak, abychom ihned dokázali "namapovat" reálnou křižovatku na zobrazení křížovatky na displeji a věděli, jak ji máme projet. Za standard již považuji 3D pohled. Když je 3D pohled dobře naprogramován, je orientace v mapě citelně jednodušší než při méně přirozeném "plochém" 2D pohledu. Přeplácaná grafika orientaci na mapě  stěžuje - čím více zbytečných detailů, které oceníte snad jen v hrách typu Need For Speed, tím pomalejší orientace v mapě. Od podrobností abstrahující a jen schematické zobrazení míst, kterými právě projíždíte, je pro navigaci mnohem přínosnější než všemi barvami a grafickými efekty opentlená mapa. 
  3.  I když dnes již snad každý program trochu alibisticky zobrazuje upozornění, které nás důrazně varuje před používáním programu za jízdy, občas se přeci jen ovládání programu při řízení nevyhneme. Když se rozhodneme někde naobědvat, budeme chtít rychle vyhledat v zájmových bodech (POI) nejbližší restauraci a nechat se k ní navigovat. Je proto důležité, aby menu bylo přehledné a aby ikonky jednotlivých funkcí v něm byly dostatečně velké. Ikonky musí být čitelné na displeji PDA i z větší dálky a celé menu musí být ovladatelné dotykem prstu, jehož průměr bývá u většiny normálně vyvinutých jedinců přeci jen o něco větší než průměr stylu. K bezpečnému ovládání za jízdy také velkou měrou přispívá velikost tlačítek na softwarové klávesnici pro zadávání názvů měst, ulic a filtrování seznamu zobrazených POI.

Jak si poradí iGO s těmito požadavky ?

Hlasová navigace v iGo je na slušné úrovni. Na výběr máte mužský i ženský hlas v češtině, slovenštině a dalších jazycích. Já jsem většinu času jezdil s navigací v anglickém jazyce, protože mi angličtina přišla příjemnější a méně "strojová" než čeština a slovenština.

Místo zmatených hlášek ve městě s mnoha bočními uličkami, které pouze říkají, co máte udělat za 150 m, ale již Vám nesdělí, jak máte těch 150 m v autě změřit, se dovíte, že  máte zabočit vpravo do následující (první, druhé) ulice. Všechna hlášení jsou také vydávána s dostatečným předstihem  - občas si ale říkáte, že po nájezdu na dálnici není nutné ihned vědět, co se stane za 40 Km, a i by podle mě mohl být zredukován počet hlášení v různých vzdálenostech od odbočky.

Při přímém porovnání s TomTomem byla hlasová navigace přesnější v Benešově při nájezdu na most. iGo naviguje "zabočte mírně vlevo a pak ihned vpravo", kdežto TomTom říká pouze "na konci ulice zahněte doprava", což může být pro řidiče, který se ve městě nevyzná, trochu matoucí. Jednak lze konec mostu za konec ulice považovat jen s velkou dávkou fantazie a také oznámení o prvním mírném zabočení doleva lépe vystihuje to, že při přímé jízdě před  začátkem mostu bychom vjeli do ulice označené značkou zákaz vjezdu. Také v Praze, při mírném odbočování vlevo z ulice U Zdravotního ústavu do ulice Ruská , kde TomTom sveřepě mlčí, i když odbočení doleva při jízdě z opačného směru hlásí, iGo přesně naviguje instrukcí, abychom se drželi na hlavní silnici a odbočili mírně vlevo. TomTom měl zase při hlasové navigaci navrch při nájezdu na dálnici u Mirošovic směrem na Prahu  - aby mě dostal na "správnou" stranu dálnice (tedy do směru na Prahu a ne na Brno), přikáže před nájezdem na dálnici "nyní se budete držet vlevo", což vám zabrání sjet do pravého pruhu a vydat se směrem do Brna. IGo pouze sdělí, že máte najet na dálnici. Také hlášení TomToma, že se blíží sjezd z dálnice, přichází oproti iGo mnohem dříve. Záleží na rychlosti auta - při rychlosti 130 Km/h TomTom oznamuje poprvé sjezd cca 2 km před samotným sjezdem, zatímco u iGo se musíte spokojit s prvním upozorněním ve vzdáleností cca 500 metrů před sjezdem. Přesto je iGo TomTomovi v kvalitě hlasové navigace dobrou konkurencí a v leckterých složitějších dopravních situacích ho i předčí. Za špičku hlasové navigace považuji stále Dynavix  - i v centru Prahy to byla opravdu jediná navigace, která mě dokázala spolehlivě vést jen hlasovými instrukcemi. TomToma i iGO bych dal při hodnocení kvality hlasové navigace společně na druhé místo ihned za Dynavix.

Grafika iGO je vynikající a při prvním pohledu TomToma předčí. Vlaječky označující počátek a konec trasy jsou krásně animované, názvy ulic ve městech perfektně čitelné a vždy české a ne ceske.  Přesto se neorientuji v 2D ani 3D pohledu tak dobře jako u TomToma, ale to může být dáno tím, že iGo používám zatím velmi krátkou dobu. Schéma následujícího manévru v levé části obrazovky a automatický zoom při příjezdu na křižovatku jsou zpracovány dobře a pokud nemáte s čím srovnávat, budete zcela spokojeni. 

Mapové podklady pro Českou republiku mají oba programy od společnosti TeleAtlas a jejich podrobnost je skvělá. U iGo ale nemáte takové množství POI, které existují pro TomToma. Nepříjemně mě překvapilo, že v obchodních centrech zcela absentuje třeba Hypernova Průhonice. Také zamrzí, že nemůžete z nejbližších POI vyfiltrovat jen ty, které leží přímo na trase.

Ovládání menu iGo "jedním prstem" sice možné je, ale při přímém srovnání s ergonomií ovládání TomToma jsou patrné zásadní nedostatky a nedomyšlenosti. Do dalších úrovní menu se dostáváte složitě a také mi moc nevyhovuje, že při hledání místa je nejdříve zobrazen formulář pro vyhledání ulic ve městě, které jste zadali při posledním hledání. Teprve po volbě změnit město se zobrazí města hledaná dříve a když chcete zadat město, které není v historii hledaných měst, musíte volbou 'Jiné město' přejít na obrazovku, kde zadáte několik počátečních písmen z názvu města. Program iGO na formuláři pro zadání názvu města ukazuje jen počet měst vyhovujících zadávaným písmenům, ale samotná města uvidíte až po stisknutí tlačítka Hotovo. Celý postup mi přijde nesmyslně zdlouhavý a krkolomný. Navíc mi tlačítka softwarové  klávesnice připadají příliš titěrná a to i po jejich zvětšení stisknutím tlačítka Klávesy.

Také první a graficky netradiční rozcestník hlavního menu volbou barev a písma nechtěně vizuálně potlačuje centrální a nejdůležitější položku menu s názvem "Vyhledat a jet". Co naopak u iGo chválím je plánování složitějších cest přes více míst ("lomových bodů"). 

V ergonomii ovládání TomTom jasně vítězí a iGo bude muset hodně zapracovat na odstranění zmíněných nedostatků.

Závěrečné postřehy:

  • U iGo stejně jako u TomToma bych vyzdvihl bezproblémovou konfiguraci a práci s GPS. (BT GPS Holux 230 - spárována s MDA Vario). 
  • Program s celou mapou Evropy se vejde na jednu 1GB SD kartu. Program iGo se prodává na SD kartě a k jeho instalaci dojde po zasunutí SD karty do slotu. Jak asi tušíte, na kartě je adresář s "wellknown" názvem 2577, ze kterého Windows Mobile po zasunutí karty vždy automaticky spustí  soubor nazvaný autorun.exe.
  • Zadání zeměpisných souřadnic.
  • Upozornění na překročení zadané rychlosti.
  • ""Ladění" algoritmu pro výpočet trasy - volba mezi nejrychlejší, nejkratší a ekonomickou trasou. "Profily" trasy pro vozidla, kola, chodce. Možnost zakázat dálnice nebo jen placené dálnice a také třeba nezpevněné cesty.
  • Zakázání obratů do protisměru. Tato funkce ale podle mě nefunguje správně - i když jsem obraty do protisměru zakázal, přesto po mě iGo párkrát při odchýlení se od stanovené trasy obrat do protisměru vyžadoval.
  • Možnost zakázat při výpočtu vnitrostátní trasy cestu přes hranice s okolními státy.
  • Automatické přepínání mezi denním a nočním barevným profilem.

Obrázková galerie iGo:

Rychlý přístup k některým položkám menu   Hlavní menu  Menu v mapě  Režim navigace  Seznam POI  Body zájmu (POI) - kina   Nastavení parametrů trasy   Mapa  - ulice Vinohradská, Praha

Thursday, 11 May 2006 15:41:31 (Central Europe Standard Time, UTC+01:00)       
Comments [3]  Mobilitky | Navigace


 Monday, 01 May 2006
Odstranění problému(ů) s instalací Compact .NET Frameworku 2.0 na Windows Mobile 5.0

Již několikrát jsem se setkal s tím, že na zařízení s Windows Mobile 5.0 byl problém nainstalovat Compact .Net Framework. Pár lidí si mi teď v poslední době v mailu nebo na ICQ také stěžovalo, že jim CNF nejde nainstalovat a že při pátrání po příčinách potíží dostanou nanejvýš jen hlášku, že došlo k problému a že by bylo dobré resetovat zařízení a zkusit instalaci znovu. ;-) Tomu říkám skvělé design guidelines v praxi - "nikdy neobtěžujte uživatele zbytečnými technickými podrobnostmi". :-D

Odstranění problému není složité - z důvodu známému asi jen vývojářskému týmu CNF v Microsoftu a mystérióznímu pro běžného smrtelníka ActiveSync při instalaci CNF z počítače přes Application Manager zkopíruje do zařízení soubor nazvaný NETCFv2.wce5.armv4i.cab místo správného NETCFv2.wm.armv4i.cab. NETCFv2.wm.armv4i.cab patří na všechna současná zařízení používající procesor s ARM V4 kompatibilní instrukční sadou.

Takže selže-li vám instalace, najděte na počítači soubor NETCFv2.wm.armv4i.cab, a zkopírujte jej "ručně" (v Exploreru nebo třeba v v Total Commanderu s pluginem pro CE zařízení)  do PDA. V PDA pak jen cab soubor spusťte a VŽDY zvolte instalaci CNF do zařízení - instalace na kartu (SD, CF) je sice možná, ale při prvním použití CNF dojde stejně k nakopírování všech knihoven do storage v zařízení.

Pokud máte stále problémy s instalací:

  1. Resetujte (soft-reset) zařízení a zkuste nainstalovat CNF znovu. Teď už má reset smysl. ;-)
  2. Nezabere-li ani to, vypněte po dobu instalace všechny Today pluginy, restartujte (soft reset) a znovu instalujte.

Přinejhorším bod 2 zabere podle mých zkušeností vždy - ještě se mi nestalo, že bych na nějaké PDA s WM 5 CNF 2 nenainstaloval ;-)

 



Monday, 01 May 2006 21:50:26 (Central Europe Standard Time, UTC+01:00)       
Comments [0]  Compact .Net Framework | Mobilitky


 Thursday, 27 April 2006
Řešení hádanky z 18-.4. - vztahy Include, Extend v případech užití

Protože kolegové v diskuzi pod původním spotem podrobně rozebrali všechny aspekty vztahu Include a Extend v případech užití,  je tento můj spot z větší části sumarizující. V čem tedy byl "chyták"? Cílem hádanky bylo upozornit na to, že vztahy Include a Extend nemusejí být tak jednoduše rozpoznatelné, jak se dočtete v různých rychlopříručkách,  a také jsem chtěl, abyste si všimli, že vztah Extend bylo bylo příjemné zpřesnit tím, co sám pro sebe nazývám "anonymní datové sloty".

Tak toto byl začátek dvoustránkového spotu, který se mi podařilo omylem smazat. :-( Protože už se mi nechce teď psát celý spot znovu, tak jen v krátkosti zrekapituluji, čeho se hádanka týkala.

Specifikace UML popisuje vztahy Include a Extend takto:

"Include is a directed relationship between two use cases, implying that the behavior of the included use case is inserted into the behavior of the including use case. The including use case may only depend on the result (value) of the included use case. This value is obtained as a result of the execution of the included use case."

"This relationship specifies that the behavior of a use case may be extended by the behavior of another (usually supplementary) use case. The extension takes place at one or more specific extension points defined in the extended use case. Note, however, that the extended use case is defined independently of the extending use case and is meaningful independently of the extending use case. On the other hand, the extending use case typically defines behavior that may not necessarily be meaningful by itself. Instead, the extending use case defines a set of modular behavior increments that augment an execution of the extended use case under specific conditions. Note that the same extending use case can extend more than one use case. Furthermore, an extending use case may itself be extended."

 

V hádance nemohl být vztah mezi případem užití 'Založení objednávky' a 'Platba kartou' Include ani Extend. Include nelze použít, protože máme i jiné typy plateb, a případ užití Platbu kartou tedy nelze vložit povinně (dalším typem platby bude třeba dobírka - žádné záludnosti ohledně plateb jsem přichystány neměl, jen jsem chtěl, abyste se jako správní analytici pídili po podrobném zadání :-) ). Nejedná se ale ani o vztah Extend, protože rozšířený případ užití nijak nezávisí na rozšiřujícím případu užití a nemůže číst žádné jeho "návratové hodnoty"  - pro nás to znamená, že nezjistíme, jestli platba prošla.

Řešením je vytvoření nového abstraktního případu užití nazvaného třeba 'Kontrola platby', který přes vazbu Include provážeme s případem užití 'Založení objednávky' . Konkrétními potomky budou "Kontrola platby kartou", "Kontrola ostatních typů plateb". Vazba Include nám dovoluje číst návratovou hodnotu vloženého případu užití. V alternativním toku událostí případu užití  'Založení objednávky' podle vybraného typu platby vložíme konkrétní případ užití a dle výsledku vkládaného případu užití dokončíme hlavní tok událostí. Když neprojde platba kartou, můžeme zákazníkovi nabídnout výběr jiného typu platby.

Hlavně ale platí - celá hádanka i její řešení je jen hříčka bez užitku pro zákazníka, která demostruje, jak lze i méně časté vztahy řešit běžnými výrazovými prostředky jazyka UML. Stále platí, že vazby Include, Extend ani gen-spec zákazníkovi raději moc neukazujte, jestliže  nechcete zbytečně trávit čas vysvětlováním vztahů, které jsou pro projekt málo podstatné a pro zákazníka samotného většinou jen iritující.



Thursday, 27 April 2006 18:25:16 (Central Europe Standard Time, UTC+01:00)       
Comments [13]  Programátorské hádanky | UML


 Wednesday, 19 April 2006
Sedm dobrých programů (nejen) pro MDA Vario
MDA

Tady je malý výběr z programů, které mám nainstalovány na MDA Variu (Qtek 9100).

 

  1. Navigační aplikace TomTom - pro Čechy i další země jedna z nejlepších navigačních aplikací. S pomocí TomToma jsem najezdil už pěkných pár tisíc km a kromě drobných nedostatků, které se najdou v každé aplikaci, musím jeho autory jen chválit. Bez navigace už nikdy jezdit nebudu a papírové mapy považuji již jen za vysloužilou archiválii hodnou možná tak piety, ale nehodnou další aktivní služby. :-) Výhodou TomToma také je, že se dá již koupit oficiálně i v Čechách - viz třeba obchod společnosti Sunnysoft. Pro Čechy doporučuji sehnat mapový balík PL-CZ-SK-HU verze 645.
  2. MChat - zdarma dostupný a přijatelně spolehlivý klient pro ICQ.  Ovládání by mohlo být lepší - v aktuální verzi se neobejdete bez vytažení dotykového pera, což trochu degraduje požitek z pohodlí integrované HW klávesnice. :-(
  3. eWallet - graficky povedený program pro správu "tajných informací" jako  jsou přístupová hesla, PINy nebo  údaje o platebních kartách. Jen nesmíte zapomenout to jediné důležíté vstupní heslo do programu. :)
  4. CheckNotifications - program pro zobrazení notifikací, který červeně odliší duplicitní notifikace a jedním tlačítkem v něm také duplicitní notifikace smažete. Notifikace je operačním systémem Windows Mobile založena interně vždy, když potřebujete systému "říci" - v určitou hodinu nebo po výskytu nějaké události (třeba probuzení systému) něco udělej (zapni budík, spusť program...).
  5. SmartsKey  - volně dostupný program, který KONEČNĚ z MDA Vario udělá zařízení, jež se dá ovládat jednou rukou. Jednou z nejlepších funkcí je, že tlačítka na zvyšování/snižování hlasitosti můžete v jiných aplikacích než jsou telefon a Today obrazovka používat pro posun textu. Funkce SmartsKey by měly být integrovány ve Windows Mobile hned od Microsoftu a nechápu, proč tomu tak není.
  6. SPB Diary - program, který na obrazovku Today integruje úkoly, kalendář, kontakty a poznámky. Integrace je oproti pluginům dodávaným přímo s Windows Mobile mnohem dokonalejší a většinu činností s PIM položkami zvládnete rovnou z Today obrazovky. Nechcete-li požívat funkcemi překypující programy, jakými jsou Pocket Informant nebo Agenda Fusion, a dáváte přednost jednoduššímu uživatelskému rozhraní, je program SPB Diary dobrá volba.
  7. SPB Pocket Plus  - SPB Pocket Plus je program od firmy, která vytvořila SPB Diary,  a i přes nějaké chybky, na které narazíte při delším používání, jde o program, který by měl mít nainstalován každý majitel zařízení s Windows Mobile. Pocket Plus umožní vytvářet zástupce na programy i různé speciální "akce" (regulace podsvícení displeje apod.) přímo na Today obrazovce v uživatelsky definovaných záložkách, do horní části obrazovky přidají nevtíravý indikátor stavu baterie, obsahují skvělý task manager a mohou spustit zařízení v nouzovém režimu + další menší vylepšení systému. Nouzový režim oceníte hlavně tehdy, když si nainstalujete nějaký špatně napsaný Today plugin, který způsobí, že zařízení po "soft resetu" nenaběhne. Když byste neměli SPB Pocket Plus, jediným řešením je "hard reset" se všemi následujícími otravnými novými instalacemi všech programů. Pozor na to, že nouzový režim na starších verzích SPB Pocket Plus nemusí na Variu fungovat správně. Paradoxně pak je zapnutá podpora nouzového režimu příčinou vynuceného "hard resetu".


Wednesday, 19 April 2006 12:03:37 (Central Europe Standard Time, UTC+01:00)       
Comments [9]  Mobilitky


 Tuesday, 18 April 2006
Zajímavý dotaz (hádanka) na případy užití v UML - vztah Include, Extend, anebo ...?

Po základní analýze jste zjistili, jak zákazníci vašeho klienta vytvářejí objednávky:

"Zákazník v našem obchodě vybírá zboží a poté si může vybrat, jak jej uhradí. Jedním z možných typů platby je platba kartou. K založení objednávky dojde pouze tehdy, pokud zadané údaje o platební kartě jsou platné, zákazník má dostatečnou hotovost na účtu a platba tedy projde."

Řekněme, že z těchto údajů vytvoříme (kromě dalších, zde nezmiňovaných) dva případy užití:

  1. UC001 - Založení objednávky zákazníkem
  2. UC002 - Platba kartou

Jaký vztah je podle UML mezi těmito případy užití ? Odpověď není tak jednoduchá, jak se může na první (a dokonce i druhý a ležérní třetí) pohled zdát... :-)



Tuesday, 18 April 2006 09:27:39 (Central Europe Standard Time, UTC+01:00)       
Comments [27]  Programátorské hádanky | UML


 Tuesday, 11 April 2006
Pár tipů na dobré blogy a typy blogů, které nemám rád

I když většinou žádné blogy neodporučuji, pomineme-li jako implicitní doporučení můj blogroll v pravé části stránky, a dokonce ani žádné nehaním, rozhodl jsem se při blížícím se dvouletém výročí mého vstupu do blogosféry :-) udělat výjimku.

Tip 1 - Borkův blogovníček - Borek píše spoty o počítačích, programování (PHP, .Net Framework), škole (VŠE) a zatím jsou sice jeho spoty sice spíše extenzivní než intenzivní, ale už teď je z nich patrné, že jejich autor není a a nebude žádný zoufalý "truhlík" bez názoru, jehož blog by byl archaickou manufakturou specializovanou na přeťukání zpráv z oficiálních médií nebo tupým seznamem linků z jiných blogů.

Tip 2 - Chorobovýplody - Blog Petra Lazeckého na serveru Vyvojar.cz sice není žádnou horkou novinkou, ale lidé vyvíjející na MS technologiich by jej měli číst povinně.  Články o C++/CLI nebo o "odvšivování" aplikací patří k tomu nejlepšímu, co se dá v Čechách o tématu nalézt,  a kvalitou svých spotů Petr několikanásobně převyšuje běžnou úroveň článků na vývojáři.

Tip 3 - GeoN - a něco ze zcela jiné oblasti. GeoNův blog je pro mě mimořádným "úkazem" na českém internetu. Nestává se často, aby si člověk s tak skvělým všeobecným vzděláním založil vlastní blog. GeoNův blog není pro každého a při jeho čtení se budete často rozčilovat, že autor občas vynáší apodiktické soudy, aniž by je pořádně zdůvodnil, nebo že laxně interpretuje pojem "pravda" a různými "oslími můstky" kritizuje pak i ostřelováním ze zálohy koncepci "metafyzické" a potažmo "vědecké" pravdy. Rozčilenost je alespoň u mě signálem, že se setkávám s textem, který mě nenechává lhostejným. GeoNa doporučuji - zapomeňte na hloupé a únavné přednášky o politologii nebo filosofii a nechte na sebe působit texty, kde myšlení "žije" a ne se jen konzervuje a zabíjí tupým  přemíláním nudných postulátů a exhumací myšlenek jiných, jak tomu mnohdy stále je hlavně na akademické půdě. Opakování totiž není matkou moudrosti, což se nám snaží vnutit  ty milé, labilní a intelektem většinou nevynikající paní učitelky ze ZŠ, ale jak věděl už Ladislav Klíma, opakování je jen matkou debility...

Určitě z blogu doporučuji alespoň dva články, které mě velmi zaujaly:

...Hierarchie parazitismu (až přízračně pravdivé postřehy o společnosti)
GeoNova volební kampaň 2006

A jaké typy blogů nemám rád? O jednom typu jsem se už zmínil. Patří do něj blogy, které mě vždy utvrdí ve víře v existenci dávicího reflexu. Jsou to blogy a články naplněné jen dalšími a dalšími únavnými seznamy odkazů, mezi které ti chytřejší alespoň občas hodí nějakou slovní vycpávku. Takových "blogů"  stále přibývá a já jsem nikdy nepochopil, proč jejich autoři sami sebe dobrovolněvystavují na moderním pranýři,  nad nímž je přibitý nápis "nic neumím, nic nedovedu, tak aspoň odkážu na ty ostatní". Moderní forma veřejného sebemrskačství... :-)

Dalším typem blogů, které se mi nelíbí, ale které mají asi velkou čtenářskou základnu, jsou blogy se spoty, které se skoro vždy "vezou" na nějakém "aktuálním" společenském tématu a které využívají laciných titulků k nějakému triviálnímu sdělení, jehož napsání muselo autorovi trvat asi 30s, do kterých můžeme započítat i spuštění počítače z hibernovaného stavu. Hlavním znakem blogu je za všech okolností názorová průměrnost - výhodou je, že když na blog přijdete třeba pro roce, nic vás nepřekvapí a vše je stále při starém a můžete si oddechnout, že starý dobrý středostavovský svět otce Kondelíka stále žije. Vyměnily se jen nepodstatné rekvizity, Knedlo Zelo Vepřo doplnila třeba Fabia nebo Roomster. :-) Typický zástupce - Laco stále bloguje.

U posledního blogu, který nemám rád,  jsem na vážkách. Myslím, že ani nejde o "typ" blogu, protože Letinka je unikátním a hýčkaným exemplářem na českém internetu a způsob jejího psaní nejde jednoduše převést na určitá pravidla, ani není snadné v něm najít nějakou šablonu nebo vyčpělou šmíru jako u předchozího typu blogu. Když jsem ale uvažoval, proč zrovna mě Letinka neoslovuje, musím říci, že nemám rád typ psaní, kde vládne cosi, co bych nazval "násilně předzjednanou harmonií". Její spoty se snaží být až příliš "tolerantní" a "moudré", nanejvýš pouč(e)né a "laskavé". Ale ta laskavost je přeslazená, takový Svěrák dovedený ad absurdum, a čiší z ní, tu více, tu méně,  že je jen speciálním a dovedně smíchaným destilátem připraveným pro vstřícně naladěné internetové publikum. Za čpící pachutí laskavosti tušíme nepřirozenou  a záměrně zvolenou stylizaci, která je neorganicky naroubována na osobnost, jež je mimo sféru internetu určitě citově i intelektuálně mnohem bohatší a zajímavější, než nám předvádí ve své zbytečné a občas i nevkusné internetové masce. Někdy mi přijde, že Letinka musí mít záměrně ve spotech "lehký žal", aby se jí nad obdivnými komentáři dojatých trumberů mohl rozlehnout v hrudi "hluboký smích" z emocionálně deprivovaného a šíleného světa, který si nakloníte na svou stranu jednou "cituplnou" či sentimentální větou. Kýč na druhou.:-)



Tuesday, 11 April 2006 11:52:19 (Central Europe Standard Time, UTC+01:00)       
Comments [12]  Ostatní


 Sunday, 09 April 2006
Bázová třída pro typové kolekce v .NET Frameworku 2.0

V souvislosti s uvedením generiky v .NET Frameworku se v různých článcích dočtete, jak generika usnadní vytváření a použití typových kolekcí. To je sice pravda, ale v článku se kromě zjednodušených příkladů a frikulínských hlášek o dokonalosti .NETu 2.0 z úst (respektiva pera) excitovaných jedinců po právě dokonaném intimním styku se softwarovou emanací božstva Microsoftu :-D málokdy dovíte, jak by taková typová kolekce měla vypadat v běžné aplikaci.

Proč nevyhovuje obyčejné použití generického typu List? (deklarace typové kolekce objednávek ve tvaru List<Order> orderCollection = new List<Order>()).

  • Jednou z dobrých praktik u generik je co nejvíce před uživateli (dalšími vývojáři) skrývat informaci, že pracují s generickým typem. Ač mně syntaxe pro práci s generickými typy připadá intuitivní, nemusí si to myslet všichni, a mnoho vývojářů stále asi raději používá důvěrně známý kód OrderCollection orderCollection = new OrderCollection() místo výše zmíněného kódu List<Order> orderCollection = new List<Order>()).  Tento požadavek by ale List<T> splnil  - typová kolekce může být potomkem List<T>.
  • Ve třídě List nejsou metody Add a Remove virtuální. To znamená, že nemůžete po přidání nebo odebrání položky z/do kolekce vyvolávat vlastní události. A to je problém, protože po přidání/odebrání položek z kolekce můžeme chtít nastavit/zrušit rodiče nebo přepočítát sumární hodnoty za položky v kolekci apod.

Bázová třída pro všechny kolekce, kterou používám, je otevřeným generickým typem a jejím předkem je třída Collection<T> z jmenného prostoru System.Collections.ObjectModel. Třída Collection<T> nabízí virtuální chráněné metody InsertItem a RemoveItem, ve kterých můžete vyvolávat potřebné události. Jestliže používate návrhový vzor Layer SuperType a máte tedy jednu bázovou třídu pro všechny business objekty (BusinessObjectBase), je vhodné, aby bázová třída pro kolekce kladla na generický typ T omezení, že musí být potomkem třídy BusinessObjectBase. Omezení slouží k tomu, abyste ve svých kolekcích mohli intuitivně používat všechny atributy a metody deklarované na úrovni společného předka BusinessObjectBase.

Kód kolekce:

   public class BusinessCollectionBase<T> : Collection<T> 
                                        where T : BusinessObjectBase
    {
        #region Events
        public event EventHandler<CollectionChangeEventArgs> ItemAdded;
        public event EventHandler<CollectionChangeEventArgs> ItemRemoved;
        #endregion Events
        #region Protected methods
        /// <summary>
        /// Přidání položky do kolekce
        /// </summary>
        /// <param name="index">Index položky</param>
        /// <param name="item">Vkládaná položka</param>
        protected override void InsertItem(int index, T item)
        {
            base.InsertItem(index, item);
            OnItemAdded(new CollectionChangeEventArgs(item));
        }

        /// <summary>
        /// Přidání položky do kolekce
        /// </summary>
        /// <param name="index">Index položky</param>        
        protected override void RemoveItem(int index)
        {
            T item = this[index];
            base.RemoveItem(index);
            OnItemRemoved(new CollectionChangeEventArgs(item));
        }

        /// <summary>
        /// Metoda odpovědná za vyvolání události ItemRemoved
        /// </summary>
        /// <param name="e">Argumenty události</param>
        protected void OnItemRemoved(CollectionChangeEventArgs e)
        {
            if (ItemRemoved != null)
            {
                ItemRemoved(this, e);
            }
            
        }
        
        /// <summary>
        /// Metoda odpovědná za vyvolání události ItemAdded
        /// </summary>
        /// <param name="e"></param>
        protected void OnItemAdded(CollectionChangeEventArgs e)
        {
            if (ItemAdded != null)
            {
                ItemAdded(this, e);
            }
         
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public virtual T FindById(Guid id)
        {
            List<T> mylist = (List<T>) Items;
            T objectMeetsCriteria = null;
            objectMeetsCriteria = mylist.Find(delegate(T iterObject)
                                  {
                                      if (iterObject.Id == id)
                                      {
                                          return true;
                                      }
                                      else
                                      {
                                          return false;
                                      }
                                                                
                                  });

            return objectMeetsCriteria;
        }

        /// <summary>
        /// Nalezení všech objektů splňujících zadanou podmínku
        /// </summary>
        /// <param name="criteria">Podmínka výběru</param>
        /// <returns>List s objekty, které splňují zadanou podmínku</returns
        public virtual List<T> Find(Predicate<T> criteria)
        {
            List<T> mylist = (List<T>) Items;           
            return (mylist.FindAll(criteria));
            
        }
        
        /// <summary>
        /// Spuštění akce nad všemi elementy v kolekci
        /// </summary>
        /// <param name="action">Akce, která se má provést</param>
        public virtual void ForEach(Action<T> action)
        {
            List<T> mylist = (List<T>)Items;
            mylist.ForEach(action);            
        }
    
        #endregion Protected methods
    }
}

Jak vidíme:

  1. Třída BusinessCollectionBase je potomkem třídy Collection<T> a vyžaduje, aby typ T byl vždy potomkem BusinessObjectBase. Motivace pro toto rozhodnutí jspu popsány výše.
  2. Nadeklarovali jsme dvě události ItemAdded a ItemRemoved, které jsou vyvolávany v přepsaných metodách InsertItem (ItemAdded) a RemoveItem (ItemRemoved). Pokud bych měl zájem, mohu jednoduše přidat i události vyvolávané před přidáním/odebráním položky z kolekce.
  3. Do rozhraní BusinessCollectionBase jsem také přidal několik zajímavých metod.
    1. Metoda FindById nalezne podle předaného Id (unikátní identifikátor instance) objekt v kolekci. V této metodě opět používáme nové konstrukce z .Net Frameworku 2.0. Implementační objekt kolekce (starý známý List<T> ) vystavuje metodu Find, která očekává generického delegáta Predicate z jmenného prostoru System.
      public delegate bool Predicate( T obj);
      Delegát Predicate je "ukazatelem" na metodu, která očekává jeden generický argument T a vrací true nebo false. Delegát Predicate tedy zastupuje metodu s podmínkou, která je pro předaný argument obj pravdivá nebo nepravdivá. My pro vytvoření podmínky použijeme anonymní metodu, která vrátí true pouze tehdy, když se Id objektu v kolekci shoduje s předaným Id. Atribut Id u generického typu T kolekce můžeme používat právě proto, že jsme zavedli pro typ T omezení (musíš být potomkem  BusinessObjectBase) a atribut Id je deklarován ve třídě BusinessOBjectBase.
    2. Pro pokročilejší operace s elementy kolekce jsme z objektu List<T> zveřejnili metody FindAll A ForEach. Metoda FindAll podle předané podmínky (delegát Predicate) nalezne a vrátí všechny objekty, které jí vyhovují. Metoda ForEach spustí pro všchny elementy v kolekci "akci - činnost" implementovanou v metodě, na níž "ukazuje" další užitečný delegát Action<T>.

      public delegate void Action<T> ( T obj);

      Když tedy budete chtít všechny objekty v kolekci zrušit, místo psaní cyklu foreach napíšete kód podobný tomuto:

      myCol.ForEach(delegate(OrderItem item)
      
                  {
      
                       item.Discard();
      
                  });
      

Vytváření vlastních typových kolekcí je jednoduché:

    /// <summary>
    /// Kolekce objektů OrderItem
    /// </summary>
    public class OrderItemCollection : BusinessCollectionBase<OrderItem>
    {

    }
 

Pro úplnost sem dávám triviální kód třídy pro argumenty události CollectionChanged.

    /// <summary>
    /// Objekt v kolekci
    /// </summary>
    public class CollectionChangeEventArgs : EventArgs
    {
        #region Private variables
        private BusinessObjectBase m_collectionObject;
        #endregion Private variables
        
        #region Constructors
        /// <summary>
        /// Konstruktor
        /// </summary>
        /// <param name="collectionObjekt">Objekt v kolekci, kterého se událost týká</param>
        public CollectionChangeEventArgs(BusinessObjectBase collectionObject)
        {
            BasicValidations.AssertNotNull(collectionObject, "collectionObject");
            m_collectionObject = collectionObject;
        }

        /// <summary>
        /// Objekt v kolekci, kterého se událost týká
        /// </summary>
        public BusinessObjectBase CollectionObject
        {
            get
            {
                return m_collectionObject;
            }
        }
        #endregion Constructors
    }
Související články:

Bázová třída pro business objekty - návrhový vzor Layer Supertype
Cachování řádků z databáze pro business objekty - třída DataCacheHelper
Ukázka použití třídy BusinessObjectBase



Sunday, 09 April 2006 14:34:33 (Central Europe Standard Time, UTC+01:00)       
Comments [7]  .NET Framework | Compact .Net Framework | Návrhové vzory


 Tuesday, 28 March 2006
Další FAQ k .NET Remotingu
  1. Při použití CAO (Client Activated) objektu nebo při vrácení MarshalByRefObjectu ze SAO (Server Activated) objektu fungují volání metod jen v lokální síti. Na počítači přistupujícím přes internet (a komunikujícím tedy většinou přes adresu firewallu/routeru) volání metody vzdáleného CAO objektu vždy selže.

    Důvodem je, že každý CAO objekt je identifikován unikátním dynamickým URI, které je vygenerováno na serveru. Server za firewallem/routerem ale vrátí název počítače nebo IP adresu (v závislosti na nastavení vlastnosti useIpAddress u přenosového kanálu), které jsou platné pouze v lokální síti. Nastavením vlastnosti machineName u kanálu můžeme zadat, jaký název serveru (IP adresa) mají být v URI pro CAO objekty vráceny - zadáme tedy externí a pro klienty viditelnou adresu (např. adresu firewallu).

    V Konfiguračním souboru

    <channel ref="tcp" machineName="serverExternalVisibleName">
    </channel>

    V kódu:
    IDictionary properties = new Hashtable();
    properties["machineName"] = "serverExternalVisibleName";
    TcpChannel tcpChannel = new TcpChannel(properties, null, null);
    ChannelServices.RegisterChannel(tcpChannel);
  2. Při komunikaci se serverovým objektem dostanete hlášení, že selhalo přihlášení k proxy (The remote server returned an error: (407) Proxy Authentication Required).

    Net Remoting podporuje přihlášení k webovému serveru, ale jeho autoři trestuhodně a pravděpodobně omylem (ale i ve verzi 2.0?) nezveřejnili pro zadání přihlašovacích údajů objekt IWebProxy. Jediným nepříliš čistým řešením (pomineme-li nespolehlivé zadání autentizačních údajů přes třídu GlobalProxySelection) je použití reflection a nastavení privátního člena _proxyObject v objektu HttpClientChannel.

    //Vytvoření nového http komunikačního kanálu
    HttpChannel channel = new HttpChannel();
    
    //Získáme deskriptor člena _clientChannel (klientský http kanál) ve třídě HttpChannel
    FieldInfo clientChannelFO = typeof(HttpChannel).GetField("_clientChannel",   BindingFlags.Instance|BindingFlags.NonPublic);
    
    //Získání instance HttpClientChannel
    HttpClientChannel clientChannel = clientChannelFO.GetValue(channel);
    
    //Získání deskriptoru proxy objektu
    FieldInfo proxyObject = typeof(HttpClientChannel).GetField("_proxyObject", BindingFlags.Instance | BindingFlags.NonPublic);
    
    //Vytvoření nového proxy objektu
    IWebProxy authProxy = new WebProxy("proxy", 80); 
    
    //Pro autentizaci k proxy použijeme údaje přihlášeného uživatele
    authProxy.UseDefaultCredentials = true;
    
    //Nastavení nové proxy v objektu HttpClientChannel
    proxyObject.SetValue( clientChannel, proxy );
    

Starší FAQ k .NET Remotingu na tomto blogu

Dva nejčastější dotazy k .NET Remotingu
Dotazy a odpovědi k .NET Remotingu



Monday, 27 March 2006 23:47:11 (Central Europe Standard Time, UTC+01:00)       
Comments [0]  .NET Framework | .Net Remoting


 Sunday, 26 March 2006
UML - O agregaci, kompozici a asociaci a jako bonus společenská aktualitka :)

Na svých přednáškách při vysvětlování asociace a kompozice mám slide, na kterém jsou zmíněny základní rozdíly mezi oběma typy vztahů v UML. Ihned k tomu ale dodávám, že zmíněné rozdíly jsou akademické a že v praxi si vystačíme většinou s jedním typem vztahu (s agregací), u kterého při kódování intuitivně víme, zda složený (kompozitní) objekt rozhoduje, a pokud ano, tak v jaké míře, o životním cyklu svých konstituentů (objektů, z nichž je složen).

 

Kompozice
  • Kompozitní objekt nemůže existovat bez komponentních složek.
  • Každý komponentní objekt může být součástí jen jedné kompozice
  • Kompozice je většinou heterometrická
Agregace
  • Agregace může existovat bez svých konstitučních objektů
  • Jeden objekt může být potenciálně konstituentem více konstitučních objektů
  • Agregace inklinují k homeometrii

Informace o rozdílu mezi agregací a kompozicí vycházejí z UML specifikace, kde se říká:

"[Agregace je] speciální forma asociace, která specifikuje vztah mezi agregujícím(celkem) a jeho konstituentem (částí)."

"Kompozitní agregace je silnější forma agregace, která vyžaduje, že jakákoli instance konstituenta (části) bude součástí nejvýše jednoho kompozitního objektu. Jestliže je kompozitní objekt smazán, jsou většinou všechny jeho části vymazány s ním".

Čte někdo vůbec UML specifikaci namísto čtení takzvaně přístupných  a "lidsky psaných" knih, které dogmaticky vyžadují rozlišování mezi kompozici a agregací? Jak si můžete všimnout, v definicích agregace i kompozice je zřetelně vidět pozvolný přechod mezi asociací, agregací a kompozicí a ne žádné ostré hranice, jak se nám mnohdy snaží vsugerovat sekundární literatura k UML nebo žabomyší akademické války o přesný význam termínů. Převedu-li definice a jejich důsledky do normální řeči - agregace ani kompozice není z hlediska UML nic jiného než asociace. Protože se ale asociace s rysy kompozice a agregace vyskytují často,  zavedeme pro ně speciální jazykový konstrukty. Tyto jazykové konstrukty jsou implicitními nositeli omezení, jež jsou na agregaci a kompozici kladeny. Spory o to, co je v diagramu ještě agregace a co už musí být kompozice jsou směšné a nikam nevedoucí  - je to jen zvlášní druh moderního pseudovědeckého sektářství, kde se na hranici místo kacířů zástupně smaží nedogmatický duch UML.

Dále moje rozlišení agregace a kompozice vycházejí z praxe, kde indukcí dojdete k tomu, že kompozice bývá heterometrická  - konstituenti jsou objekty z různých tříd. Typicky například u faktury rozlišujete mezi řádky faktury, záhlavím faktury a zápatím faktury - faktura je tedy složena minimálně z objektů tří různých tříd. Když faktura nemá záhlaví, zápatí nebo alespoň jeden řádek, nemůže jít o fakturu. 

U agregace si častěji všimneme toho, že agregované objekty mohou být sdílené a že nezanikají se zánikem celku. Typicky o agregaci hovoříme třeba, když budeme modelovat kalendář pro obchodní zástupce a v kalendáři budou zadávány  jejich schůzky. Kalendář jako takový (seznam dní) může existovat i bez jakékoli evidované události. Některé události v kalendáři jsou privátní - typicky pravidelná návštěva přiděleného zákazníka. Jiné události jsou sdílené - společná schůzka všech obchodních zástupců je (přesněji řečeno - v tomto případě může být výhodně modelována jako) sdílená událost ve více kalendářích. I ty takzvaně privátní schůzky mohou "putovat" po kalendářích různých obchodních zástupců, když se dohodnou, že jeden po dobu dovolené zastoupí druhého a že převezme i jeho schůzky.

Důležité je si uvědomit, že v jazycích jako je C# nebo Java je rozdíl mezi kompozicí a agregaci opravdu zanedbatelný. Důvod je jednoduchý - nemáme v nich operátor delete([]) a odpovědnost za likvidaci objektů za nás převzal Garbage Collector. V C# a Javě si musíme pouze hlídat řízení životního cyklu objektů ukládaných do databáze - pokud něco (exkluzivně i sdíleně) vlastním (kompozice i agregace) , měl bych si uvědomit, že:

  1. Při zániku vazby musí dojít k odstranění objektu z databáze. Tedy odebere-li někdo řádek faktury z kolekce, je objekt faktura odpovědný za smazání vazby.
  2. Jestliže nemažu vazby fyzicky v databázi, ale pouze u objektů ze zrušených vazeb nastavuji stav 'neaktivní', musí moje business vrstva centrálně podporovat princip "co je neaktivní, je tabu a nikdo s tím nesmí pracovat  - a jak to v SW bývá, vždy máme výjimky. Jedinou výjimkou z tohoto pravidla jsou objekty, u nichž eviduji historii."

Poslední poznámka z praxe:

Na jedné přednášce (myslím před rokem na DNG) jsem se setkal s názorem, že typickým zástupcem agregace je "typování" objektů. Pod typováním objektu se můžeme představit objekt Uchazeč o práci, který má na sobě navázáno 0..n dalších objektů z třídy (z hlediska databáze z číselníku) Schopnost (řidičák, anglický jazyk, německý jazyk, práce s Excelem, lámání ženských srdcí atd.). Schopnost je zde termín, pod který jsou subsumovány všechny sledované typy evidovaných znalostí a dovedností. "Typování" není nic jiného, než klasifikace objektů dle různých kritérií, což nám umožňuje objekty rychle seskupovat do (z hlediska projektu) zajímavých množin - například budu-li obsazovat pozici lamačem srdcí se znalostí německého jazyka, snadno zjistím množinu uchazečů splňujících zadaná omezení.

Každý uchazeč může tedy "agregovat" o..n znalostí. Ale jde opravdu o agregaci? Já myslím, že je to prostá asociace - uchazeč nemá žádnou zvláštní vazbu na znalosti, ty jsou k němu jen "volně přidruženy".

A za jakých podmínek by bylo vhodné překvalifikovat vazbu mezi Uchazečem a Schopností opravdu na agregaci ? V momentě, kdy budete chtít evidovat nějakou dodatečnou a unikátní informaci k vazbě mezi uchazečem a jeho schopnostmi. Když tedy budeme chtít zjistit, od kdy uchazeč vlastní řidičský průkaz, nebo kolik dobytí ženských srdcí posloužilo jako předmostí k dobytí klínů;-) , zavedeme novou třídu DetailSchopnostíUchazeče - tato třída bude v asociaci s původní třídou Schopnost a bude agregována třídou Uchazeč (anebo dokonce v tomto případě bude jejím komponentním objektem) :-) .

Proč? Informace, které nesou objekty z třídy DetailSchopnostíUchazeče nemají význam "an sich", ale jen ve spojení s uchazečem. Jsou nedílnou částí právě jednoho obrazu, který nám dává objekt Uchazeč. Třída Schopnost nedílnou součástí obrazu právě jednoho Uchazeče nebyla - nenesla žádné specifické informace o uchazeči, jen s ním byla svázána náhodným poutem - asociací.

Na konec aktualitka na asociaci a kompozici ze života. :)

Asociace je pouhé registrované partnerství, ve kterém jsou dva konstituenti nahodilými objekty, kteří mají vůči sobě jeden kontingentní a zparchantělým parlamentem posvěcený link. Oba nijak závazně neřídí životní cyklus toho druhého.

Uzavřením manželství vzniká sakrální kompozitní objekt, jehož nedílnými součástmi jsou oba manželé s vzájemně protknutými životními cykly. A jak víme, při zániku kompozitního objektu, dříve či později nevratně odumírají i komponentní objekty.

I když nemusíme rozlišovat mezi asociací, agregací a kompozicí v UML, v životě může být dobře patrný rozdíl mezi asociací a kompozicí diferencujícím znakem mezi frivolní hrou a závazným svazkem. A pak že nelze do odborného blogu jednoduše a nenásilně propašovat názor na politická nebo společenská témata:-)



Sunday, 26 March 2006 15:50:53 (Central Europe Standard Time, UTC+01:00)       
Comments [5]  UML