13
1 DESIGN BY CONTRACT I ORACLE PL/SQL SAŽETAK Design by contract (DBC) je metoda čiji je autor Bertrand Meyer, tvorac OOPL jezika Eiffel, a razvio ju je 80-tih godina prošlog stoljeća. Pojednostavljeno rečeno, DBC se zasniva na ideji da svaka metoda (procedura ili funkcija), uz "standardni" programski kod, treba imati još dva dodatna dijela - pretkondiciju (precondition) i postkondiciju (postcondition). Klasa treba imati još jedan dodatni dio - invarijantu (invariant). Ugovor (contract) se zasniva na tome da metoda "zahtijeva" od svog pozivatelja (neke druge metode) da zadovolji uvjete definirane u pretkondiciji plus uvjete definirane u invarijanti, a ona (pozvana metoda) se tada "obvezuje" da će na kraju zadovoljiti uvjete definirane u postkondiciji plus uvjete definirane u invarijanti. Ideja je na neki način upravo suprotna od tzv. defanzivnog programiranja, koje zagovora da se u svim mogućim trenucima pokušava što više toga provjeriti. Eiffel u potpunosti podržava DBC. Za sada niti jedan drugi OOPL ne podržava DBC u potpunosti, barem ne na razini jezika. Međutim, moguće je primijeniti DBC i u ostalim OOPL jezicima (pretkondicije i postkondicije se mogu primijeniti i u ne-OOPL jezicima, ali se invarijante mogu realizirati samo u OOPL jezicima). U radu se prikazuje pokušaj primjene DBC metode u programskom jeziku Oracle PL/SQL. ABSTRACT Design by contract is a method whose author is Bertrand Mayer also maker of OOPL language Eiffel, developed in 1980's. Simplified, DBC is based on principle that in each routine (procedure or function) with standard code, two additional parts precondition and postcondition need to be asserted. An additional assertion in class is called invariant. Contract is based on routine's putting up an obligation to caller (to some other routine) to satisfy conditions of precondition and conditions of invariant, and hers obligation (called routine's) to satisfy conditions of postcondition and conditions of invariant. The main idea is quite the reverse of defensive programming methodology where the program code itself must try to verify as much as it can. The object oriented Eiffel programming language was created to implement Design by Contract. For now, other object-oriented languages don’t support the ideas behind DBC. However, precondition and postcondition are applicable to many programming languages, both object-oriented and otherwise but invariant are applicable only in object-oriented languages. This paper will present application of DBC methodology in Oracle PL/SQL programming language. 1. UVOD Šezdesete i prva polovina sedamdesetih godina prošlog stoljeća predstavljaju razdoblje burnog razvoja teorije (i prakse) programskih jezika. U tom razdoblju razvijani su strukturirani jezici, npr. Algol (ALGOrithmic Language), kojeg su Backus i Naur dizajnirali već 1958. godine ili njegov nasljednik Pascal (autor je N.Wirth), a već 1967. godine su K.Nygaard i O.J.Dahl razvili i prvi OOPL u povijesti - Simula 67. U tom razdoblju intenzivno su proučavane i formalne (matematičko-logičke) metode za dokazivanje korektnosti programa, čime su se naročito bavili R.W.Floyd, C.A.R.Hoare, E.W.Dijkstra (poznata je njegova rečenica "Go to statement considered harmful") i N.Wirth. Zanimljivo je spomenuti da se 1976. godine i na području naše bivše države pojavila jedna knjiga (Principi programiranja, autor S.Alagić) u kojoj su prikazane tada najnovije spoznaje na području strukturiranog programiranja i formalnih metoda za dokazivanje korektnosti programa.

DESIGN BY CONTRACT I ORACLE PL/SQL - · PDF fileili njegov nasljednik Pascal (autor je N.Wirth), a već 1967. godine su ... (Principi programiranja, autor S.Alagić) u kojoj su prikazane

Embed Size (px)

Citation preview

1

DESIGN BY CONTRACT I ORACLE PL/SQL

SAŽETAK

Design by contract (DBC) je metoda čiji je autor Bertrand Meyer, tvorac OOPL jezika Eiffel, a razvio ju je 80-tih godina prošlog stoljeća. Pojednostavljeno rečeno, DBC se zasniva na ideji da svaka metoda (procedura ili funkcija), uz "standardni" programski kod, treba imati još dva dodatna dijela - pretkondiciju (precondition) i postkondiciju (postcondition). Klasa treba imati još jedan dodatni dio - invarijantu (invariant). Ugovor (contract) se zasniva na tome da metoda "zahtijeva" od svog pozivatelja (neke druge metode) da zadovolji uvjete definirane u pretkondiciji plus uvjete definirane u invarijanti, a ona (pozvana metoda) se tada "obvezuje" da će na kraju zadovoljiti uvjete definirane u postkondiciji plus uvjete definirane u invarijanti. Ideja je na neki način upravo suprotna od tzv. defanzivnog programiranja, koje zagovora da se u svim mogućim trenucima pokušava što više toga provjeriti. Eiffel u potpunosti podržava DBC. Za sada niti jedan drugi OOPL ne podržava DBC u potpunosti, barem ne na razini jezika. Međutim, moguće je primijeniti DBC i u ostalim OOPL jezicima (pretkondicije i postkondicije se mogu primijeniti i u ne-OOPL jezicima, ali se invarijante mogu realizirati samo u OOPL jezicima). U radu se prikazuje pokušaj primjene DBC metode u programskom jeziku Oracle PL/SQL.

ABSTRACT

Design by contract is a method whose author is Bertrand Mayer also maker of OOPL language Eiffel, developed in 1980's. Simplified, DBC is based on principle that in each routine (procedure or function) with standard code, two additional parts – precondition and postcondition need to be asserted. An additional assertion in class is called invariant. Contract is based on routine's putting up an obligation to caller (to some other routine) to satisfy conditions of precondition and conditions of invariant, and hers obligation (called routine's) to satisfy conditions of postcondition and conditions of invariant. The main idea is quite the reverse of defensive programming methodology where the program code itself must try to verify as much as it can. The object oriented Eiffel programming language was created to implement Design by Contract. For now, other object-oriented languages don’t support the ideas behind DBC. However, precondition and postcondition are applicable to many programming languages, both object-oriented and otherwise but invariant are applicable only in object-oriented languages. This paper will present application of DBC methodology in Oracle PL/SQL programming language.

1. UVOD Šezdesete i prva polovina sedamdesetih godina prošlog stoljeća predstavljaju razdoblje burnog razvoja teorije (i prakse) programskih jezika. U tom razdoblju razvijani su strukturirani jezici, npr. Algol (ALGOrithmic Language), kojeg su Backus i Naur dizajnirali već 1958. godine ili njegov nasljednik Pascal (autor je N.Wirth), a već 1967. godine su K.Nygaard i O.J.Dahl razvili i prvi OOPL u povijesti - Simula 67. U tom razdoblju intenzivno su proučavane i formalne (matematičko-logičke) metode za

dokazivanje korektnosti programa, čime su se naročito bavili R.W.Floyd, C.A.R.Hoare, E.W.Dijkstra (poznata je njegova rečenica "Go to statement

considered harmful") i N.Wirth. Zanimljivo

je spomenuti da se 1976. godine i na području naše bivše države pojavila jedna knjiga (Principi programiranja, autor S.Alagić) u kojoj su prikazane tada najnovije spoznaje na području strukturiranog programiranja i formalnih metoda za dokazivanje korektnosti programa.

2

Formalne metode za dokazivanje korektnosti programa nisu uhvatile dubljeg korijena u praksi programiranja. Razlozi su, vjerojatno, i u hardveru (tada relativno slabom i skupom), i u želji za što bržim razvojem softvera ("moramo programirati - nemamo vremena za dokazivanje korektnosti programa"), ali i u složenosti tadašnjih formalnih metoda za dokazivanje korektnosti programa. Nažalost, izgleda da se od tada u svijetu komercijalnog softvera daleko veća pažnja posvećuje brzini softvera (bilo brzini rada programa, bilo brzini izlaska softvera na tržište) nego kvaliteti softvera. Poznati stručnjak na području baza podataka C.J.Date u jednoj od svojih najnovijih knjiga (Database in depth) kaže: "The emphasis in the commercial world always seems to be on performance, performance, performance … Frankly, I don't care how fast a system runs if I don't feel I can trust it to give me the right answers to my queries.". No, daljnji razvoj formalnih metoda za dokazivanje korektnosti programa i formalnih specifikacijskih jezika ipak nije stao, već je i dalje ostao živ, naročito u akademskim krugovima. Bertrand Meyer je prije dizajniranja programskog jezika Eiffel sudjelovao u razvoju formalnih specifikacijskih jezika Z i M i na bazi tih iskustava i svojih originalnih ideja razvio je metodu Design by contract (DBC) i ugradio je u svoj programski jezik Eiffel, koji nije "akademski jezik", već vrlo praktičan OOPL jezik opće namjene.

2. APSTRAKTNI TIPOVI PODATAKA (ABSTRACT DATA TYPES - ADT) Apstraktni tipovi podataka (ADT) predstavljaju teorijsku (matematičku) osnovu za DBC metodu, pa i za cijeli Eiffel jezik. Apstraktni tipovi podataka omogućavaju nam da specificiramo naš softverski sustav na precizan i kompletan (koliko je to potrebno) način, ali bez prespecifikacije (overspecification), tj. bez ulaženja u detalje softverskog rješenja. Drugim riječima, ADT omogućavaju da se precizno definira što se želi dobiti, a da se

ne mora ulaziti u detalje kako to postići (aplikativni, a ne imperativni pristup). Kod objašnjavanja ADT-ova često se kao primjer uzima stog (stack). Stog je objekt (u OOPL jezicima) koji sadrži elemente (druge objekte) i puni se i prazni na LIFO način (last-in, first-out), što znači da će onaj element koji smo zadnji stavili na stog biti prvi koji ćemo uzeti sa stoga. Neovisno od toga kako stog fizički realiziramo (npr. kao niz ili povezanu listu), stog uobičajeno ima sljedeće operacije:

- naredbu put (ili push) kojom

stavljamo neki element na vrh stoga

- naredbu remove (ili pop) kojom

mičemo element sa vrha stoga (ako nije prazan)

- upit item (ili top) kojim čitamo

element koji je na vrhu stoga (ako nije prazan)

- upit empty kojim tražimo da li je

stog prazan

- operaciju kreiranja make (ili new)

kojom kreiramo (inicijalno prazan) stog.

No, ADT ne čine samo operacije. ADT specifikacija se sastoji od četiri dijela: TIPOVI

U ovom se dijelu jednostavno navode svi tipovi koji se koriste u ADT specifikaciji. Često se koristi tzv. formalni generički parametar, kao

npr. u STACK G, što znači da stog može sadržavati elemente bilo kojeg tipa, a taj tip označavamo sa G.

3

FUNKCIJE

U ovom se dijelu navode sve operacije (ovdje nazvane funkcije) koje su primjenjuju na instancu ADT-a. Ne navode se detalji o tome kako funkcija radi, već samo kako izgleda, tj. kakva je njena signatura. Funkcija se općenito može prikazati u obliku: ime_funkcije: tip_argumenta_1 X … X

tip_argumenta_n tip_rezultata npr.

put: STACK G X G STACK G S lijeve strane strelice navode se tipovi argumenata funkcije (kao Kartezijev produkt), a s desne strane tip rezultata. U ovom slučaju to znači da funkcija put kao argumente uzima stog i (neki) element, a kao rezultat vraća opet stog. Funkcija koja ne može za vrijednost argumenta uzeti sve elemente iz skupa koji čini tip argumenta naziva se parcijalnom funkcijom i označava se prekriženom strelicom. Npr. ne možemo pročitati element iz praznog stoga, pa je funkcija item ovako definirana - argument je neprazni stog, a rezultat je (neki) element:

item: STACK G -/ G

Funkcije se mogu podijeliti na - funkcije-kreatore, koje kreiraju

instancu ADT-a; za njih je karakteristično da nemaju argumente (lijevo od strelice), već imaju samo rezultat (desno od strelice), a taj rezultat je ADT tipa, npr.

make: STACK G ili skraćeno

make: STACK G

- funkcije-upite, kod kojih se ADT

tip javlja samo sa lijeve strane strelice, npr.

item: STACK G -/ G

empty: STACK G BOOLEAN

- funkcije-naredbe, kod kojih se

ADT tip javlja i sa lijeve i sa desne strane strelice i koje zapravo mijenjaju instancu ADT-a, npr.

put: STACK G X G STACK G

remove: STACK G -/ STACK G

AKSIOMI

Aksiomima se implicitno definira na koji način se funkcije moraju ponašati, tj. kojim zahtjevima funkcije moraju udovoljiti.

Npr. za svaki x: G, s: STACK G A1: item (put (s, x)) = x

znači da kad stavimo (sa put) neki element x (tipa G), na stog s (tipa

STACK G) i nakon toga pročitamo (sa item) novodobijeni stog, dobit ćemo kao rezultat element x (drugačije rečeno, ono što smo stavili na stog bit će na vrhu stoga). Ostala tri aksioma za stog jesu:

A2: remove (put (s, x)) = s A3: empty (make) A4: not empty (put (s, x)

PRETKONDICIJE

Pretkondicije služe za definiranje domene parcijalnih funkcija. Za stog imamo sljedeće dvije pretkondicije (jer imamo dvije parcijalne funkcije):

remove (s: STACK G) require not

empty (s)

item (s: STACK G) require not

empty (s) Kompletna ADS specifikacija za stog izgleda ovako: TIPOVI

STACK G FUNKCIJE

put: STACK G X G STACK G

remove: STACK G -/ STACK G

item: STACK G -/ G

empty: STACK G BOOLEAN

make: STACK G

4

AKSIOMI

Za svaki x: G, s: STACK G A1: item (put (s, x)) = x A2: remove (put (s, x)) = s A3: empty (make) A4: not empty (put (s, x)

PRETKONDICIJE

remove (s: STACK G) require not empty (s)

item (s: STACK G) require not empty (s)

3. EIFFEL I DESIGN BY CONTRACT (DBC)

Bertrand Meyer, jedan od najvećih autoriteta na OOPL području, dizajnirao je OOPL Eiffel 1985. godine (1986. je napravljen prvi compiler, 2006. je postao ISO standardiziran). Prvo izdanje (1988.) njegove knjige OOSC, kapitalno je djelo informatičke literature (aktualno je 2. izdanje iz 1997.). Ta je knjiga upoznala javnost sa jezikom Eiffel (tada verzije 2). Od početka je podržavao višestruko nasljeđivanje, generičke klase, obradu iznimaka, i (naravno) Design by contract. U široj je javnosti daleko manje poznat nego C++ i Java, ali ga mnogi autoriteti smatraju danas najboljim OOPL jezikom. Prošle godine (2005.) donesen je ECMA (367) standard za Eiffel, a ove godine očekuje se ISO standardizacija. Design by contract (DBC) je metoda čiji je autor također Meyer. Stoga nije čudno da Eiffel u potpunosti podržava DBC. Za sada niti jedan drugi OOPL ne podržava DBC u potpunosti, barem ne na razini

jezika. DBC je opširno opisan u Meyer

1997, ali postoji još (barem) jedna dobra knjiga koja je posvećena toj temi: "Design by Contract by Example" (autori su R. Mitchell i J.McKim). Može se reći da DBC ima dosta dodirnih točaka sa metodama analize, dizajna i izrade poslovnih pravila (business rules), koje sve više dobivaju na popularnosti i za koje postoji dosta bogata literatura. I mi primjenjujemo neke od tih metoda (prikazali smo ih ukratko na CASE 14 i CASE 16).

Pojednostavljeno rečeno, DBC se zasniva na ideji da svaka metoda (procedura ili funkcija), uz "standardni" programski kod, treba imati još dva dodatna dijela - pretkondiciju (precondition) i postkondiciju (postcondition). Klasa

treba imati još jedan dodatni dio - invarijantu (invariant). Ugovor (contract)

se zasniva na tome da metoda "traži" od svog pozivatelja (neke druge metode) da zadovolji uvjete definirane u pretkondiciji (plus uvjete definirane u invarijanti), a ona (pozvana metoda) se tada "obvezuje" da će na kraju zadovoljiti uvjete definirane u postkondiciji (plus uvjete definirane u invarijanti). Ideja je na neki način upravo suprotna od tzv. defanzivnog programiranja, koje zagovora da se u svim mogućim trenucima pokušava što više toga provjeriti. Eiffel za specifikaciju DBC elemenata koristi ključne riječi require (odnosno require else u nadjačanoj metodi, kod

nasljeđivanja) za označavanje pretkondicije, ensure (odnosno ensure then u nadjačanoj metodi) za postkondicije i invariant za invarijante

klasa. Navedene naredbe su najvažnije za DBC podršku, ali Eiffel ih ima još. Naredba check služi za provjeru nekog

uvjeta u bilo kom trenutku. Za provjeru programskih petlji postoje dvije naredbe: variant provjerava da li se cjelobrojni

izraz smanjuje kod svakog prolaza u petlji, a invariant (opet ista riječ kao za

označavanje invarijante klase, ali je u kontekstu petlje značenje drugačije) provjerava da li je određeni uvjet u petlji zadovoljen u svakom prolazu. Može se postaviti pitanje utjecaja izvršenja pretkondicija, postkondicija i invarijanti na brzinu izvođenja programa. Nažalost, utjecaj postoji, a naročito je skupa provjera invarijanti. Stoga se u Eiffel-u može odrediti nekoliko stupnjeva provjere. Najslabija provjera (ali sa najmanjim negativnim utjecajem na brzinu izvođenja) je provjera samo pretkondicija (ta se provjera obično ostavlja i nakon završetka faze testiranja, tj. ostaje u produkcijskom kodu).

5

Sljedeći stupanj uključuje provjeru postkondicija, slijedi provjera invarijanti, zatim provjera petlji i na kraju provjera pomoću check naredbi. Najveći stupanj

provjere se obično koristi samo kod testiranja, jer se takvi programi izvršavaju i do 2-3 puta sporije u odnosu na programe koji nemaju nikakvih provjera. Treba naglasiti da su pretkondicije, postkondicije i invarijante korisne čak i kada nisu realizirane kao programski kod, nego samo kao komentar (najčešće zato što je nešto teško ili nemoguće izraziti – Eiffel nema operatore univerzalnog i egzistencijalnog kvantifikatora iz predikatnog računa), jer poboljšavaju dokumentiranost izvornog koda. Zbog DBC podrške, može se reći da je Eiffel i specifikacijski (a ne samo programski) jezik. Slijedi Eiffel programski kod za realizaciju stoga (OOSC2 str. 390.-391.). Zapravo, to nije kompletan programski kod, već samo tzv. kratki oblik klase, koji ne prikazuje izvršni kod metoda (zato to nije class, već class interface). Kratki oblik klase ne

prikazuje niti skrivene implementacijske detalje, tako da ne vidimo da klasa ima jedan skriveni atribut tipa niz (implement:

ARRAY G) koji služi za implementaciju stoga:

class interface STACK G

creation make

feature – inicijalizacija

-- stog od n elemenata

make (n: INTEGER) is

require

non_negative_capacity:

n >= 0

ensure

capacity_set:

capacity = n

end

feature -- pristup

-- maksimalni broj elemenata

capacity: INTEGER

-- broj elemenata stoga

el_count: INTEGER

-– element na vrhu stoga

item: G is

require

not_empty: not empty

end

feature – statusi

–- da li je stog prazan?

empty: BOOLEAN is

ensure

empty_definition:

Result = (el_count = 0)

end

–- da li je stog pun?

full: BOOLEAN is

ensure

full_definition:

Result =

(el_count = capacity)

end

feature – promjena

–- dodaj x na vrh stoga

put (x: G) is

require

not_full: not full

ensure

not_empty: not empty

added_to_top: item = x

one_more_item:

el_count =

old el_count + 1

end

-– makni element sa vrha

remove is

require

not_empty: not empty

ensure

not_full: not full

one_fewer:

el_count =

old el_count - 1

end

invariant

count_non_negative:

0 <= el_count

count_bounded:

el_count <= capacity

empty_if_no_elements:

empty = (el_count = 0)

end -– class interface STACK

6

Zanimljivo je usporediti kod sa ADT specifikacijom. Možemo vidjeti sljedeće: - pretkondicije iz ADT specifikacije

remove (s: STACK G) require not empty (s) i

item (s: STACK G) require not empty (s)

realizirane su (u programskom kodu) kao pretkondicije not_empty metoda remove i item

- aksiomi koji se odnose na funkciju-naredbu put A1: item (put (s, x)) = x i A4: not empty (put (s, x)

realizirani su kao postkondicije added_to_top i not_empty procedure put; s druge strane, vidljivo je da aksiom A2: remove (put (s, x)) = s nije nigdje realiziran u programskom kodu, zato što Eiffel podržava samo algebru sudova (proširenu sa konceptom old); no,

ipak se moglo realizirati taj aksiom pomoću odgovarajuće (programske) funkcije, ali uz opasnost uvođenja imperativnih elemenata (funkcija, procedura) u aplikativni svijet pretkondicija, postkondicija i invarijanti

- aksiomi koji se odnose isključivo na funkcije-upite (ali, takvih aksioma ovdje nema) realizirali bi se kao postkondicije pripadajućih metoda ili kao klauzule u invarijantama klase - aksiom koji se odnosi na funkciju-kreator make A3: empty (make) realiziran je kao postkondicija capacity_set procedure make. Primijećujemo da u programskom kodu postoje neke pretkondicije (npr. pretkondicija non_negative_capacity u metodi make), neke postkondicije (npr. postkondicija not_full u metodi remove) ali i neke invarijante (u konkretnom slučaju – sve tri) koje nemaju svoj "izvor" u ADT specifikaciji. One su nastale na temelju konkretne implementacije programskog rješenja, tj. nisu definirane u ADT specifikaciji. Invarijante koje su nastale na temelju implementacije (u našem slučaju sve tri invarijante) nazivaju se implementacijskim invarijantama.

4. DBC I OBRADA IZNIMAKA, DBC I NASLJEĐIVANJE Uza sve provjere, u toku izvođenja programa može doći do događaja koji zovemo iznimka (exception), koji može uzrokovati da pozvana metoda (procedura/funkcija) završi sa neuspjehom, ako iznimka nije u njoj obrađena. Ako metoda ne uspije, dolazi do iznimke u metodi koja ju je pozvala. Jedan od prvih jezika koji je imao obradu iznimaka bio je IBM-ov PL/1. Inače, u Eiffel-u iznimke nisu objekti, za razliku od programskih jezika Java i C++. Eiffel-ov model obrade iznimaka zasniva se na tome da grešku možemo obraditi na jedan od sljedeća tri načina:

- pokušati ponovno (koristeći

alternativnu strategiju): ako u Eiffel-u želimo postići da se iznimka uspješno obradi, moramo u rescue dijelu (dijelu

za obradu iznimaka) metode koristiti naredbu retry, kojom

vraćamo izvršavanje na početak metode

- "organizirana panika": vratiti

stanje klase tako da vrijede invarijante klase i onda proslijediti grešku dalje

- "lažni alarm": ponekad je

moguće nastaviti bez obzira na grešku, eventualno poduzimajući jednostavne korektivne mjere (ili samo obavještavajući korisnika); takvi su slučajevi rijetki (nažalost - jer je takve slučajeve najlakše riješiti).

Naglasimo da u Eiffel-u metoda ne može obraditi iznimku koja se desila zbog nezadovoljavanja pretkondicije – jednostavno, pozvana metoda nema što raditi ako se druga metoda (pozivatelj) ne drži ugovora! Dakle, za pojavu

iznimke u vrijeme izvršavanja programskog koda pretkondicije "krivac" je metoda-pozivatelj, dok je za pojavu iznimke u vrijeme izvršavanja programskog koda postkondicije "krivac" metoda-izvršavatelj.

7

Nasljeđivanje je jedna od tri osnovne operacije računa klasa (ostale dvije su agregacija i generičnost). Metoda iz nadklase može se u podklasi nadjačati (drugom) metodom. Eiffel omogućava i da se atribut nadjača atributom (koji mora biti ili istog tipa ili mora biti podtipa – tzv. covariance) ili da se funkcija bez parametara nadjača atributom (isto mora vrijediti covariance). Nadjačane metode ne mogu imati bilo kakve pretkondicije ili postkondicije, već nadjačana metoda mora imati jednaku ili slabiju pretkondiciju (tj. može

zahtijevati od metode koja ju je pozvala ili isto što i metoda nadklase, ili manje od toga) i mora imati jednaku ili jaču postkondiciju (tj. mora osigurati barem

ono što je osiguravala metoda nadklase, ili više od toga). Zato se za definiranje pretkondicije u nadjačanoj metodi koristi oblik require else (umjesto require), a za

definiranje postkondicije u nadjačanoj metodi koristi se ensure then. 5. DBC IZVAN EIFFEL-A Zanimljivo je da je u jednoj staroj verziji specifikacije jezika Java (1994. godine), zadnjoj koju je J.Gosling (glavni autor jezika Java) pisao sam, postojala podrška za pretkondicije, postkondicije i invarijante. Nažalost, zbog potrebe za što bržim izlaskom na tržište, Gosling je odlučio da to izbaci iz specifikacije jezika Java. Na početku Java nije imala čak niti naredbu assert (sličnu Eiffel naredbi check) dok je C++ jezik imao tu naredbu.

Java je u verziji JDK 1.4 ipak dobila naredbu assert. Assert omogućava

određenu podršku za DBC, ali (relativno) potpuna podrška za DBC u Javi za sada postoji samo na razini nezavisnih alata/tehnika. Postoje različiti pristupi za realizaciju DBC-a u Javi. Većina se starijih pristupa, koji su napravljeni prije uvođenja naredbe assert (jedan od prvih je bio

iContract alat, kojeg je 1999. napravio R.Kramer) temelji na pretprocesorskim tehnikama, tj. uvodi se "proširenje" jezika Java (najčešće kao komentar), a izvorni programi pisani u "proširenom" jeziku prvo

prolaze kroz pretprocesor, koji onda taj izvorni kod pretvori u "običan" Java izvorni kod. Slične pretprocesorske tehnike koristili su i prvi C++ "prevodioci", zapravo pretprocesori koji su C++ kod pretvarali u C kod, a dalje se koristio C prevodilac. Nakon uvođenja assert naredbe, pojavili

su se i drugačiji pristupi (npr. Kopi Java compiler, kojeg su 2002. napravili M.Lackner i A.Krall), koji su omogućili da se izvorni kod (koji uključuje podršku za DBC) direktno prevede u byte kod. U verziji 5.0 (na početku zvana 1.5) uvedene su u jezik Java anotacije (annotations), poznate i kao metapodaci (metadata). One omogućavaju uvođenje u program dodatnih informacija koje su testirane i verificirane od strane prevodioca. Te nove mogućnosti Java 5.0 jezika iskoristio je jedan od najnovijih projekata na području uvođenja DBC-a u jezik Java - Contract4J (Design by Contract for Java). Contract4J je projekt firme Aspect Research Associates i vodi se kao SourceForge.net projekt. I jezik UML (danas de-facto standard za modeliranje OO sustava) podržava DBC od 1999., zahvaljujući OCL (Object Constraint Language) formalnom jeziku (čiji su autori J.Warmer i A.Kleppe). Nešto više o tome može se naći u knjizi "Design Patterns and Contracts", koja prikazuje izvorna 23 GoF (Gang of Four) uzorka dizajna (design patterns). Za razliku od GoF knjige ("Design Patterns - Elements of Reusable Object-Oriented Software"), navedena knjiga za prikaz primjera koristi Eiffel (a ne C++), te upotpunjuje primjere sa DBC elementima.

Nažalost, Oracle PL/SQL nema za sada ništa slično naredbi assert ili anotacijama.

No, u verziji Oracle 10.2 uvedeno je u PL/SQL tzv. uvjetno prevođenje, koje omogućava da se neki dijelovi programskog koda (ne) uključe u izvršni kod. Ta je mogućnost prije svega uvedena da se omogući pisanje koda koji će biti različito preveden za različite verzije Oracle baze, ali može poslužiti i za opcionalno uvođenje "debugging" procedura u izvršni kod, pa i za simulaciju DBC-a.

8

6. ORACLE PL/SQL – OSNOVNE OO(PL) OSOBINE Oracle korporacija je 1988.godine objavila verziju 6.0 svog relacijskog DBMS-a. U sklopu baze 6.0 pojavio se i novi programski jezik - PL/SQL (Procedural Language extensions to the Structured Query Language), koji je napravljen kao proceduralna nadopuna (deklarativnog) programskog jezika SQL, kako bi se olakšalo programiranje "mission-critical" poslovnih aplikacija. Oracle je napravio PL/SQL na temelju programskog jezika ADA 83. Budući da ADA 83 nije bila objektno-orijentirani programski jezik niti PL/SQL nije bio OOPL (napomena: ADA 95 je dobila neke objektne mogućnosti). Međutim, 1997. je Oracle objavio 8.0 verziju baze i nazvao ju objektno-relacijskom. U skladu s tim nadopunjen je i programski jezik PL/SQL (naravno, i SQL). Ta verzija baze, kao niti sljedeća verzija 8i (sa Javom unutar baze), nije imala neke važne objektne mogućnosti kao što su npr. nasljeđivanje, nadjačavanje metoda, polimorfizam. Te je mogućnosti Oracle uveo 2001. godine u bazi 9i. Najnovija baza 10g nije donijela značajnih novosti na području OO osobina PL/SQL-a. PL/SQL za sada ne podržava: - privatne metode i atribute (po ugledu na privatne procedure/funkcije u tijelu PL/SQL paketa) - reference na tranzijentne objekte, u smislu da ako klasa K1 ima atribut k2 koji se odnosi na klasu K2, taj atribut može (ako to programer želi) sadržavati referencu na objekt klase K2, a ne cijeli (pod)objekt - višestruko nasljeđivanje (no, njih ne podržava niti Java, za razliku od jezika Eiffel i C++ ) - generičke klase (no, i Java ih podržava tek od najnovije verzije 5.0). PL/SQL klasa se (barem za sada) može definirati samo kao objekt baze (u određenoj shemi). PL/SQL klasa ima dva dijela - specifikaciju i tijelo. U specifikaciji se deklariraju atributi i metode, a u tijelu klase se metode (deklarirane u specifikaciji) u potpunosti definiraju. U

sljedećem primjeru prikazana je PL/SQL klasa (samo specifikacija) koja ima dva atributa i tri metode, od kojih je jedna metoda konstruktor: CREATE OR REPLACE TYPE

osoba AS OBJECT

(

ime VARCHAR2 (20),

tezina_u_kg NUMBER,

CONSTRUCTOR FUNCTION osoba (

p_ime VARCHAR2,

p_tezina_u_kg NUMBER)

RETURN SELF AS RESULT,

MEMBER PROCEDURE

prikazi_podatke,

MEMBER FUNCTION

tezina_u_funtama

RETURN NUMBER

) NOT FINAL;

Budući da prethodna klasa nije finalna (označena je sa NOT FINAL, jer je default FINAL), možemo napraviti podklasu te klase i nadjačati bilo koju njenu MEMBER metodu (jer je default kod metoda NOT FINAL). Npr. u nastavku radimo podklasu prethodne klase i u njoj nadjačavamo metodu prikazi_podatke, te uvodimo novu metodu jedi_puno_i_dobro, koja se dalje ne može nadjačati, jer je označena sa FINAL (a podklasa je označena sa NOT FINAL, kako bi se mogle raditi i njene podklase): CREATE OR REPLACE TYPE gurman

UNDER osoba

(

omiljena_hrana VARCHAR2(20),

CONSTRUCTOR FUNCTION gurman (

p_ime VARCHAR2,

p_tezina_u_kg NUMBER,

p_omiljena_hrana VARCHAR2)

RETURN SELF AS RESULT,

OVERRIDING MEMBER PROCEDURE

prikazi_podatke,

FINAL MEMBER PROCEDURE

jedi_puno_i_dobro

) NOT FINAL;

9

7. DBC I ORACLE PL/SQL Slijedi prikaz jedne jednostavne (i primitivne) realizacije DBC metode u jeziku PL/SQL. Koristit ćemo isti primjer kao kod jezika Eiffel, tj. klasu stack. Za razliku od jezika Eiffel, ovdje ćemo prvo morati na bazi kreirati tip (type) array_t koji će nam kasnije trebati za deklaraciju atributa implement u klasi stack:

CREATE OR REPLACE

TYPE array_t

AS TABLE OF INTEGER

/

Sada možemo kreirati specifikaciju i tijelo klase stack. Podsjetimo se da u PL/SQL-u nema privatnih atributa i metoda, tj. svi su atributi i metode javni, pa i implementacijski atribut implement. Osim toga, budući da PL/SQL ne podržava generičke klase, ovdje stog neće moći imati elemente bilo kojeg tipa, nego samo elemente određenog tipa - npr. INTEGER. Prvo pišemo programski kod koji ne koristi pretkondicije, postkondicije i invarijante, tj. programski kod bez DBC metode: CREATE OR REPLACE TYPE

stack AS OBJECT (

-- maksimalni broj elemenata

capacity INTEGER,

-- broj elemenata stoga

el_count INTEGER,

-- stog je realiziran kao niz

implement array_t,

-- stog od n elemenata

CONSTRUCTOR FUNCTION

stack (n INTEGER)

RETURN SELF AS RESULT,

-- element na vrhu stoga

MEMBER FUNCTION

item (SELF IN OUT stack)

RETURN INTEGER,

-- da li je stog prazan?

MEMBER FUNCTION

empty RETURN BOOLEAN,

-- da li je stog pun?

MEMBER FUNCTION

full RETURN BOOLEAN,

-- dodaj x na vrh stoga

MEMBER PROCEDURE

put (x INTEGER),

-- makni element sa vrha

MEMBER PROCEDURE remove

) NOT FINAL;

CREATE OR REPLACE TYPE BODY

stack

AS

CONSTRUCTOR FUNCTION

stack (n INTEGER)

RETURN SELF AS RESULT IS

BEGIN

capacity := n;

implement := array_t();

implement.EXTEND (n);

END;

MEMBER FUNCTION

item (SELF IN OUT stack)

RETURN INTEGER IS

BEGIN

RETURN implement(el_count);

END;

MEMBER FUNCTION

empty RETURN BOOLEAN IS

BEGIN

IF el_count = 0 THEN

RETURN TRUE;

END IF;

RETURN FALSE;

END;

MEMBER FUNCTION

full RETURN BOOLEAN IS

BEGIN

IF el_count = capacity THEN

RETURN TRUE;

END IF;

RETURN FALSE;

END;

MEMBER PROCEDURE

put (x INTEGER) IS

BEGIN

el_count := el_count + 1;

implement(el_count) := x;

END;

MEMBER PROCEDURE

remove IS

BEGIN

el_count := el_count - 1;

END;

END; -- class body STACK

Vidimo da se konstruktor metoda ne zove make, već stack, tj. isto kao klasa, jer PL/SQL (kao i C++ i Java) to obavezno tako traži (za razliku od Eiffel-a).

10

Sada ćemo napraviti pomoćni paket dbc koji će služiti za prikaz poruke o grešci i za definiranje razine provjere: CREATE OR REPLACE PACKAGE dbc

AS

c_no_check

CONSTANT INTEGER := 0;

c_check_preconditions

CONSTANT INTEGER := 1;

c_check_pre_postconditions

CONSTANT INTEGER := 2;

c_check_pre_post_invariants

CONSTANT INTEGER := 3;

FUNCTION

check_preconditions

RETURN BOOLEAN;

FUNCTION

check_pre_postconditions

RETURN BOOLEAN;

FUNCTION

check_pre_post_invariants

RETURN BOOLEAN;

PROCEDURE set_level

(p_level INTEGER);

PROCEDURE display_error

(p_error VARCHAR2);

END;

/

CREATE OR REPLACE PACKAGE BODY

dbc AS

m_razina INTEGER :=

c_no_check;

FUNCTION

check_preconditions

RETURN BOOLEAN IS

BEGIN

IF m_level >=

c_check_preconditions

THEN

RETURN TRUE;

ELSE

RETURN FALSE;

END IF;

END;

FUNCTION

check_pre_postconditions

RETURN BOOLEAN IS

BEGIN

IF m_level >=

c_check_pre_postconditions

THEN

RETURN TRUE;

ELSE

RETURN FALSE;

END IF;

END;

FUNCTION

check_pre_post_invariants

RETURN BOOLEAN IS

BEGIN

IF m_level >=

c_check_pre_post_invariants

THEN

RETURN TRUE;

ELSE

RETURN FALSE;

END IF;

END;

PROCEDURE set_level

(p_level INTEGER) IS

BEGIN

IF p_level NOT IN

(c_no_check,

c_check_preconditions,

c_check_pre_postconditions,

check_pre_post_invariants)

THEN

RAISE_APPLICATION_ERROR

(-20000,

'Wrong checking level');

END IF;

m_level := p_level;

END;

PROCEDURE display_error

(p_error VARCHAR2) IS

BEGIN

RAISE_APPLICATION_ERROR

(-20000,

'ERROR in method ' ||

p_error);

END;

END;

/

11

Dalje ćemo u klasu stack dodati tri nove funkcije, od kojih će svaka realizirati po jednu invarijantu klase. Dodat ćemo i proceduru provjeri_invarijante koja će (ako je razina provjere postavljena na check_pre_post_invariants) provjeravati da li sve te funkcije vraćaju TRUE, što znači da su sve invarijante zadovoljene (ako nisu, prikazuje se greška). Ovdje prikazujemo samo novi programski kod dodan na kraj tijela klase (naravno, treba mijenjati i specifikaciju):

CREATE OR REPLACE TYPE BODY

stack AS

...

MEMBER FUNCTION

count_non_negative

RETURN BOOLEAN IS

BEGIN

IF el_count >= 0 THEN

RETURN TRUE;

ELSE

RETURN FALSE;

END IF;

END;

MEMBER FUNCTION

count_bounded

RETURN BOOLEAN IS

BEGIN

IF el_count <= capacity

THEN

RETURN TRUE;

ELSE

RETURN FALSE;

END IF;

END;

MEMBER FUNCTION

empty_if_no_elements

RETURN BOOLEAN IS

BEGIN

IF empty AND(el_count = 0)

OR

NOT empty AND(el_count<>0)

THEN

RETURN TRUE;

ELSE

RETURN FALSE;

END IF;

END;

MEMBER PROCEDURE

check_invariants IS

BEGIN

IF NOT

dbc.check_pre_post_invariants

THEN

RETURN;

END IF;

IF NOT count_non_negative

THEN

dbc.display_error

('INVARIANT

count_non_negative');

END IF;

IF NOT count_bounded

THEN

dbc.display_error

('INVARIANT

count_bounded');

END IF;

IF NOT empty_if_no_elements

THEN

dbc.display_error

('INVARIANT

empty_if_no_elements');

END IF;

END;

END; -- class body STACK

12

Konačno ćemo u ostale metode klase dodati provjeru pret i postpostkondicija, te provjeru invarijanti (i to dva puta: na početku programskog koda metode, iza provjere pretkondicije, i na kraju programskog koda, iza provjere postkondicije) – označeno kurzivom: CREATE OR REPLACE TYPE BODY

stack AS

CONSTRUCTOR FUNCTION

stack (n INTEGER)

RETURN SELF AS RESULT IS

BEGIN

IF dbc.check_preconditions

AND n < 0

THEN

dbc.display_error

('stack - PRE');

END IF;

check_invariants;

capacity := n;

implement := array_t();

implement.EXTEND (n);

IF

dbc.check_pre_postconditions

AND capacity <> n

THEN

dbc.display_error

('stack - POST');

END IF;

check_invariants;

END;

MEMBER FUNCTION

item (SELF IN OUT stack)

RETURN INTEGER IS

BEGIN

IF dbc.check_preconditions

AND empty

THEN

dbc.display_error

('item - PRE');

END IF;

check_invariants;

RETURN implement(el_count);

END;

MEMBER FUNCTION

empty RETURN BOOLEAN IS

BEGIN

IF el_count = 0 THEN

RETURN TRUE;

ELSE

RETURN FALSE;

END IF;

END;

MEMBER FUNCTION

full RETURN BOOLEAN IS

BEGIN

IF el_count = capacity THEN

RETURN TRUE;

ELSE

RETURN FALSE;

END IF;

END;

MEMBER PROCEDURE

put (x INTEGER) IS

BEGIN

IF dbc.check_preconditions

AND full

THEN

dbc.display_error

('put - PRE');

END IF;

check_invariants;

el_count := el_count + 1;

implement(el_count) := x;

-- nema Eiffel-ov old !

IF

dbc.check_pre_postconditions

AND (empty OR item <> x)

THEN

dbc.display_error

('put - POST');

END IF;

check_invariants;

END;

MEMBER PROCEDURE

remove IS

BEGIN

IF dbc.check_preconditions

AND empty

THEN

dbc.display_error

('remove - PRE');

END IF;

check_invariants;

el_count := el_count - 1;

-- nema Eiffel-ov old !

IF

dbc.check_pre_postconditions

AND full

THEN

dbc.display_error

('remove - POST');

END IF;

check_invariants;

END;

-- invarijante

...

END; -- class body STACK

13

8. ZAKLJUČAK Iako je metoda Design by contract (DBC) stara već 20-tak godina, izgleda da je tek sada počela privlačiti nešto veću pažnju. Naime, kao i jezik Eiffel (koji ju za sada jedini u potpunosti podržava) DBC metoda je dugo bila zanemarivana od šire javnosti. Čini se da je baš razvoj programskog jezika Java, koji je na neki način konkurent Eiffel-u, utjecao na širenje ideje o DBC metodi, jer se u Javi razvilo dosta alata za podršku DBC metode. Naravno, najveću zaslugu za širenje ideje DBC-a ima njen autor, Bertrand Meyer, koji već 20 godina uporno pokušava uvjeriti stručnu i akademsku javnost (Meyer je redovni profesor na odjelu za kompjuterske znanosti Švicarskog federalnog instituta za tehnologiju - ETH Zurich) da treba povećati kvalitetu programskog koda. Naime, bez povećanja kvalitete koda nema višestruke iskoristivosti koda (tko želi koristiti tuđi kod ako sumnja u njegovu ispravnost?), a bez višestruke iskoristivosti koda produktivnost u razvoju softvera ne može se značajno povećati.

Dugo se mislilo da će sama primjena OOPL jezika donijeti višestruku iskoristivost koda. Međutim pokazalo se da je programski kod niske kvalitete moguće napisati u bilo kom programskom jeziku ili jezičnoj paradigmi. DBC metoda može značajno doprinijeti povećanju kvalitete programskog koda čak i u onim slučajevima kad ju primijenimo samo kao metodu za analizu i za dokumentiranje sofverskih komponenti (kao komentar u izvornom kodu). Naravno, bilo bi puno bolje kada bi dizajneri programskih jezika uveli direktnu podršku za DBC u svoje jezike. Međutim, ne moramo čekati dizajnere jezika - u bilo kojem programskom jeziku moguće je (pa makar i na relativno primitivan način) napraviti barem pretkondicije i postkondicije kao prave softverske elemente. U OOPL jezicima (tj. jezicima koji imaju klase) mogu se realizirati i invarijante klase.

Literatura:

Standardni Oracle priručnici za baze 9i i 10g, a naročito: 1. Application Developer's Guide – Object-Relational Features 2. PL/SQL User's Guide and Reference Nezavisna literatura: 1. Suad Alagić: Principi programiranja, Svjetlost, 1976. 2. C.J.Date: Database in depth, O'Reilly, 2005. 3. Steven Feuerstein: Oracle PL/SQL Programming, O'Reilly, 2005. 4. Jean-Marc Jezequel i drugi: Design Patterns and Contracts, Addison-Wesley, 2000. 5. Thomas Kyte: Expert Oracle Database Architecture, Apress, 2005. 6. Craig Larman: Applying UML and Patterns, Prentice Hall, 2002. 7. Bertrand Meyer: Object-Oriented Software Construction, Prentice Hall, 1997.

Podaci o autoru:

Zlatko Sirotić Istra informatički inženjering d.o.o., Pula e-mail: [email protected]