Ar nebūtų malonu, jei visos jūsų naudojamos „Java“ klasės, įskaitant ir jūsų pačių, išpildytų jų pažadus? Tiesą sakant, ar nebūtų malonu, jei iš tikrųjų tiksliai žinotumėte, ką žada tam tikra klasė? Jei sutinkate, skaitykite toliau - „Design by Contract“ ir „iContract“ ateina į pagalbą.
Pastaba: Šio straipsnio pavyzdžių kodo šaltinį galima atsisiųsti iš išteklių.
Projektavimas pagal sutartį
„DBC“ programinės įrangos kūrimo technika užtikrina aukštos kokybės programinę įrangą, garantuodama, kad kiekvienas sistemos komponentas pateisina jos lūkesčius. Kaip kūrėjas, naudojantis DBC, nurodote komponentą sutarčių kaip komponento sąsajos dalį. Sutartyje nurodoma, ko tas komponentas tikisi iš klientų ir ko klientai gali iš jo tikėtis.
Bertrandas Meyeris sukūrė DBC kaip savo Eifelio programavimo kalbos dalį. Nepaisant kilmės, DBC yra vertinga dizaino technika visoms programavimo kalboms, įskaitant „Java“.
DBC yra svarbiausia tvirtinimas - loginė išraiška apie programinės įrangos sistemos būseną. Vykdymo metu mes vertiname teiginius konkrečiuose kontrolės punktuose vykdant sistemą. Galiojančioje programinės įrangos sistemoje visi teiginiai yra teisingi. Kitaip tariant, jei kuris nors teiginys vertinamas kaip klaidingas, mes laikome programinės įrangos sistemą negaliojančia arba sugadinta.
Pagrindinė DBC sąvoka šiek tiek susijusi su #assert
makrokomanda C ir C ++ programavimo kalbomis. Tačiau DBC tvirtina dar milijardą.
DBC nustatome tris skirtingas išraiškų rūšis:
- Išankstinės sąlygos
- Postconditions
- Variantai
Panagrinėkime kiekvieną iš jų išsamiau.
Išankstinės sąlygos
Išankstinės sąlygos nurodo sąlygas, kurios turi atitikti metodo vykdymą. Jie yra vertinami prieš pradedant taikyti metodą. Išankstinės sąlygos apima sistemos būseną ir į metodą perduodamus argumentus.
Išankstinės sąlygos nurodo įsipareigojimus, kuriuos turi įvykdyti programinės įrangos komponento klientas, kad galėtų pasinaudoti tam tikru komponento metodu. Jei išankstinė sąlyga nepavyksta, programinės įrangos komponento kliente yra klaida.
Postconditions
Priešingai, vėlesnėse sąlygose nurodomos sąlygos, kurios turi galioti baigus metodą. Vadinasi, vėlesnės sąlygos vykdomos baigus metodą. Papildomos sąlygos apima seną sistemos būseną, naują sistemos būseną, metodo argumentus ir metodo grąžinimo vertę.
Postconditions nurodo garantijas, kurias programinės įrangos komponentas teikia savo klientams. Jei pažeista vėlesnė sąlyga, programinės įrangos komponentas turi klaidą.
Variantai
Nekintamasis nurodo sąlygą, kuri turi būti bet kada, kai klientas gali pasinaudoti objekto metodu. Variantai apibrėžiami kaip klasės apibrėžimo dalis. Praktiškai invariantai yra vertinami bet kuriuo metu prieš ir po metodo, kurį vykdo bet kurios klasės egzempliorius. Invarianto pažeidimas gali reikšti klaidą kliente arba programinės įrangos komponente.
Teiginiai, paveldėjimas ir sąsajos
Visi teiginiai, nurodyti klasei ir jos metodams, taip pat taikomi visiems poklasiams. Taip pat galite nurodyti sąsajų tvirtinimus. Visi sąsajos teiginiai turi atitikti visas klases, naudojančias sąsają.
„iContract“ - DBC su „Java“
Iki šiol mes kalbėjome apie DBC apskritai. Jūs tikriausiai jau turite idėją, apie ką kalbu, bet jei dar nesate nauji DBC, viskas vis tiek gali būti šiek tiek miglota.
Šiame skyriuje viskas taps konkretesnė. Reto Kamer sukurtas „iContract“ prideda „Java“ konstrukcijas, leidžiančias nurodyti DBC tvirtinimus, apie kuriuos kalbėjome anksčiau.
„iContract“ pagrindai
„iContract“ yra „Java“ pirminis procesorius. Norėdami jį naudoti, pirmiausia apdorokite „Java“ kodą naudodami „iContract“ ir sukurkite dekoruotų „Java“ failų rinkinį. Tada jūs sukompiliuojate dekoruotą Java kodą, kaip įprasta, naudodami Java kompiliatorių.
Visos „Java“ kodo „iContract“ direktyvos yra klasių ir metodų komentaruose, kaip ir „Javadoc“ direktyvos. Tokiu būdu „iContract“ užtikrina visišką atgalinį suderinamumą su esamu „Java“ kodu ir visada galite tiesiogiai sukompiliuoti savo „Java“ kodą be „iContract“ tvirtinimų.
Įprastu programos gyvavimo ciklu, jūs perkeltumėte savo sistemą iš kūrimo aplinkos į bandymo aplinką, tada į gamybos aplinką. Kūrimo aplinkoje savo kodą suprogramuosite su „iContract“ teiginiais ir paleisite. Tokiu būdu galite anksti sugauti naujai įvestas klaidas. Bandymo aplinkoje vis tiek galbūt norėsite, kad didžioji dalis teiginių būtų įgalinti, tačiau turėtumėte juos pašalinti iš kritinių efektyvumo klasių. Kartais prasminga kai kuriuos teiginius laikyti įgalintais gamybos aplinkoje, tačiau tik klasėse, kurios tikrai nėra jokios kritinės jūsų sistemos našumui. „iContract“ leidžia aiškiai pasirinkti klases, kurias norite patvirtinti teiginiais.
Išankstinės sąlygos
„IContract“ prielaidas pateikiate metodo antraštėje naudodami @pre
direktyvą. Štai pavyzdys:
/ ** * @pre f> = 0.0 * / public float sqrt (float f) {...}
Išankstinė sąlyga užtikrina, kad argumentas f
funkcijos sqrt ()
yra didesnis arba lygus nuliui. Klientai, kurie naudojasi tuo metodu, yra atsakingi už šios prielaidos laikymąsi. Jei jie to nepadaro, mes, kaip įgyvendintojai sqrt ()
tiesiog nėra atsakingi už pasekmes.
Išraiška po @pre
yra „Java“ loginė išraiška.
Postconditions
Postconditions taip pat pridedami prie metodo, kuriam jie priklauso, antraštės komentaro. Programoje „iContract“ @post
direktyva apibrėžia vėlesnes sąlygas:
/ ** * @pre f> = 0.0 * @post Math.abs ((return * return) - f) <0.001 * / public float sqrt (float f) {...}
Savo pavyzdyje mes pridėjome papildomą sąlygą, kuri užtikrina, kad sqrt ()
metodas apskaičiuoja kvadratinę šaknį f
neviršijant tam tikros paklaidos ribos (+/- 0,001).
„iContract“ pateikia keletą specifinių postcondition žymėjimų. Pirmiausia, grįžti
reiškia metodo grąžinimo vertę. Vykdymo metu tai bus pakeista metodo grąžinimo verte.
Vėlesnėse sąlygose dažnai reikia atskirti argumento vertę prieš tai metodo vykdymas ir vėliau, palaikomas iContract su @pre
operatorius. Jei pridedate @pre
išraiškai vėlesnėje sąlygoje, ji bus įvertinta remiantis sistemos būsena prieš atliekant metodą:
/ ** * Pridėti elementą prie kolekcijos. * * @post c.size () = [email protected] () + 1 * @post c.contains (o) * / public void append (c rinkinys, objektas o) {...}
Pirmiau pateiktame kode pirmoji postcondition nurodo, kad kolekcijos dydis turi padidėti 1, kai pridedame elementą. Išsireiškimas c @ pre
nurodo kolekciją c
prieš vykdant pridėti
metodas.
Variantai
Naudodami „iContract“ galite nurodyti invariantus klasės apibrėžimo antraštės komentare:
/ ** * „PositiveInteger“ yra sveikasis skaičius, kuris garantuotai bus teigiamas. * * @inv intValue ()> 0 * / klasė „PositiveInteger“ pratęsia sveikąjį skaičių {...}
Šiame pavyzdyje nekintamasis garantuoja, kad Teigiamas skaičius
reikšmė visada yra didesnė arba lygi nuliui. Šis teiginys patikrinamas prieš ir po bet kurio tos klasės metodo vykdymo.
Objekto suvaržymo kalba (OCL)
Nors „iContract“ tvirtinimo išraiškos yra galiojančios „Java“ išraiškos, jos yra modeliuojamos po objekto apribojimų kalbos (OCL) pogrupiu. OCL yra vienas iš standartų, kuriuos palaiko ir koordinuoja „Object Management Group“ arba OMG. (OMG rūpinasi CORBA ir su tuo susijusiais dalykais, jei praleidžiate ryšį.) OCL buvo skirtas nurodyti apribojimus objekto modeliavimo įrankiuose, kurie palaiko Unified Modeling Language (UML), dar vieną OMG saugomą standartą.
Kadangi „iContract“ išraiškų kalba yra modeliuojama pagal OCL, ji teikia keletą pažangių loginių operatorių, išskyrus pačios „Java“ logikos operatorius.
Kiekybiniai rodikliai: visi ir egzistuoja
„iContract“ palaiko visiems
ir egzistuoja
kiekybininkai. visiems
kiekybinis koeficientas nurodo, kad sąlyga turėtų atitikti kiekvieną kolekcijos elementą:
/ * * @variantasvisi IEdarbuotojai e „getEmployees“ () | * getRooms (). yra (e.getOffice ()) * /
Pirmiau nurodytas nekintamas variantas nurodo, kad kiekvienas darbuotojas grįžo „getEmployees“ ()
turi biurą kambarių, kuriuos grąžino, kolekcijoje „getRooms“ ()
. Išskyrus visiems
raktinio žodžio sintaksė yra tokia pati kaip egzistuoja
išraiška.
Čia yra pavyzdys naudojant egzistuoja
:
/ ** * @post egzistuoja „IRoom r“ „getRooms“ () | r.isAvailable () * /
Šioje sąlygoje nurodoma, kad atlikus susietą metodą, kolekcija grąžinama „getRooms“ ()
bus bent vienas laisvas kambarys. egzistuoja
tęsia rinkinio elemento „Java“ tipą - IRoom
pavyzdyje. r
yra kintamasis, nurodantis bet kurį kolekcijos elementą. į
po raktinio žodžio yra išraiška, kuri pateikia kolekciją (Surašymas
, Masyvas
arba Kolekcija
). Po šios išraiškos eina vertikali juosta, po kurios seka sąlyga, susijusi su elemento kintamuoju, r
pavyzdyje. Įdarbinti egzistuoja
kvantorius, kai bent vieno kolekcijos elemento sąlyga turi atitikti.
Tiek visiems
ir egzistuoja
galima pritaikyti įvairioms „Java“ kolekcijoms. Jie palaiko Surašymas
s, Masyvas
smėlis Kolekcija
s.
Poveikis: reiškia
„iContract“ teikia reiškia
operatorius nurodyti formos suvaržymus: „Jei A turi, tada turi atitikti ir B“. Mes sakome: „A reiškia B“. Pavyzdys:
/ ** * @invariant getRooms (). isEmpty () reiškia getEmployees (). isEmpty () // nėra kambarių, nėra darbuotojų * /
Tas nekintamasis išreiškia, kad kai „getRooms“ ()
kolekcija tuščia, „getEmployees“ ()
kolekcija taip pat turi būti tuščia. Atkreipkite dėmesį, kad jame nenurodyta, kada „getEmployees“ ()
Yra tuščias, „getRooms“ ()
taip pat turi būti tuščias.
Taip pat galite sujungti ką tik pristatytus loginius operatorius ir sudaryti sudėtingus teiginius. Pavyzdys:
/ ** * @invariantas visas IEmployee e1 „getEmployees“ () | * visi IEmployee e2 „getEmployees“ () | * (e1! = e2) reiškia e1.getOffice ()! = e2.getOffice () // vienas biuras vienam darbuotojui * /
Apribojimai, paveldėjimas ir sąsajos
„iContract“ skleidžia apribojimus paveldėjimo ir sąsajų įgyvendinimo santykiuose tarp klasių ir sąsajų.
Tarkime, klasė B
pratęsia klasę A
. Klasė A
apibrėžia invariantų, išankstinių sąlygų ir papildomų sąlygų rinkinį. Tokiu atveju klasės invariantai ir prielaidos A
kreiptis į klasę B
taip pat ir metodai klasėje B
turi tenkinti tas pačias vėlesnes sąlygas, kaip ir klasė A
tenkina. Į klasę galite įtraukti daugiau ribojančių teiginių B
.
Minėtas mechanizmas taip pat veikia sąsajose ir realizacijose. Tarkim A
ir B
yra sąsajos ir klasė C
įgyvendina abu. Tuo atveju, C
yra abiejų sąsajų invariantai, išankstinės ir vėlesnės sąlygos, A
ir B
, taip pat tiesiogiai klasėje apibrėžtus C
.
Saugokitės šalutinių poveikių!
„iContract“ pagerins jūsų programinės įrangos kokybę leis iš anksto sugauti daugybę galimų klaidų. Bet jūs taip pat galite šaudyti sau į koją (tai yra, pristatyti naujų klaidų) naudodami „iContract“. Taip gali atsitikti, kai „iContract“ tvirtinimuose naudojate funkcijas, kurios sukelia šalutinį poveikį, pakeičiantį jūsų sistemos būseną. Tai veda prie nenuspėjamo elgesio, nes, kai jūs sukompiliuosite kodą be „iContract“ instrumentų, sistema elgsis kitaip.
„Stack“ pavyzdys
Pažvelkime į išsamų pavyzdį. Aš apibrėžiau Sukrauti
sąsaja, apibrėžianti mano mėgstamos duomenų struktūros pažįstamas operacijas:
/ ** * @inv! isEmpty () reiškia top ()! = null // neleidžiami jokie nuliniai objektai * / viešosios sąsajos kaminas {/ ** * @pre o! = null * @post! isEmpty () * @post viršuje () == o * / void push (Objekto o); / ** * @pre! isEmpty () * @post @return == top () @ pre * / Object pop (); / ** * @pre! isEmpty () * / Object top (); loginis yra tuščias (); }
Mes teikiame paprastą sąsajos įgyvendinimą:
importuoti java.util. *; / ** * @inv isEmpty () reiškia elementus.size () == 0 * / public class StackImpl įgyvendina Stack {private final LinkedList elements = new LinkedList (); public void push (Object o) {elements.add (o); } public Object pop () {final Object popped = top (); elementai.removeLast (); grįžimas iššoko; } public Object top () {return elements.getLast (); } public Boolean isEmpty () {return elements.size () == 0; }}
Kaip matote, Sukrauti
diegime nėra jokių „iContract“ tvirtinimų. Atvirkščiai, visi teiginiai daromi sąsajoje, o tai reiškia, kad komponento „Stack“ sutartis sąsajoje apibrėžta visa. Tiesiog žiūrėdamas į Sukrauti
sąsaja ir jos teiginiai, Sukrauti
elgesys yra visiškai apibrėžtas.
Dabar pridedame nedidelę bandymų programą, kad pamatytume „iContract“ veikimą:
public class StackTest {public static void main (String [] args) {final Stack s = new StackImpl (); s.push („vienas“); s.pop (); s.push („du“); s.push („trys“); s.pop (); s.pop (); s.pop (); // sukelia teiginio nesėkmę}}
Tada paleisime „iContract“, kad sukurtume kamino pavyzdį:
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
Aukščiau pateiktas teiginys reikalauja šiek tiek paaiškinimo.