Vytváření standardních výjimek

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.

One Comment

Comments are closed.