Programiranje

iContract: pogodbeno oblikovanje v Javi

Ali ne bi bilo lepo, če bi vsi razredi Java, ki jih uporabljate, tudi vaši, izpolnili svoje obljube? Pravzaprav, ali ne bi bilo lepo, če bi dejansko natančno vedeli, kaj dani razred obljublja? Če se strinjate, preberite - Design by Contract in iContract priskočita na pomoč.

Opomba: Vir kode za primere v tem članku lahko prenesete iz virov.

Oblikovanje po pogodbi

Tehnika razvoja programske opreme Design by Contract (DBC) zagotavlja visoko kakovostno programsko opremo in zagotavlja, da vsaka komponenta sistema izpolnjuje svoja pričakovanja. Kot razvijalec, ki uporablja DBC, določite komponento pogodbe kot del vmesnika komponente. Pogodba določa, kaj ta komponenta pričakuje od strank in kaj stranke lahko od nje pričakujejo.

Bertrand Meyer je DBC razvil kot del svojega Eifflovega programskega jezika. DBC je ne glede na izvor dragocena tehnika oblikovanja za vse programske jezike, vključno z Javo.

DBC je osrednjega pomena pojem trditev - logični izraz o stanju programskega sistema. Med izvajanjem ocenjujemo trditve na določenih kontrolnih točkah med izvajanjem sistema. V veljavnem programskem sistemu so vse trditve resnične. Z drugimi besedami, če katera koli trditev šteje za napačno, štejemo sistem programske opreme za neveljavnega ali pokvarjenega.

Osrednji pojem DBC se nekoliko nanaša na #assert makro v programskem jeziku C in C ++. Vendar DBC uveljavlja trditve še za milijon ravni.

V DBC prepoznamo tri različne vrste izrazov:

  • Predpogoji
  • Postpogoji
  • Invariante

Vsakega podrobneje preučimo.

Predpogoji

Predpogoji določajo pogoje, ki jih morajo imeti pred izvajanjem metode. Kot taki so ovrednoteni tik pred izvajanjem metode. Predpogoji vključujejo stanje sistema in argumente, posredovane v metodo.

Predpogoji določajo obveznosti, ki jih mora izpolniti odjemalec programske komponente, preden se lahko sklicuje na določeno metodo komponente. Če predpogoj ne uspe, je napaka v odjemalcu programske komponente.

Postpogoji

Nasprotno pa postpogoji določajo pogoje, ki morajo veljati po zaključku metode. Posledično se postpogoji izvedejo po zaključku metode. Postpogoji vključujejo staro stanje sistema, novo stanje sistema, argumente metode in vrnjeno vrednost metode.

Postconditions določajo jamstva, ki jih programska komponenta daje svojim strankam. Če je kršen postpogoj, ima programska komponenta napako.

Invariante

Invarianta podaja pogoj, ki mora vsebovati kadar koli lahko odjemalec prikliče metodo predmeta. Invariante so opredeljene kot del definicije razreda. V praksi se invariante ocenijo kadar koli pred in po izvedbi metode v katerem koli primerku razreda. Kršitev invariante lahko pomeni napako v odjemalcu ali programski komponenti.

Trditve, dedovanje in vmesniki

Vse trditve, podane za razred in njegove metode, veljajo tudi za vse podrazrede. Za vmesnike lahko določite tudi trditve. Kot take morajo vse trditve vmesnika veljati za vse razrede, ki izvajajo vmesnik.

iContract - DBC z Javo

Doslej smo govorili o DBC na splošno. Verjetno že imate nekaj idej, o čem govorim, toda če ste nov v DBC, bodo stvari morda še vedno nekoliko meglene.

V tem poglavju bodo stvari postale bolj konkretne. iContract, ki ga je razvil Reto Kamer, Javi doda konstrukte, ki omogočajo določitev trditev DBC, o katerih smo govorili prej.

Osnove iContract

iContract je predprocesor za Javo. Če jo želite uporabiti, svojo kodo Java najprej obdelate z iContract in ustvarite nabor okrašenih datotek Java. Nato okrašeno kodo Java z običajnim prevajalnikom prevedete.

Vse direktive iContract v kodi Java se nahajajo v komentarjih razredov in metod, tako kot direktive Javadoc. Na ta način iContract zagotavlja popolno združljivost z obstoječo kodo Java in svojo kodo Java lahko vedno sestavite neposredno brez trditev iContract.

V tipičnem življenjskem ciklu programa bi sistem iz razvojnega okolja premaknili v testno, nato v produkcijsko okolje. V razvojnem okolju bi svojo kodo opremili s trditvami iContract in jo zagnali. Tako lahko zgodaj ujamete na novo uvedene napake. V testnem okolju boste morda še vedno želeli ohraniti večino trditev omogočenih, vendar jih morate odstraniti iz razredov, ki so kritični za delovanje. Včasih je celo smiselno, da so nekatere trditve omogočene v produkcijskem okolju, vendar le v razredih, ki zagotovo niso nikakor kritični za delovanje vašega sistema. iContract vam omogoča izrecno izbiro razredov, ki jih želite opredeliti s trditvami.

Predpogoji

V iContract postavite predpogoje v glavo metode z uporabo @pre direktive. Tu je primer:

/ ** * @pre f> = 0,0 * / javni float sqrt (float f) {...} 

Primer predpogoja zagotavlja, da argument f funkcije sqrt () je večja ali enaka nič. Stranke, ki uporabljajo to metodo, so odgovorne za spoštovanje tega predpogoja. Če se ne, smo kot izvajalci sqrt () preprosto niso odgovorni za posledice.

Izraz po @pre je logični izraz Java.

Postpogoji

Postpogoji se dodajo tudi komentarju glave metode, ki ji pripadajo. V programu iContract je @post direktiva opredeljuje postpogoje:

/ ** * @pre f> = 0,0 * @post Math.abs ((return * return) - f) <0,001 * / public float sqrt (float f) {...} 

V našem primeru smo dodali postpogoj, ki zagotavlja, da sqrt () metoda izračuna kvadratni koren iz f znotraj določene meje napake (+/- 0,001).

iContract uvaja nekatere posebne zapise za postkondicije. Najprej, vrnitev pomeni vrnjeno vrednost metode. Med izvajanjem bo to nadomeščeno z vrnjeno vrednostjo metode.

V postpogojih je pogosto treba razlikovati med vrednostjo argumenta prej izvajanje metode in kasneje, podprto v iContract z @pre operater. Če dodate @pre izrazu v postkondiciji bo ovrednoten na podlagi stanja sistema, preden se metoda izvede:

/ ** * Dodaj element zbirki. * * @post c.size () = [email protected] () + 1 * @post c.contains (o) * / public void append (Zbirka c, Predmet o) {...} 

V zgornji kodi prvi postpogoj določa, da se mora velikost zbirke povečati za 1, ko dodamo element. Izraz c @ pre se nanaša na zbirko c pred izvedbo priloži metoda.

Invariante

Z iContract lahko v komentarju glave definicije razreda določite invariante:

/ ** * Pozitivno celo število je celo število, ki je zajamčeno pozitivno. * * @inv intValue ()> 0 * / razred PositiveInteger razširi celo število {...} 

V tem primeru invariant zagotavlja, da PositiveIntegerje vrednost vedno večja ali enaka nič. Ta trditev se preveri pred in po izvedbi katere koli metode tega razreda.

Object Constraint Language (OCL)

Čeprav so izrazi trditve v iContractu veljavni izrazi Java, so modelirani po podmnožici jezika omejitev objektov (OCL). OCL je eden od standardov, ki ga vzdržuje in usklajuje skupina za upravljanje predmetov ali OMG. (OMG skrbi za CORBA in sorodne stvari, če zamudite povezavo.) OCL naj bi določil omejitve v orodjih za objektno modeliranje, ki podpirajo Unified Modeling Language (UML), drug standard, ki ga varuje OMG.

Ker je jezik izrazov iContract oblikovan po vzoru OCL, ponuja nekaj naprednih logičnih operaterjev, ki presegajo lastne logične operaterje Jave.

Merilniki: celotni in obstajajo

iContract podpira za vse in obstaja količniki. The za vse kvantifikator določa, da mora veljati pogoj za vsak element v zbirki:

/ * * @invariantni forall IEeeee e v getEfficiees () | * getRooms (). vsebuje (e.getOffice ()) * / 

Zgornja invarianta določa, da vsak zaposleni, ki ga je vrnil getEfficiees () ima pisarno v zbirki sob, ki jih je vrnil getRooms (). Razen za vse ključna beseda je sintaksa enaka sintaksi obstaja izraz.

Tu je primer uporabe obstaja:

/ ** * @post obstaja IRoom r v getRooms () | r.isAvailable () * / 

Ta postpogoj določa, da po izvedbi povezane metode zbirka vrne getRooms () bo vseboval vsaj eno prosto sobo. The obstaja nadaljuje tip Java elementa zbirke - IRoom v primeru. r je spremenljivka, ki se nanaša na kateri koli element v zbirki. The v Ključni besedi sledi izraz, ki vrne zbirko (Naštevanje, Matrika, ali Zbirka). Temu izrazu sledi navpična vrstica, čemur sledi pogoj, ki vključuje spremenljivko elementa, r v primeru. Zaposlite obstaja kvantifikator, kadar mora pogoj veljati za vsaj en element v zbirki.

Oboje za vse in obstaja se lahko uporablja za različne vrste zbirk Java. Podpirajo Naštevanjes, Matrikas in Zbirkas.

Posledice: implicira

iContract ponuja pomeni operator, da poda omejitve obrazca, "Če velja A, mora veljati tudi B." Pravimo: "A pomeni B." Primer:

/ ** * @invariant getRooms (). isEmpty () implicira getEfficiees (). isEmpty () // brez sob, brez zaposlenih * / 

Ta invariant izraža, da ko getRooms () zbirka je prazna, getEfficiees () tudi zbirka mora biti prazna. Upoštevajte, da ne določa, kdaj getEfficiees () je prazno, getRooms () mora biti tudi prazno.

Prav tako lahko kombinirate logične operatorje, ki ste jih pravkar uvedli, in oblikujete zapletene trditve. Primer:

/ ** * @invariantni forall IEeeeee e1 v getEfficiees () | * za vse IEeeeee2 v getEeueeees () | * (e1! = e2) pomeni e1.getOffice ()! = e2.getOffice () // ena pisarna na zaposlenega * / 

Omejitve, dedovanje in vmesniki

iContract širi omejitve vzdolž odnosov med dedovanjem in izvajanjem vmesnikov med razredi in vmesniki.

Recimo razred B podaljša razred A. Razred A definira nabor invariant, predpogojev in postpogojev. V tem primeru invariante in predpogoji razreda A velja za razred B pa tudi metode v razredu B mora izpolnjevati enake postpogoje kot razred A zadovoljuje. V razred lahko dodate bolj omejevalne trditve B.

Omenjeni mehanizem deluje tudi za vmesnike in izvedbe. Recimo A in B so vmesniki in razred C izvaja oboje. V tem primeru, C je podvržen invariantam, predpogojem in postpogojem obeh vmesnikov, A in B, pa tudi tiste, ki so določene neposredno v razredu C.

Pazite se stranskih učinkov!

iContract bo izboljšal kakovost vaše programske opreme, tako da vam bo omogočil, da zgodaj odkrijete številne možne napake. Lahko pa si tudi ustrelite nogo (to je, predstavite nove napake) z uporabo iContract. To se lahko zgodi, ko v trditvah iContract prikličete funkcije, ki povzročajo neželene učinke, ki spreminjajo stanje vašega sistema. To vodi do nepredvidljivega vedenja, ker se bo sistem obnašal drugače, ko boste sestavili kodo brez instrumentacije iContract.

Primer sklada

Oglejmo si celoten primer. Določil sem Stack vmesnik, ki opredeljuje znane operacije moje najljubše podatkovne strukture:

/ ** * @inv! isEmpty () implicira top ()! = null // niso dovoljeni ničli predmeti * / javni vmesnik Stack {/ ** * @pre o! = null * @post! isEmpty () * @post zgoraj () == o * / void push (Predmet o); / ** * @pre! isEmpty () * @post @return == top () @ pre * / Object pop (); / ** * @pre! isEmpty () * / Object top (); logična isEmpty (); } 

Ponujamo preprosto izvedbo vmesnika:

uvoz java.util. *; / ** * @inv isEmpty () implicira elements.size () == 0 * / javni razred StackImpl implementira Stack {private final LinkedList elements = new LinkedList (); javni void push (objekt o) {elements.add (o); } public Object pop () {final Object popped = top (); elements.removeLast (); vrnitev popped; } javni objekt top () {return elements.getLast (); } javno logično isEmpty () {return elements.size () == 0; }} 

Kot lahko vidite, Stack izvedba ne vsebuje nobenih trditev iContract. Namesto tega so vse trditve podane v vmesniku, kar pomeni, da je pogodba o komponentah sklada definirana v celoti v vmesniku. Samo s pogledom na Stack vmesnik in njegove trditve, Stackvedenje osebe je popolnoma določeno.

Zdaj dodamo majhen preizkusni program, da vidimo iContract v akciji:

javni razred StackTest {public static void main (String [] args) {final Stack s = new StackImpl (); s.push ("ena"); s.pop (); s.push ("dva"); s.push ("tri"); s.pop (); s.pop (); s.pop (); // povzroči, da trditev ne uspe}} 

Nato zaženemo iContract, da zgradimo primer sklada:

java -cp% CLASSPATH%; src; _contract_db; instr com.reliablesystems.iContract.Tool -Z -a -v -minv, pre, post> -b "javac -classpath% CLASSPATH%; src" -c "javac -classpath % CLASSPATH%; instr "> -n" javac -classpath% CLASSPATH%; _ contract_db; instr "-oinstr / @ p / @ f. @ E -k_contract_db / @ p src / *. Java 

Zgornja izjava zahteva malo razlage.

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