Ohjelmointi

Log4j-ortogonaalisuus esimerkin avulla

Ortogonaalisuus on käsite, jota käytetään usein kuvaamaan modulaarisia ja ylläpidettäviä ohjelmistoja, mutta se on helpommin ymmärrettävissä tapaustutkimuksella. Tässä artikkelissa Jens Dietrich demystifioi ortogonaalisuuden ja joitain niihin liittyviä suunnitteluperiaatteita osoittamalla niiden käytön suositussa Log4j-apuohjelmakirjastossa. Hän keskustelee myös siitä, kuinka Log4j rikkoo ortogonaalisuutta muutamassa tapauksessa, ja keskustelee esiin tulleiden ongelmien mahdollisista kiertotavoista.

Ortogonaalisuuden käsite perustuu kreikkalaiseen sanaan ortogōnios, mikä tarkoittaa "suorakulmaista". Sitä käytetään usein ilmaisemaan eri ulottuvuuksien välinen riippumattomuus. Kun esine liikkuu pitkin x-akseli kolmiulotteisessa tilassa, sen y ja z koordinaatit eivät muutu. Yhden ulottuvuuden muutos ei aiheuta muutosta toisessa ulottuvuudessa, mikä tarkoittaa, että yksi ulottuvuus ei voi aiheuttaa sivuvaikutuksia muille.

Tämä selittää, miksi ortogonaalisuuden käsitettä käytetään usein kuvaamaan modulaarista ja ylläpidettävää ohjelmistosuunnittelua: ajattelu järjestelmistä moniulotteisen tilan pisteinä (itsenäisten, ortogonaalisten ulottuvuuksien synnyttäminä) auttaa ohjelmistokehittäjiä varmistamaan, että muutoksemme järjestelmän yhteen osaan ei ole sivuvaikutuksia toiselle.

Sattuu, että Log4j, suosittu avoimen lähdekoodin kirjauspaketti Java: lle, on hyvä esimerkki ortogonaalisuuteen perustuvasta moduulirakenteesta.

Log4j: n mitat

Kirjaaminen on vain miellyttävämpi versio System.out.println () lause, ja Log4j on apuohjelma, joka tiivistää logiikan mekaniikan Java-alustalle. Log4j-ominaisuuksien avulla kehittäjät voivat muun muassa tehdä seuraavaa:

  • Kirjaudu eri liitteisiin (paitsi konsoliin, myös tiedostoihin, verkkopaikkoihin, relaatiotietokantoihin, käyttöjärjestelmän lokiohjelmiin ja muuhun)
  • Kirjaudu useilla tasoilla (kuten ERROR, WARN, INFO ja DEBUG)
  • Hallitse keskitetysti, kuinka paljon tietoa kirjataan tietyllä lokitasolla
  • Määritä eri asetteluilla, miten lokitapahtuma renderoidaan merkkijonoksi

Vaikka Log4j: llä on muita ominaisuuksia, keskityn näihin kolmeen sen toiminnallisuuden ulottuvuuteen tutkiakseen ortogonaalisuuden käsitettä ja etuja. Huomaa, että keskusteluni perustuu Log4j-versioon 1.2.17.

Log4j JavaWorldissa

Hanki yleiskatsaus Log4j: stä ja oppia kirjoittamaan oma mukautetut Log4j-liitteet. Haluatko lisää Java-oppaita? Hanki Enterprise Java -uutiskirje toimitetaan postilaatikkoosi.

Otetaan huomioon Log4j-tyypit

Liitteet, taso ja asettelu ovat Log4j: n kolme näkökohtaa, jotka voidaan nähdä itsenäisinä ulottuvuuksina. Käytän termiä näkökohta tässä synonyyminä koskea, mikä tarkoittaa jotain kiinnostavaa tai kohdennettua ohjelmaa. Tässä tapauksessa on helppo määritellä nämä kolme ongelmaa kunkin kysymyksen perusteella:

  • Liite: Mihin lokitapahtumatiedot on lähetettävä näyttöä tai tallennusta varten?
  • Layout: Kuinka lokitapahtuma tulisi esittää?
  • Taso: Mitkä lokitapahtumat tulisi käsitellä?

Yritä nyt tarkastella näitä näkökohtia yhdessä kolmiulotteisessa tilassa. Jokainen tämän tilan piste edustaa kelvollista järjestelmän kokoonpanoa, kuten kuvassa 1 on esitetty. (Huomaa, että tarjoan hieman yksinkertaistetun näkymän Log4j: Kuvan 1 jokainen piste ei todellakaan ole maailmanlaajuinen koko järjestelmän kokoonpano, vaan yhden Puunkorjuijoita itse voidaan pitää neljäntenä ulottuvuutena.)

Listaus 1 on tyypillinen Log4j-koodinpätkä:

Listaus 1. Esimerkki Log4j-toteutuksesta

// asetusten kirjaaminen! Logger logger = Logger.getLogger ("Foo"); Appender appender = uusi ConsoleAppender (); Asettelun asettelu = new org.apache.log4j.TTCCLayout () appender.setLayout (layout); logger.addAppender (appender); logger.setLevel (taso.INFO); // aloita kirjaaminen! logger.warn ("Hei maailma");

Haluan sinun huomaavan tämän koodin on, että se on ortogonaalinen: voit muuttaa liittimen, asettelun tai tason näkökohdan rikkomatta koodia, mikä pysyisi täysin toiminnassa. Ortogonaalisuunnittelussa kukin piste ohjelman annetussa tilassa on kelvollinen järjestelmän kokoonpano. Mitään rajoituksia ei saa rajoittaa, mitkä pisteet mahdollisten kokoonpanojen alueella ovat kelvollisia.

Ortogonaalisuus on tehokas käsite, koska sen avulla voimme luoda suhteellisen yksinkertaisen henkisen mallin monimutkaisiin sovelluskäyttötapauksiin. Voimme keskittyä erityisesti yhteen ulottuvuuteen jättämättä huomiotta muita näkökohtia.

Testaus on yleinen ja tuttu skenaario, jossa ortogonaalisuudesta on hyötyä. Voimme testata lokitasojen toimivuutta käyttämällä sopivaa kiinteää lisäysparia ja asettelua. Ortogonaalisuus varmistaa meille, että yllätyksiä ei tule: lokitasot toimivat samalla tavalla minkä tahansa lisäyksen ja asettelun yhdistelmän kanssa. Paitsi että tämä on kätevää (työtä on vähemmän), se on myös välttämätöntä, koska lokimäärien testaaminen kaikilla tunnetuilla lisäyksen ja asettelun yhdistelmillä olisi mahdotonta. Tämä pätee erityisesti, kun otetaan huomioon, että Log4j, kuten monet ohjelmistotyökalut ja apuohjelmat, on suunniteltu laajennettavaksi kolmansille osapuolille.

Ortogonaalisuuden aiheuttama monimutkaisuuden väheneminen ohjelmisto-ohjelmille on samanlainen kuin mitat käytetään geometriassa, jossa pisteiden monimutkainen liike n-ulotteisessa tilassa hajotetaan suhteellisen yksinkertaiseksi vektorien manipuloinniksi. Tähän voimakkaaseen ajatukseen perustuu koko lineaarisen algebran kenttä.

Suunnittelu ja koodaus ortogonaalisuudelle

Jos mietit nyt, kuinka suunnitella ja koodata ortogonaalisuus ohjelmistoihisi, olet oikeassa paikassa. Keskeinen ajatus on käyttää abstraktio. Kukin ortogonaalisen järjestelmän ulottuvuus käsittää yhden ohjelman osan. Tällaista ulottuvuutta edustaa yleensä a tyyppi (luokka, rajapinta tai luettelo). Yleisin ratkaisu on käyttää abstrakti tyyppi (käyttöliittymä tai abstrakti luokka). Jokainen näistä tyypeistä edustaa ulottuvuutta, kun taas tyyppiesimerkki edustaa pisteitä annetussa ulottuvuudessa. Koska abstrakteja tyyppejä ei voida suoraan instantisoida, tarvitaan myös konkreettisia luokkia.

Joissakin tapauksissa voimme tehdä ilman niitä. Esimerkiksi, emme tarvitse konkreettisia luokkia, kun tyyppi on vain merkintä eikä sisällä kapselointia. Sitten voimme vain instantisoida itse ulottuvuutta edustavan tyypin ja määritellä usein ennalta kiinteän joukon instansseja joko käyttämällä staattisia muuttujia tai käyttämällä nimenomaista luettelotyyppiä. Luettelossa 1 tätä sääntöä sovellettaisiin "taso" -ulottuvuuteen.

Kuva 3. Taso-ulottuvuuden sisällä

Ortogonaalisuuden yleissääntönä on välttää viittauksia tiettyihin konkreettisiin tyyppeihin, jotka edustavat ohjelman muita näkökohtia (ulottuvuuksia). Tämän avulla voit kirjoittaa yleiskoodin, joka toimii samalla tavalla kaikissa mahdollisissa esiintymissä. Tällainen koodi voi silti viitata instanssien ominaisuuksiin, kunhan ne ovat osa ulottuvuutta määrittelevän tyypin rajapintaa.

Esimerkiksi Log4j: ssä abstrakti tyyppi Layout määrittelee menetelmän ohittaa Heitettävä (). Tämä menetelmä palauttaa loogisen arvon, joka ilmaisee, voiko ulkoasu tehdä poikkeuspinojen jäljet ​​vai ei. Kun hakija käyttää asettelua, olisi ehdottoman hienoa kirjoittaa ehdollinen koodi ohittaa Heitettävä (). Esimerkiksi tiedostonlisäosa voisi tulostaa poikkeuspinon jäljet System.err käytettäessä asettelua, joka ei kestänyt poikkeuksia.

Samalla tavalla a Layout toteutus voisi viitata tiettyyn Taso kun hahmonnetaan lokitapahtumia. Esimerkiksi, jos lokitaso oli Taso VIRHE, HTML-pohjainen asettelutoteutus voi kääri lokiviestin tunnisteisiin, jotka tekevät siitä punaisen. Jälleen, asia on siinä Taso VIRHE on määritelty Taso, tyyppi, joka edustaa ulottuvuutta.

Sinun tulisi kuitenkin välttää viittauksia tiettyihin toteutusluokkiin muille ulottuvuuksille. Jos hakija käyttää asettelua, ei tarvitse tietää minkälainen asettelusta se on. Kuva 4 kuvaa hyviä ja huonoja viitteitä.

Useat mallit ja kehykset helpottavat riippuvuuksien välttämistä toteutustyypeistä, mukaan lukien riippuvuusinjektio ja palvelun paikannusmalli.

Ortogonaalisuuden rikkominen

Kaiken kaikkiaan Log4j on hyvä esimerkki ortogonaalisuuden käytöstä. Jotkut Log4j: n koodit rikkovat kuitenkin tätä periaatetta.

Log4j sisältää sovelluksen nimeltä JDBCAppender, jota käytetään kirjautumiseen relaatiotietokantaan. Kun otetaan huomioon relaatiotietokannan skaalautuvuus ja suosio sekä se, että tämä tekee lokitapahtumista helposti haettavissa (SQL-kyselyillä), JDBCAppender on tärkeä käyttötapaus.

JDBCAppender on tarkoitettu ongelman kirjaamiseen relaatiotietokantaan muuttamalla lokitapahtumat SQL: ksi LISÄÄ lausunnot. Se ratkaisee tämän ongelman käyttämällä a Kuvion asettelu.

Kuvion asettelu käyttää mallipohjaa antaa käyttäjälle maksimaalisen joustavuuden määrittää lokitapahtumista luodut merkkijonot. Malli on määritelty merkkijonoksi, ja mallissa käytetyt muuttujat instantisoidaan lokitapahtumista ajon aikana, kuten luettelossa 2 on esitetty.

Listaus 2. PatternLayout

Merkkijono = "% p [@% d {pp KK vvvv HH: mm: ss}% t]% m% n"; Asettelun asettelu = new org.apache.log4j.PatternLayout (pattern); appender.setLayout (asettelu);

JDBCAppender käyttää a Kuvion asettelu mallilla, joka määrittelee SQL: n LISÄÄ lausunto. Seuraavaa koodia voidaan käyttää käytetyn SQL-käskyn asettamiseen:

Listaus 3. SQL-lisäyslauseke

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

Tähän koodiin on sisällytetty implisiittinen oletus, että asettelu, jos se on asetettu ennen setLayout (Asettelu) kohdassa määritelty menetelmä Liite, on itse asiassa esimerkki Kuvion asettelu. Ortogonaalisuuden kannalta tämä tarkoittaa, että yhtäkkiä 3D-kuution pisteitä käytetään yhtäkkiä JDBCAppender muilla asetteluilla kuin Kuvion asettelu eivät enää edustaa kelvollisia järjestelmän kokoonpanoja! Toisin sanoen kaikki yritykset asettaa SQL-merkkijono erilaisella asettelulla aiheuttaisivat ajonaikaisen (luokan heittoyksikön) poikkeuksen.

Kuva 5. JDBCAppender rikkoo ortogonaalisuutta

Tähän on toinenkin syy JDBCAppenderSuunnittelu on kyseenalaista. JDBC: llä on omat mallimoottorivalmistelut. Käyttämällä Kuvion asettelu, mutta mallimoottori ohitetaan. Tämä on valitettavaa, koska JDBC laatii esikoostetut lausunnot, mikä johtaa merkittäviin suorituskyvyn parannuksiin. Valitettavasti tähän ei ole helppoa korjausta. Ilmeinen lähestymistapa olisi hallita, millaista asettelua voidaan käyttää JDBCAppender ohittamalla asettajan seuraavasti.

Listaus 4. Overriding setLayout ()

public void setLayout (Layout layout) {if (layout instanceOf PatternLayout) {super.setLayout (layout); } else {heittää uusi IllegalArgumentException ("Asettelu ei kelpaa"); }}

Valitettavasti myös tällä lähestymistavalla on ongelmia. Luettelon 4 menetelmä heittää ajonaikaisen poikkeuksen, ja tätä menetelmää kutsuvat sovellukset eivät välttämättä ole valmiita saamaan sitä kiinni. Toisin sanoen setLayout (Asettelun asettelu) menetelmä ei voi taata, ettei ajonaikaista poikkeusta heitetä; Siksi se heikentää ohitetun menetelmän takauksia (jälkisehtoja). Jos katsomme sitä ennakkoehdoilla, setLayout edellyttää, että asettelu on esimerkki Kuvion asettelu, ja siksi vahvempi ennakkoedellytykset kuin menetelmä, jonka se ohittaa. Kummassakin tapauksessa olemme rikkoneet ydinolosuhteisiin perustuvaa suunnitteluperiaatetta, joka on perinnön turvaamiseksi käytetty Liskov-korvausperiaate.

Kiertotavat

Se, että suunnittelun korjaamiseen ei ole helppoa ratkaisua JDBCAppender osoittaa, että työssä on syvempi ongelma. Tässä tapauksessa abstraktin ydintyyppien suunnittelussa valittu abstraktiotaso (erityisesti Layout) tarvitsee hienosäätöä. Ydinmenetelmä, jonka määrittelee Layout On muoto (LoggingEvent-tapahtuma). Tämä menetelmä palauttaa merkkijonon. Kun kirjaudut relaatiotietokantaan, arvoja (rivi) eikä merkkijonoa tarvitse luoda.

Yksi mahdollinen ratkaisu olisi käyttää kehittyneempää tietorakennetta muodon palautustyypiksi. Tämä merkitsisi kuitenkin ylimääräisiä yleiskustannuksia tilanteissa, joissa haluat todella luoda merkkijonon. Muita väliobjekteja olisi luotava ja sitten kerättävä roskat, mikä vaarantaa puunkorjuukehyksen suorituskyvyn. Kehittyneemmän palautustyypin käyttäminen vaikeuttaisi myös Log4j: n ymmärtämistä. Yksinkertaisuus on erittäin toivottava suunnittelutavoite.

Toinen mahdollinen ratkaisu olisi käyttää "kerrostettua abstraktiota" käyttämällä kahta abstraktityyppiä, Liite ja CustomizableAppender joka ulottuu Liite. Vain CustomizableAppender sitten määritellä menetelmä setLayout (Asettelun asettelu). JDBCAppender vain toteuttaa Liite, kun taas muut appender-toteutukset, kuten ConsoleAppender toteuttaisi CustomizableAppender. Tämän lähestymistavan haittana on lisääntynyt monimutkaisuus (esim. Miten Log4j-kokoonpanotiedostoja käsitellään) ja se, että kehittäjien on tehtävä tietoinen päätös siitä, mitä abstraktiotasoa käytetään aikaisin.

Tiivistettynä

Tässä artikkelissa olen käyttänyt Log4j: tä esimerkkinä sekä ortogonaalisuuden suunnitteluperiaatteesta että satunnaisesta kompromissista suunnitteluperiaatteen noudattamisen ja järjestelmän laatuominaisuuksien, kuten skaalautuvuuden, välillä. Jopa tapauksissa, joissa on mahdotonta saavuttaa täyttä ortogonaalisuutta, uskon, että kompromissin on oltava tietoinen päätös ja että se on dokumentoitava hyvin (esimerkiksi teknisenä velkana). Katso Resurssit-osiosta lisätietoja tässä artikkelissa käsitellyistä käsitteistä ja tekniikoista.

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