Szerződésalapú programozás

A Wikipédiából, a szabad enciklopédiából
Ugrás a navigációhoz Ugrás a kereséshez
Szerződésalapú programozás

A szerződésalapú tervezés, más néven szerződésalapú programozás (angolul: design by contract, DbC) egy szoftvertervezési módszer.

A szerződésalapú programozás előírja, hogy a szoftvertervezőknek meg kell határozniuk a szoftverkomponensek specifikációit, amelyek kiegészítik az absztrakt adattípusok meghatározását előfeltétellel, utófeltétellel és invariánssal. Ezen specifikációkat szerződéseknek nevezik, az üzleti szerződések feltételeivel és kötelezettségeivel való hasonlóságuk miatt.

A szerződésalapú megközelítés feltételezi, hogy minden olyan kliensösszetevő, amely egy szerverösszetevőn végrehajt egy műveletet, teljesíti az adott művelethez előírt feltételeket.

Ahol ez a feltételezés túl kockázatosnak tűnik (mint a többcsatornás vagy elosztott programozás esetében), ott fordított megközelítést alkalmaznak, vagyis a szerverösszetevő teszteli minden releváns előfeltétel fennállását (a kliensösszetevő kérésének feldolgozása előtt vagy közben), és megfelelő hibaüzenettel válaszol, ahol mégsem teljesül az előfeltétel.

Történet[szerkesztés]

A kifejezést Bertrand Meyer alkotta meg az Eiffel programozási nyelv tervezésekor, és először tudományos cikkekben írta le 1986-ban,[1][2][3] majd az Object-Oriented Software Construction című könyvének két egymást követő kiadásában (1988, 1997). Az Eiffel Software 2003 decemberében kérte a Design by Contract védjegybejegyzését, amit 2004 decemberében kapott meg.[4][5] A védjegy jelenlegi[mikor?] tulajdonosa az Eiffel Software.[6][7]

A szerződéses tervezés a formális ellenőrzés, formális specifikáció és a Hoare-logika munkájában gyökerezik. Az eredeti kivitelezések tartalmazzák:

  • Világos metafora a tervezési folyamat irányításához
  • Alkalmazás az öröklődésre, így különösen az újradefiniálás és a dinamikus kötés formalizálása
  • Alkalmazás kivételkezeléshez
  • Kapcsolat az automatikus szoftverdokumentációval

Leírás[szerkesztés]

A szerződésalapú tervezés központi gondolata egy metafora arról, hogy a szoftverrendszer elemei miként működnek együtt a kölcsönös kötelezettségek és előnyök alapján. A metafora az üzleti életből származik, ahol az ügyfél és a szolgáltató szerződést kötnek, amely például meghatározza, hogy:

  • A szolgáltatónak biztosítania kell egy bizonyos terméket (kötelezettség), és jogosult arra számítani, hogy az ügyfél megfizette a díját (előny).
  • Az ügyfélnek meg kell fizetnie a díjat (kötelezettség), és jogosult a termék megszerzésére (előny).
  • Mindkét félnek teljesítenie kell az összes szerződésre vonatkozó bizonyos kötelezettségeket, például törvényeket és rendeleteket.

Hasonlóképpen, ha az objektumorientált programozásban egy osztály metódusa biztosít egy bizonyos funkciót, akkor:

  • Elvár egy bizonyos feltételt, melyet minden őt hívó modulnak biztosítania kell: kötelezettség az ügyfélnek és előny a szolgáltatónak (maga a metódus), ami lehetővé teszi, hogy ne kelljen az előfeltételen kívüli esetet kezelni.
  • Garantálja az állapotot kilépéskor: az eljárás utófeltétele a szolgáltató kötelezettsége, és nyilvánvalóan haszon (a fő előny az eljárás hívása) az ügyfél számára.
  • Megtart egy bizonyos tulajdonságot, amely belépéskor feltételezett és kilépéskor garantált: osztályinvariáns.

A szerződés szemantikailag megegyezik egy Hoare-hármassal, amely formalizálja a kötelezettségeket. Ezt össze lehet foglalni a „három kérdéssel”, amelyeket a tervezőnek többször meg kell válaszolnia a szerződésben:

  • Mit vár a szerződés?
  • Mit garantál a szerződés?
  • Mit tart fenn a szerződés?

Számos programozási nyelv lehetővé teszi ilyen állítások megfogalmazását. A DbC azonban úgy ítéli meg, hogy ezek a szerződések annyira döntő fontosságúak a szoftver helyességéhez, hogy azoknak a tervezési folyamat részét kell képezniük. Valójában a DbC először az állítások felírását javasolja. A szerződések leírhatók megjegyzésekként a kódban, érvényesíthetők tesztekkel (akár mindkettő is alkalmazható), akkor is, ha a használt programozási nyelvben nem támogatott a szerződésalapú programozás nyelvi szinten.

A szerződés fogalma a metódus/eljárás szintjére terjed ki; az egyes alprogramokra vonatkozó szerződés általában a következő információkat tartalmazza:

  • Az elfogadható és elfogadhatatlan bemeneti értékek vagy típusok, és azok jelentése
  • Visszatérési értékek vagy típusok és jelentéseik
  • A hiba- és kivételértékek vagy -típusok, amelyek előfordulhatnak, és azok jelentése
  • Mellékhatások
  • Előfeltételek
  • Utófeltételek
  • Invariánsok
  • (ritkábban) Teljesítménygaranciák, pl. a futási időre vagy helyigényre

Az öröklési hierarchia alosztályai gyengíthetik (de nem erősíthetik) az előfeltételeket, és erősíthetik (de nem gyengíthetik) az utófeltételeket és invariánsokat. Ezek a szabályok hasonlítanak a viselkedésbeli altípusokhoz.

Minden osztálykapcsolat a kliensosztályok és a szolgáltató osztályok között zajlik. A kliens osztály köteles úgy hívni a szolgáltató szolgáltatásait, hogy a hívás eredménye nem sérti a szolgáltató állapotát. Ezt követően a szolgáltató köteles olyan visszatérési állapotot és adatokat szolgáltatni, amelyek nem sértik az ügyfél állapotkövetelményeit.

Például egy szolgáltató adatpuffer megkövetelheti, hogy az adatok legyenek jelen a pufferben a törlési funkció meghívásakor. Ezt követően a szállító garantálja az ügyfél számára, hogy amikor a törlési funkció befejezi a munkáját, az adatelem valóban törlődik a pufferből. Más tervezési szerződések az osztályinvariáns fogalmai. Az osztályvariáns garantálja (a helyi osztály számára), hogy az osztály állapota az egyes funkciók végrehajtása végén a meghatározott tűréshatáron belül megmarad.

A szerződések használatakor a szállítónak nem szabad ellenőrizni, hogy a szerződéses feltételek teljesülnek-e (támadó/offenzív programozás). Az az általános elképzelés, hogy a kód „keményen kudarcot vall”, a szerződés ellenőrzése pedig biztonsági háló.

A DbC „keményen kudarcot valló” tulajdonsága leegyszerűsíti a szerződéses viselkedés hibakeresését, mivel az egyes módszerek tervezett viselkedése egyértelműen meghatározásra kerül.

Ez a megközelítés lényegesen különbözik a védekező programozástól, ahol a szolgáltató felelős annak meghatározásában, hogy mit tegyünk, ha az előfeltétel nem teljesül. Gyakran előfordul, hogy a szolgáltató kivételt dob, hogy tájékoztassa a klienst az előfeltétel megszegéséről, és végső soron mindkét esetben – DbC és védekező programozás esetén is – a kliensnek kell kitalálnia, hogyan reagáljon erre. Ilyen esetekben a DbC megkönnyíti a szolgáltató munkáját.

A szerződésalapú tervezés meghatározza a szoftvermodul helyességének kritériumait is:

  • Ha az osztályvariáns és az előfeltétel teljesül, mielőtt egy ügyfél meghívja a szolgáltatót, akkor az invariáns és az utófeltétel a szolgáltatás teljesítése után igaz lesz.
  • A szolgáltató hívásakor a szoftvermodul nem sértheti a szolgáltató előfeltételeit.

A szerződésalapú tervezés megkönnyítheti a kód újrafelhasználását, mivel az egyes kóddarabokra vonatkozó szerződések teljes mértékben dokumentálva vannak. A modulra vonatkozó szerződések a modulok viselkedését leíró szoftver dokumentációjának tekinthetők.

Teljesítménnyel kapcsolatos következmények[szerkesztés]

A hibamentes program végrehajtása során soha nem szabad megsérteni a szerződés feltételeit. Ezért a szerződéseket rendszerint csak hibakeresési módban ellenőrzik a szoftverfejlesztés során. Később, a kiadáskor a szerződés ellenőrzése le van tiltva a teljesítmény maximalizálása érdekében.

Számos programozási nyelven a szerződéseket állításokkal adják meg. Ezeket a C/C++ alapértelmezés szerint kiadási módban eldobja; hasonlóképpen deaktiválják a C#[8] és a Java nyelven.

A Python értelmező indításakor az -O („optimalizálás”) kapcsolót megadva a Python kódgenerátor sem bocsát ki semmilyen bájtkódot az állításokhoz.[9]

Ez hatékonyan kiküszöböli az állítások futási idejű költségeit a kiadott kódban – függetlenül a fejlesztési módban használt erőforrások számától és számítási költségeitől –, mivel ezeket az utasításokat a fordító egyszerűen kihagyja a kiadott kódból.

Kapcsolat a szoftver tesztelésével[szerkesztés]

A szerződésalapú tervezés nem helyettesíti a rendszeres tesztelési stratégiákat, például az egységteszteket, integrációs teszteket és rendszerteszteket. Inkább a külső tesztelést kiegészíti belső öntesztekkel, amelyek aktiválhatók mind az izolált tesztekhez, mind a végleges (kiadandó) kódban egy tesztfázis alatt.

A belső öntesztek előnye, hogy felismerik a hibákat, még mielőtt az ügyfél által visszaadott hibás eredményekként jelennének meg, így a hibákat korábban és pontosabban meg lehet találni.

Az állítások felhasználása teszt oracle egyik formájának, a szerződésalapú tervezés megvalósításának egy módjának tekinthető.

Nyelvi támogatás[szerkesztés]

Azok a nyelvek, amelyek a legtöbb DbC-funkciót natív módon valósítják meg[szerkesztés]

Nyelvek harmadik fél támogatásával[szerkesztés]

Különböző könyvtárakat, előfeldolgozókat és egyéb eszközöket fejlesztettek ki a meglévő, a szerződésalapú programozást natívan nem támogató programozási nyelvekhez:

  • Ada, a GNAT előfeltételeinek és utófeltételeinek pragmáin keresztül.
  • C és C ++, a Boost.Contracttel,[15] a DBC for C előfeldolgozó, a GNU Nana, az eCv és az eCv ++ formális ellenőrző eszközökkel, vagy a Digital Mars C++ fordító, a C nyelv CTESK kiterjesztése segítségével. A Loki programkönyvtár egy ContractChecker nevű mechanizmust biztosít, amely ellenőrzi, hogy az osztály teljesíti-e szerződésalpú tervezést.
  • C# (és egyéb .NET-nyelvek) Code Contracts[16] segítségével (a Microsoft Research projektje, integrálva .NET Framework 4.0-ba)
  • Groovy a GContracts segítségével
  • Go a dbc-vel[17]
  • Java:
    • Aktív:
    • Inaktív/ismeretlen:
      • Jtest (aktív, de úgy tűnik, hogy a DbC-t már nem támogatják)[19]
      • iContract2/JContracts
      • Contract4J
      • jContractor
      • C4J
      • Google CodePro Analytix
      • SpringContracts a Spring keretrendszerhez
      • Jass
      • Modern Jass (utódja Cofoja)[20][21]
      • JavaDbC az AspectJ használatával
      • JavaTESK a Java kiterjesztésével
      • chex4j javassist használatával
      • nagymértékben testreszabható java-on-contracts
  • JavaScript, az AspectJS (konkrétan AJS_Validator), Cerny.js, ecmaDebug, jsContract, dbc-code-contracts vagy jscategory útján.
  • Common Lisp, a makró eszközön vagy a CLOS metaobjektum-protokollon keresztül.
  • Nemerle, makrók segítségével.
  • Nim, makrók útján.
  • Perl, a Class::Contract (Damian Conway) és Carp::Datum (Raphael Manfredi) CPAN-modulokon keresztül.
  • PHP, a PhpDeal, Praspel vagy Stuart Herbert ContractLibje útján.
  • Python, olyan csomagokkal, mint az icontract, PyContracts, Decontractors, dpcontracts, zope.interface, PyDBC vagy Contracts for Python. A PEP-316-ban a szerződésalapú tervezés állandó támogatását javasolták a Pythonban, de a javaslat állapota 2020 júniusában „elhalasztva”.[22]
  • A Ruby, Brian McCallister DesignByContractje, a Ruby DBC ruby-contract vagy a contracts.ruby útján.
  • Rust a contracts könyvtáron keresztül
  • Tcl, az XOTcl objektumorientált kiterjesztésen keresztül.

Jegyzetek[szerkesztés]

  1. Meyer, Bertrand: Design by Contract, Technical Report TR-EI-12/CO, Interactive Software Engineering Inc., 1986
  2. Meyer, Bertrand: Design by Contract, in Advances in Object-Oriented Software Engineering, eds. D. Mandrioli and B. Meyer, Prentice Hall, 1991, pp. 1–50
  3. Meyer, Bertrand: Applying "Design by Contract", in Computer (IEEE), 25, 10, October 1992, pp. 40–51, also available online
  4. United States Patent and Trademark Office registration for "DESIGN BY CONTRACT"
  5. United States Patent and Trademark Office registration for the graphic design with words "Design by Contract"
  6. Trademark Status & Document Retrieval. tarr.uspto.gov
  7. Trademark Status & Document Retrieval. tarr.uspto.gov
  8. Assertions in Managed Code. msdn.microsoft.com
  9. Official Python Docs, assert statement
  10. Bright: D Programming Language, Contract Programming. Digital Mars, 2014. november 1. (Hozzáférés: 2014. november 10.)
  11. Hodges, Nick: Write Cleaner, Higher Quality Code with Class Contracts in Delphi Prism. Embarcadero Technologies. (Hozzáférés: 2016. január 20.)
  12. Findler, Felleisen Contracts for Higher-Order Functions
  13. Scala Standard Library Docs - Assertions. EPFL. (Hozzáférés: 2019. május 24.)
  14. Strong typing as another "contract enforcing" in Scala, see discussion at scala-lang.org/.
  15. https://www.boost.org/doc/libs/master/libs/contract/doc/html/index.html
  16. Code Contracts. msdn.microsoft.com
  17. https://github.com/drblez/dbc
  18. Bean Validation specification. beanvalidation.org
  19. https://www.parasoft.com/wp-content/uploads/pdf/JtestDataSheet.pdf
  20. Archived copy. [2016. március 28-i dátummal az eredetiből archiválva]. (Hozzáférés: 2016. március 25.) p. 2
  21. No chance of releasing under Apache/Eclipse/MIT/BSD license? · Issue #5 · nhatminhle/cofoja. GitHub
  22. Terence Way: PEP 316 – Programming by Contract for Python. Python.org, 2003. május 2. (Hozzáférés: 2020. június 18.)

Fordítás[szerkesztés]

Ez a szócikk részben vagy egészben a Design by contract című angol Wikipédia-szócikk ezen változatának fordításán alapul. Az eredeti cikk szerkesztőit annak laptörténete sorolja fel. Ez a jelzés csupán a megfogalmazás eredetét jelzi, nem szolgál a cikkben szereplő információk forrásmegjelöléseként.

Kapcsolódó szócikk[szerkesztés]

További információk[szerkesztés]