A Digitalmars D programozási nyelv

Helyesség

Szerződések

A specifikáció és az implementáció szisztematikus megközelítésére van szükség az objektum-orientált elemeknél és a köztük lévő kapcsolatoknál is. A DBC elmélet a szoftvert egymással kommunikáló részek összességnek tekinti, ahol az interakciók pontosan definiált kölcsönös kötelességeken alapulnak – ezek a szerződések.

A megbízható szoftver készítésének fő problémája, hogy minél pontosabban kell definiálnunk minden egyes elemnél, hogy pontosan mit is fog csinálni. Ezért a DBC elmélet szerint minden szoftver elemet ki kell egészítenünk a specifikációval (ez a szerződés). Ezek a szerződések irányítják az elemek és a világ többi része közötti interakciót.

Egy hagyományos program egy algoritmus megvalósítása. A szerződés pedig a megvalósítás specifikációja. A DBC szerint a szerződéseket a programkódba írjuk, így fordítás alatt vagy akár futási időben ellenőrizhetjük a helyességet, ami sokkal átfogóbb módszer, mintha csak a compiler szintaxis ellenőrző részére hagyatkoznánk.

A szerződések következetes használata a program specifikációját a dokumentációból (amely szükségszerűen rossz, hiányos, ellentmondásos és aktualitását vesztett) magába a programkódba helyezi át, garantálva ezzel, hogy ha a szerződések és az implementáció nem egyezik, akkor valamelyiket meg fogjuk változtatni (mert muszáj).

Assert Szerződés

A legegyszerűbb szerződés az assert. Ez már a C-ben is létezik, de ott a preprocesszor kezeli, a D nyelvben viszont beépül a nyelv szintaxisába. Az assert egy kifejezést, mint argumentumot vizsgál, amelynek igaznak kell lennie. Ha ez nem teljesül, egy AssertException-t dob.

Példa:

int i; Symbol s = null; for (i = 0; i < 100 ; i++) { s = search(array[i]); if (s != null) break; } assert (s != null); //megtaláltuk-e a keresett elemet

Elő- és utófeltételek

Az előfeltétel olyan szerződés, amely specifikálja egy utasítás előfeltételét, mielőtt végrehajtódik. Tipikus használata: egy függvény bemeneti paramétereinek helyességét ellenőrzi.

Az utófeltételek olyan szerződések, amelyek ellenőrzik az utasítás kimenetét. Tipikusan utófeltételt egy függvény kimeneténél, visszatérési értéknél, az out paramétereknél és néhány tervezett mellékhatás esetén vizsgálunk.

Az elő-, utófeltételek szintaxisa:

in { ...előfeltételek szerződése... } out (result) { ...utófeltételek szerződése... } törzs { ...kód... }

A definíció szerint ha a az előfeltétel nem teljesül, akkor a törzs rossz paramétert vesz át. Egy InException kivétel lép fel. Ha az utófeltétel nem teljesül, akkor valami zavar van a törzsben. Egy OutException lép fel.

Akár az elő- akár az utófeltételt elhagyhatjuk. Ha a függvénynek van visszatérési értéke, akkor az out részben ezt result néven vizsgálhatjuk. Például nézzük a négyzetgyök függvényt:

long square_root(long x) in { assert(x >= 0); } out (result) { assert((result * result) == x); } body { return math.sqrt(x); }

Az in és out részben lévő assert kifejezéseket hívjuk szerződéseknek. Bármely más D-beli utasítás megengedett a törzsökben, de fontos biztosítani, hogy a kódnak ne legyenek mellékhatásai, és hogy a kód végleges változata ne függjön a kód bármely hatásától. A kód végső felépítésébe az in és out kódrészek nem kerülnek bele.

Ha a függvény egy void-ot ad vissza, akkor nincs result, így az utófeltételben nem lehet result deklaráció- Ebben az esetben a következőképpen járunk el:

void func() out { ...szerződések... } //törzs { ... }

Az in és out utasításokban a result inicializálva lesz, és beállítódik a függvény visszatérési értéke.

A fordító helyességellenőrzésre áll, ha minden in és inout paraméterre hivatkoznunk az in{} részben. és minden out és inout paraméterre hivatkozunk az out{} részben.

Az in-out utasításokat használhatjuk egy függvényen belül is, például ellenőrizhetjük egy ciklus eredményét:

in { assert(j == 0); } out { assert(j == 10); } body { for (i = 0; i < 10; i++) j++; }

Osztályinvariánsok

Az osztály invariánsok azt mutatják, hogy az objektum több mint adatok és függvények egyszerű összessége, hiszen ők az osztály teljes tartalmára vonatkoznak; az egész tulajdonságait szabályozzák, nem a részekét. Ezek leírása megtalálható az Osztály Invariánsok részben.

A szerződés ellenőrzések végrehajtása lassú lehet, ezért a compiler-ben ki/be kapcsolható a végrehajtásuk.

Az invariáns ellenőrzések lefutnak a konstruktor után, a destruktor előtt, valamint minden publikus metódus előtt és után. A privát és védett metódusok előtt és után nem futnak le. Az ilyen metódusok mindig valamilyen publikus metódus futása alatt hívódnak meg, és ezalatt az invariáns sérülhet.

class Date { int day; int hour; invariant() { assert(1 <= day && day <= 31); assert(0 <= hour && hour < 24); } }

Unit tesztek

A Unit tesztekről bővebb ismertető a 9. fejezetben található.