Programování

Log4j ortogonalita příkladem

Ortogonalita je koncept, který se často používá k popisu modulárního a udržovatelného softwaru, ale je snáze pochopitelný pomocí případové studie. V tomto článku Jens Dietrich demystifikuje ortogonalitu a některé související konstrukční principy tím, že demonstruje jejich použití v populární knihovně nástrojů Log4j. V několika případech také diskutuje o tom, jak Log4j porušuje ortogonalitu, a diskutuje možná řešení vzniklých problémů.

Koncept ortogonality je založen na řeckém slově orthogōnios, což znamená „pravoúhlý“. Často se používá k vyjádření nezávislosti mezi různými dimenzemi. Když se objekt pohybuje podél X-osa v trojrozměrném prostoru, jeho y a z souřadnice se nemění. Změna v jedné dimenzi nezpůsobí změnu v jiné dimenzi, což znamená, že jedna dimenze nemůže způsobit vedlejší účinky pro ostatní.

To vysvětluje, proč se koncept ortogonality často používá k popisu modulárního a udržovatelného designu softwaru: přemýšlení o systémech jako o bodech ve vícerozměrném prostoru (vytvořených nezávislými, ortogonálními rozměry) pomáhá vývojářům softwaru zajistit, aby naše změny jednoho aspektu systému nebude mít vedlejší účinky pro jiné.

Stává se, že Log4j, populární open source logovací balíček pro Javu, je dobrým příkladem modulárního designu založeného na ortogonalitě.

Rozměry Log4j

Protokolování je jen oblíbenější verzí System.out.println () a Log4j je balíček nástrojů, který abstrahuje mechaniku protokolování na platformě Java. Funkce Log4j mimo jiné umožňují vývojářům provádět následující:

  • Přihlaste se k různým připojovacím zařízením (nejen ke konzole, ale také k souborům, síťovým umístěním, relačním databázím, nástrojům pro protokoly operačního systému atd.)
  • Přihlaste se na několika úrovních (například CHYBA, VAROVÁNÍ, INFO a DEBUG)
  • Centrálně ovládejte, kolik informací je protokolováno na dané úrovni protokolování
  • Pomocí různých rozvržení můžete definovat, jak se událost protokolování vykreslí do řetězce

Zatímco Log4j má jiné funkce, zaměřím se na tyto tři dimenze jeho funkčnosti, abych prozkoumal koncept a výhody ortogonality. Všimněte si, že moje diskuse je založena na Log4j verze 1.2.17.

Log4j na JavaWorld

Získejte přehled Log4j a naučit se psát vlastní vlastní dodatky Log4j. Chcete více výukových programů Java? Dostaň Informační bulletin Enterprise Java doručeno do vaší doručené pošty.

Zvažování typů Log4j jako aspektů

Dodatky, úroveň a rozložení jsou tři aspekty Log4j, které lze považovat za nezávislé dimenze. Používám tento výraz aspekt zde jako synonymum pro znepokojení, což znamená část zájmu nebo zaměření v programu. V takovém případě je snadné definovat tyto tři obavy na základě otázek, které každý z nich řeší:

  • Dodatek: Kam mají být zaslána data událostí protokolu pro zobrazení nebo uložení?
  • Rozložení: Jak by měla být prezentována událost protokolu?
  • Úroveň: Které události protokolu by měly být zpracovány?

Nyní zkuste tyto aspekty společně zvážit v trojrozměrném prostoru. Každý bod v tomto prostoru představuje platnou konfiguraci systému, jak je znázorněno na obrázku 1. (Všimněte si, že nabízím mírně zjednodušený pohled na Log4j: Každý bod na obrázku 1 ve skutečnosti není globální konfigurace celého systému, ale konfigurace pro jeden konkrétní záznamník. Samotné záznamníky lze považovat za čtvrtou dimenzi.)

Výpis 1 je typický fragment kódu implementující Log4j:

Výpis 1. Příklad implementace Log4j

// nastavení protokolování! Logger logger = Logger.getLogger ("Foo"); Appender appender = new ConsoleAppender (); Layout layout = new org.apache.log4j.TTCCLayout () appender.setLayout (layout); logger.addAppender (appender); logger.setLevel (Level.INFO); // spustit přihlášení! logger.warn („Hello World“);

Chci, abyste si všimli tohoto kódu, je to, že je ortogonální: můžete změnit doplněk, rozložení nebo aspekt úrovně, aniž byste porušili kód, který by zůstal zcela funkční. V ortogonálním designu je každý bod v daném prostoru programu platnou konfigurací systému. Žádné omezení nesmí omezovat, které body v prostoru možných konfigurací jsou platné nebo ne.

Ortogonalita je silný koncept, protože nám umožňuje vytvořit relativně jednoduchý mentální model pro případy použití složitých aplikací. Zejména se můžeme zaměřit na jednu dimenzi a ignorovat další aspekty.

Testování je běžný a známý scénář, kde je ortogonalita užitečná. Můžeme otestovat funkčnost úrovní protokolu pomocí vhodné pevné dvojice appenderu a rozvržení. Ortogonalita nám zaručuje, že nás nepřekvapí: úrovně protokolu budou fungovat stejně s jakoukoli kombinací appender a layout. Nejen, že je to pohodlné (je tu méně práce), ale je to také nutné, protože by nebylo možné testovat úrovně protokolu s každou známou kombinací appender a layout. To platí zejména vzhledem k tomu, že Log4j, stejně jako mnoho softwarových nástrojů a nástrojů, je navržen tak, aby byl rozšířen o třetí strany.

Snížení složitosti, které ortogonalita přináší softwarovým programům, je podobné tomu, jak se dimenze používají v geometrii, kde je komplikovaný pohyb bodů v n-dimenzionálním prostoru rozdělen na relativně jednoduchou manipulaci s vektory. Celé pole lineární algebry je založeno na této silné myšlence.

Navrhování a kódování ortogonality

Pokud teď přemýšlíte, jak navrhnout a kódovat ortogonalitu do svých programů, pak jste na správném místě. Klíčovou myšlenkou je použít abstrakce. Každá dimenze ortogonálního systému řeší jeden konkrétní aspekt programu. Takovou dimenzi obvykle představuje a typ (třída, rozhraní nebo výčet). Nejběžnějším řešením je použít abstraktní typ (rozhraní nebo abstraktní třída). Každý z těchto typů představuje dimenzi, zatímco instance typu představuje body v dané dimenzi. Protože abstraktní typy nelze přímo konkretizovat, jsou také potřeba konkrétní třídy.

V některých případech se bez nich obejdeme. Například nepotřebujeme konkrétní třídy, když je typ jen značením a nezakrývá chování. Pak můžeme pouze vytvořit instanci typu představujícího samotnou dimenzi a často předdefinovat pevnou sadu instancí, buď pomocí statických proměnných, nebo pomocí explicitního typu výčtu. V seznamu 1 by toto pravidlo platilo pro dimenzi „úroveň“.

Obrázek 3. Uvnitř dimenze Úroveň

Obecným pravidlem ortogonality je vyhýbat se odkazům na konkrétní konkrétní typy představující další aspekty (dimenze) programu. To vám umožní psát obecný kód, který bude fungovat stejně pro všechny možné instance. Takový kód může stále odkazovat na vlastnosti instancí, pokud jsou součástí rozhraní typu definujícího dimenzi.

Například v Log4j abstraktní typ Rozložení definuje metodu ignoresThrowable (). Tato metoda vrací boolean označující, zda rozložení může vykreslit trasování zásobníku výjimek nebo ne. Když appender používá rozložení, bylo by naprosto v pořádku napsat podmíněný kód ignoresThrowable (). Například přihlašovatel souboru může tisknout stopy zásobníku výjimek System.err při použití rozložení, které nemohlo zpracovat výjimky.

Podobným způsobem, a Rozložení implementace by se mohla vztahovat ke konkrétnímu Úroveň při vykreslování událostí protokolování. Například pokud byla úroveň protokolu ÚROVEŇ CHYBA, implementace rozložení založená na HTML by mohla zabalit zprávu protokolu do značek, které ji vykreslí červeně. Opět jde o to ÚROVEŇ CHYBA je definováno Úroveň, typ představující kótu.

Měli byste se však vyhnout odkazům na konkrétní třídy implementace pro jiné dimenze. Pokud přihlašovatel používá rozvržení, není třeba to vědět jaký druh rozložení to je. Obrázek 4 ilustruje dobré a špatné reference.

Několik vzorů a rozhraní usnadňuje vyhnutí se závislostem na typy implementace, včetně vkládání závislostí a vzoru vyhledávače služeb.

Porušení ortogonality

Celkově je Log4j dobrým příkladem použití ortogonality. Některý kód v Log4j však tento princip porušuje.

Log4j obsahuje volaného s názvem JDBCAppender, který se používá k přihlášení do relační databáze. Vzhledem k škálovatelnosti a popularitě relační databáze a skutečnosti, že to umožňuje snadné prohledávání událostí protokolu (pomocí dotazů SQL), JDBCAppender je důležitý případ použití.

JDBCAppender je určen k řešení problému s protokolováním do relační databáze přeměnou událostí protokolu na SQL VLOŽIT prohlášení. Tento problém řeší pomocí a PatternLayout.

PatternLayout používá šablon, aby uživateli poskytl maximální flexibilitu při konfiguraci řetězců generovaných z událostí protokolu. Šablona je definována jako řetězec a proměnné použité v šabloně jsou vytvořeny z událostí protokolu za běhu, jak je znázorněno v seznamu 2.

Výpis 2. PatternLayout

Řetězcový vzor = "% p [@% d {dd MMM rrrr HH: mm: ss} v% t]% m% n"; Layout layout = new org.apache.log4j.PatternLayout (pattern); appender.setLayout (rozložení);

JDBCAppender používá a PatternLayout se vzorem, který definuje SQL VLOŽIT prohlášení. Následující kód lze použít zejména k nastavení použitého příkazu SQL:

Výpis 3. Příkaz vložení SQL

public void setSql (String s) {sqlStatement = s; if (getLayout () == null) {this.setLayout (nový PatternLayout (s)); } else {((PatternLayout) getLayout ()). setConversionPattern (s); }}

Do tohoto kódu je zabudován implicitní předpoklad, že rozložení, pokud je nastaveno před použitím setLayout (rozložení) metoda definovaná v Dodatek, je ve skutečnosti příkladem PatternLayout. Z hlediska ortogonality to znamená, že najednou mnoho bodů v 3D krychli, které používají JDBCAppender s jiným rozložením než PatternLayout již nepředstavují platné konfigurace systému! To znamená, že jakékoli pokusy o nastavení řetězce SQL s jiným rozložením by vedly k výjimce za běhu (obsazení třídy).

Obrázek 5. JDBCAppender porušuje ortogonalitu

Existuje ještě další důvod JDBCAppenderdesign je sporný. JDBC má vlastní připravené příkazy šablony. Používáním PatternLayout, nicméně, stroj šablony je obejit. To je nešťastné, protože předkompilace JDBC připravily prohlášení, což vedlo k významnému zlepšení výkonu. Bohužel pro to neexistuje žádná snadná oprava. Zjevným přístupem by bylo řídit, v jakém rozložení lze použít JDBCAppender přepsáním nastavovače následujícím způsobem.

Výpis 4. Přepsání setLayout ()

public void setLayout (rozložení rozložení) {if (layout instanceOf PatternLayout) {super.setLayout (rozložení); } else {hodit novou IllegalArgumentException ("Rozložení není platné"); }}

Tento přístup má bohužel také problémy. Metoda v seznamu 4 vyvolá runtime výjimku a aplikace volající tuto metodu nemusí být připraveny ji zachytit. Jinými slovy setLayout (rozložení rozvržení) metoda nemůže zaručit, že nebude vyvolána žádná runtime výjimka; proto oslabuje záruky (dodatečné podmínky) poskytnuté metodou, kterou přepíše. Podíváme-li se na to z hlediska předpokladů, setLayout vyžaduje, aby rozložení bylo instancí PatternLayout, a proto má silnější předpoklady než metoda, kterou přepíše. Ať tak či onak, porušili jsme základní objektově orientovaný princip návrhu, což je liskovský substituční princip používaný k ochraně dědičnosti.

Řešení

Skutečnost, že neexistuje snadné řešení, jak opravit design JDBCAppender naznačuje, že v práci existuje hlubší problém. V tomto případě je úroveň abstrakce zvolena při navrhování základních abstraktních typů (zejména Rozložení) vyžaduje jemné doladění. Základní metoda definovaná Rozložení je formát (událost LoggingEvent). Tato metoda vrací řetězec. Při protokolování do relační databáze je ale třeba vygenerovat n-tici hodnot (řádek) a nikoli řetězec.

Jedním z možných řešení by bylo použití sofistikovanější datové struktury jako návratového typu pro formát. To by však znamenalo další režii v situacích, kdy byste skutečně chtěli vygenerovat řetězec. Bude nutné vytvořit další zprostředkující objekty a poté je shromáždit, což by ohrozilo výkon rámce protokolování. Použití sofistikovanějšího typu návratu by také ztížilo pochopení Log4j. Jednoduchost je velmi žádoucí designový cíl.

Dalším možným řešením by bylo použití „vrstvené abstrakce“ pomocí dvou abstraktních typů, Dodatek a CustomizableAppender který se prodlužuje Dodatek. Pouze CustomizableAppender by pak definoval metodu setLayout (rozložení rozvržení). JDBCAppender by jen implementovat Dodatek, zatímco jiné implementace appenderu jako ConsoleAppender by realizovat CustomizableAppender. Nevýhodou tohoto přístupu je zvýšená složitost (např. Způsob zpracování konfiguračních souborů Log4j) a skutečnost, že vývojáři musí učinit informované rozhodnutí o tom, jakou úroveň abstrakce použít dříve.

Závěrem

V tomto článku jsem použil Log4j jako příklad k prokázání jak principu návrhu ortogonality, tak příležitostného kompromisu mezi dodržováním principu designu a dosažením atributu kvality systému, jako je škálovatelnost. I v případech, kdy není možné dosáhnout úplné ortogonality, se domnívám, že kompromis by měl být vědomým rozhodnutím a měl by být dobře zdokumentován (například jako technický dluh). V části Zdroje se dozvíte více o konceptech a technologiích diskutovaných v tomto článku.

$config[zx-auto] not found$config[zx-overlay] not found