Navrhovanie užívateľsky prívetivej knižnice Java

1. Prehľad

Java je jedným z pilierov sveta otvoreného zdroja. Takmer každý projekt Java využíva iné open-source projekty, pretože nikto nechce znovuobjaviť koleso. Mnohokrát sa však stane, že potrebujeme knižnicu kvôli jej funkčnosti, ale nemáme potuchy, ako ju používať. Narazíme na veci ako:

  • Čo je to so všetkými týmito triedami služby „*“?
  • Ako to urobím, vyžaduje to príliš veľa závislostí. Čo je „západka“?
  • Ach, dal som to dokopy, ale teraz sa to začne hádzať IllegalStateException. Čo robím zle?

Problém je v tom, že nie všetci dizajnéri knižníc myslia na svojich používateľov. Väčšina myslí iba na funkčnosť a funkcie, ale málokto zvažuje, ako sa API bude v praxi používať a ako bude vyzerať a testovaný kód používateľov.

Tento článok obsahuje niekoľko rád, ako našim používateľom ušetriť niektoré z týchto problémov - a nie, nejde o písanie dokumentácie. Na túto tému by samozrejme mohla byť napísaná celá kniha (a niekoľko ich bolo); to sú niektoré z kľúčových bodov, ktoré som sa naučil pri práci na niekoľkých knižniciach sám.

Tu uvediem príklad týchto myšlienok pomocou dvoch knižníc: charles a jcabi-github

2. Hranice

Malo by to byť zrejmé, ale mnohokrát to tak nie je. Predtým, ako začneme písať akýkoľvek riadok kódu, musíme mať jasnú odpoveď na niektoré otázky: aké vstupy sú potrebné? Aká bude prvá trieda, ktorú uvidí môj používateľ? potrebujeme nejaké implementácie od používateľa? aký je výstup? Akonáhle budú tieto otázky jasne zodpovedané, všetko sa uľahčí, pretože knižnica už má podšívku, tvar.

2.1. Vstup

Toto je možno najdôležitejšia téma. Musíme sa ubezpečiť, že je jasné, čo musí používateľ knižnici poskytnúť, aby mohla vykonávať svoju prácu. V niektorých prípadoch je to veľmi triviálna záležitosť: môže to byť iba reťazec predstavujúci token auth pre API, ale môže to byť aj implementácia rozhrania alebo abstraktná trieda.

Veľmi dobrým zvykom je prevziať všetky závislosti prostredníctvom konštruktorov a zachovať ich krátke s niekoľkými parametrami. Ak potrebujeme mať konštruktor s viac ako tromi alebo štyrmi parametrami, potom by kód mal byť jednoznačne refaktorovaný. A ak sa na zavedenie povinných závislostí použijú metódy, potom používatelia s najväčšou pravdepodobnosťou skončia s treťou frustráciou popísanou v prehľade.

Mali by sme tiež vždy ponúknuť viac ako jedného konštruktora, dať používateľom alternatívy. Nech s oboma pracujú String a Celé číslo alebo ich neobmedzujte na a FileInputStream, pracovať s InputStream, aby mohli predložiť možno ByteArrayInputStream pri testovaní jednotky atď.

Tu je napríklad niekoľko spôsobov, ako môžeme vytvoriť inštanciu vstupného bodu API Github pomocou jcabi-github:

Github noauth = nový RtGithub (); Github basicauth = nový RtGithub ("používateľské meno", "heslo"); Github oauth = nový RtGithub („token“); 

Jednoduché, žiadny zhon, žiadne tienisté konfiguračné objekty na inicializáciu. A má zmysel mať týchto troch konštruktérov, pretože web Github môžete používať, keď ste odhlásení, prihlásení alebo sa môže vo vašom mene overiť aplikácia. Niektoré funkcie samozrejme nebudú fungovať, ak nebudete autentifikovaní, ale viete to od začiatku.

Ako druhý príklad uvádzame, ako by sme pracovali s knihou Charles, knižnicou prehľadávania webu:

Ovládač WebDriver = nový FirefoxDriver (); Úložisko repo = nové InMemoryRepository (); Reťazec indexPage = "//www.amihaiemil.com/index.html"; Graf WebCrawl = nový GraphCrawl (indexPage, ovládač, nové IgnoredPatterns (), repo); graph.crawl (); 

Verím tomu, že je to tiež dosť samozrejmé. Pri písaní tohto článku si však uvedomujem, že v súčasnej verzii je chyba: všetci konštruktéri vyžadujú, aby používateľ zadal inštanciu IgnorovanéVzory. Predvolene by sa nemali ignorovať žiadne vzory, ale používateľ by to nemusel špecifikovať. Rozhodol som sa to tu nechať tak, takže vidíte protikladný príklad. Predpokladám, že by ste sa pokúsili vytvoriť inštanciu WebCrawl a napadlo by vás: „Čo je to s tým IgnorovanéVzory?!”

Variabilná indexPage je adresa URL, od ktorej by sa malo začať indexové prehľadávanie, pričom ovládač je prehliadač, ktorý sa má používať (nemôže byť predvolene nastavený na nič, pretože nevieme, ktorý prehľadávač je nainštalovaný na bežiacom počítači). Repo premenná bude vysvetlená nižšie v nasledujúcej časti.

Ako teda vidíte v príkladoch, snažte sa, aby to bolo jednoduché, intuitívne a zrozumiteľné. Zapuzdrte logiku a závislosti takým spôsobom, aby si používateľ pri pohľade na vašich konštruktérov nepoškriabal hlavu.

Ak stále máte pochybnosti, skúste zadať požiadavky HTTP na AWS pomocou aws-sdk-java: budete sa musieť vysporiadať s takzvanou AmazonHttpClient, ktorá niekde používa ClientConfiguration, a potom niekde medzi tým musíte vziať ExecutionContext. Nakoniec by ste mohli splniť svoju požiadavku a dostať odpoveď, ale stále nemáte potuchy, čo je to napríklad ExecutionContext.

2.2. Výkon

Je to väčšinou pre knižnice, ktoré komunikujú s vonkajším svetom. Tu by sme mali odpovedať na otázku „ako bude spracovaný výstup?“. Opäť dosť vtipná otázka, ale je ľahké vykročiť zle.

Znova sa pozrite na vyššie uvedený kód. Prečo musíme zabezpečiť implementáciu úložiska? Prečo metóda WebCrawl.crawl () iba nevráti zoznam prvkov webovej stránky? Knižnica zjavne nemá za úlohu manipulovať s prehľadávanými stránkami. Ako by malo vôbec vedieť, čo by sme s nimi chceli robiť? Niečo také:

Graf WebCrawl = nový GraphCrawl (...); Zoznam stránok = graph.crawl (); 

Nič nemôže byť horšie. Výnimka OutOfMemory sa môže stať z ničoho nič, ak by prehľadávaný web náhodou mal povedzme 1 000 stránok - knižnica ich všetky načíta do pamäte. Existujú dve riešenia:

  • Stále vracajte stránky, ale implementujte nejaký mechanizmus stránkovania, v ktorom by používateľ musel zadať počiatočné a koncové čísla. Alebo
  • Požiadajte používateľa, aby implementoval rozhranie s metódou nazývanou export (zoznam), ktoré by algoritmus zavolal vždy, keď by sa dosiahol maximálny počet stránok.

Druhá možnosť je jednoznačne najlepšia; udržuje veci jednoduchšie na oboch stranách a je viac testovateľné. Popremýšľajte, koľko logiky by bolo treba implementovať na strane používateľa, keby sme išli s prvou. Takto je zadané úložisko pre stránky (je možné ich odoslať do databázy alebo ich napísať na disk) a po vyvolaní metódy crawl () sa už nemusí robiť nič iné.

Mimochodom, kód z vyššie uvedenej sekcie Vstup je všetko, čo musíme napísať, aby sme načítali obsah webových stránok (stále v pamäti, ako hovorí repo implementácia, ale je to naša voľba - túto implementáciu sme poskytli tak, riskujeme).

Ak zhrnieme túto časť: nikdy by sme nemali úplne oddeľovať svoju prácu od práce klienta. Mali by sme vždy myslieť na to, čo sa stane s výstupom, ktorý vytvoríme. Rovnako ako vodič nákladného vozidla by mal po príchode na miesto určenia pomôcť skôr s rozbalením tovaru, ako s jednoduchým vyhodením.

3. Rozhrania

Vždy používajte rozhrania. Používateľ by mal s našim kódom interagovať iba prostredníctvom prísnych zmlúv.

Napríklad v jcabi-github knižnica triedy RtGithub je jediná, ktorú používateľ v skutočnosti vidí:

Repo repo = new RtGithub ("oauth_token"). Repos (). Get (new Coordinates.Simple ("eugenp / tutorials")); Issue issue = repo.issues () .create ("Vzorové vydanie", "Vytvorené pomocou jcabi-github");

Vyššie uvedený úryvok vytvára lístok v repozitári eugenp / tutorials. Používajú sa inštancie repo a emisie, ale skutočné typy sa nikdy neodhalia. Nemôžeme urobiť niečo také:

Repo repo = nový RtRepo (...)

Vyššie uvedené nie je možné z logického dôvodu: nemôžeme priamo vytvoriť problém v repo Github, že? Najprv sa musíme prihlásiť, potom prehľadať repo a až potom môžeme vytvoriť problém. Vyššie uvedený scenár by sa samozrejme dal povoliť, ale kód používateľa by sa znečistil množstvom štandardného kódu: ten RtRepo pravdepodobne by musel prevziať nejaký autorizačný objekt prostredníctvom svojho konštruktora, autorizovať klienta a dostať sa do správneho repo atď.

Rozhrania tiež poskytujú ľahkú rozšíriteľnosť a spätnú kompatibilitu. Na jednej strane sme ako vývojári povinní rešpektovať už vydané zmluvy a na druhej strane môže užívateľ rozširovať nami ponúkané rozhrania - môže ich zdobiť alebo písať alternatívne implementácie.

Inými slovami, abstraktné a zapuzdrené čo najviac. Použitím rozhraní to dokážeme elegantným a neobmedzujúcim spôsobom - presadzujeme architektonické pravidlá a dávame programátorovi slobodu vylepšiť alebo zmeniť správanie, ktoré vystavujeme.

Na záver tejto časti nezabudnite na: našu knižnicu, naše pravidlá. Mali by sme presne vedieť, ako bude vyzerať kód klienta a ako ho bude testovať jednotka. Ak to nebudeme vedieť, nikto to neurobí a naša knižnica jednoducho prispeje k vytvoreniu kódu, ktorý je ťažké pochopiť a udržiavať.

4. Tretie strany

Majte na pamäti, že dobrá knižnica je ľahká knižnica. Váš kód môže vyriešiť problém a bude funkčný, ale ak jar pridá k mojej zostave 10 MB, potom je zrejmé, že ste plány svojho projektu stratili už dávno. Ak potrebujete veľa závislostí, pravdepodobne sa snažíte pokryť príliš veľa funkcií a mali by ste projekt rozdeliť na niekoľko menších projektov.

Ak je to možné, buďte čo najtransparentnejší, neviažte sa na skutočné implementácie. Najlepší príklad, ktorý vám napadne, je: použiť SLF4J, čo je iba API na protokolovanie - nepoužívajte priamo log4j, možno by užívateľ chcel používať iné protokolovacie nástroje.

Knižnice dokumentov, ktoré prechádzajú vašim projektom prechodne, a uistite sa, že neobsahujete nebezpečné závislosti, ako napr xalan alebo xml-apis (to, prečo sú nebezpečné, nie je potrebné rozpracovávať v tomto článku).

Záverom je: udržujte svoju zostavu ľahkú, priehľadnú a vždy vedzte, s čím pracujete. Môže to vašim používateľom ušetriť viac zhonu, ako si dokážete predstaviť.

5. Záver

V článku sa uvádza niekoľko jednoduchých nápadov, ktoré môžu pomôcť projektu zostať v súlade s ohľadom na použiteľnosť. Knižnica, ktorá by si mala nájsť miesto vo väčšom kontexte, by mala mať výkonnú funkčnosť a ponúka plynulé a dobre prepracované rozhranie.

Je to ľahký krok za čiaru a robí z dizajnu neporiadok. Prispievatelia budú vždy vedieť, ako ho použiť, ale niekto nový, kto to najskôr zazrie, by to nemusel. Produktivita je najdôležitejšia zo všetkých a na základe tohto princípu by mali byť používatelia schopní začať používať knižnicu v priebehu niekoľkých minút.


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