Jedna z věcí, které mi v X++ nejvíce chybí, je alespoň trochu rozumná práce s výjimkami. Dynamics AX nezná objekty výjimek, má jen výčtový typ Exception a navíc by programátoři měli explicitně vyhazovat v podstatě jen Error, UpdateConflict a UpdateConflictNotRecovered, což efektivně znemožňuje zpracovávat chyby na základě jejich typu.
S vlastnostmi X++ těžko něco uděláme, ale je s objekty výjimek souvisí i jiná věc, která řešitelná je: Mnoho výjimek je lze označit za výjimky stejného standardního typu (např. neočekávaně prázdné pole v tabulce), ale pro každý takový typ je stále nutné hledat/pamatovat si text vhodného chybového hlášení. Líbilo by se mi něco jako třeba v C#:
throw new ArgumentNullException("itemId"); //Text výjimky: //Value cannot be null. //Parameter name: itemId
Prostě nestarat se o konkrétní znění chybového hlášení a muset si pamatovat jen správný typ výjimky.
Udělal jsem jednu pokusnou implementaci (odkaz na .xpo na konci) – vemte prosím na vědomí, že je to spíš ukázka k zamyšlení než hotová knihovna výjimek.
Původně jsem začal s trochu komplikovanějším návrhem, ale v průběhu refaktoringu to vykrystalizovalo do velmi jednoduchého rozhraní. Základní použití vypadá takto:
throw exception(new ArgumentEmptyException('_itemId'));
Celkem čitelné, ne?
Konstrukce exception je metoda v třídě Global, ekvivalentní metodě error(), ale akceptující jediný parametr – objekt typu SysException. Stejně jako error() vrací hodnotu výčtového typu Exception, která se pak používá v příkazu throw.
Základem hierarchie výjimek je třída SysException, která má tyto hlavní členy:
class SysException { new() //bezparametrický konstruktor str message() //vrací chybové hlášení Exception show() //zapíše výjimku do infologu }
SysException obsahuje základní implementaci, takže odvozené výjimky nemusí překrývat žádné metody (ačkoli by měly). Třída výjimky v následujícím příkladu deklaruje pouze extends SysException
:
throw exception(new DeveloperIsDumbheadException()); //Infolog: Exception of type 'DeveloperIsDumbheadException' was thrown.
Výjimky mohou poskytnout vlastní chybové hlášení triviálním překrytím metody message().
public str message(){ return "Oops"; }
SysException má ve skutečnosti ještě vlastnosti HelpUrl a InfoAction. Ty mohou být nastaveny buď pomocí přístupových metod nebo pomocí řetězení. Mojí snahou je, aby všechny vlastnosti mohly být nastaveny přístupovými metodami, ale aby to nebylo potřeba.
//zápis pomocí běžné přístupové metody SysException ex = new DeveloperIsDumbheadException(); ex.helpUrl("http://thedailywtf.com"); throw exception(ex); //to samé pomocí řetězení metod throw exception(new DeveloperIsDumbheadException().withHelpUrl("http://thedailywtf.com"));
Konstruktor odvozených tříd může akceptovat nepovinné parametry, jako v případě ArgumentEmptyException zmíněné na začátku.
V následujících příkladech jsou ukázány všechny odvozené výjimky, které jsou obsaženy v .xpo. Není jich mnoho, zatím totiž sloužily jen k ověření architektury.
//Infolog: Value of parameter '_userId' is not valid. throw exception(new ArgumentException("_userId")); //Infolog: Parameter '_userId' cannot be empty. throw exception(new ArgumentEmptyException("_userId")); //Infolog: Field EmplId must be filled in. throw exception(new FieldEmptyException(fieldStr(EmplTable, EmplId))); switch (numberSeqModuleEnumValue) { //(...) zpracování známých prvků default: //neplatná hodnota nebo hodnota neexistující v době psaní programu //Infolog: Enum element 'PRODCOM' is not valid. throw exception(new InvalidEnumException(numberSeqModuleEnumValue)); }
Je zjevné, že mým cílem nebylo nahradit všechna vyhazování chyb – pokud potřebujete specializovaný text chyby, použijte stejný přístup jako dosud. Ale vyhazujete-li “standardizovanou” chybu, můžete využít objekt typu SysException a o konstrukci textu chyby se nestarat. Potenciálně budete také moci změnit label na jediném místě, logovat určitý typ chyb a podobně.
Zdrojový kód je ke stažení zde.
Jen upozorním, že většina výjimek používá doslovné řetězce, ne labely, a .xpo obsahuje dva standardní objekty: třídy Global a SysInfoAction_Editor.
Ahoj, diky za clanek, vypujcim si kod:) R