Szerződésalapú programozás

A Wikipédiából, a szabad enciklopédiából
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 Archiválva 2003. április 3-i dátummal a Wayback Machine-ben
      • 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". [2016. december 21-i dátummal az eredetiből archiválva]. (Hozzáférés: 2020. május 27.)
  5. United States Patent and Trademark Office registration for the graphic design with words "Design by Contract". [2016. december 21-i dátummal az eredetiből archiválva]. (Hozzáférés: 2020. május 27.)
  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. [2021. április 26-i dátummal az eredetiből archiválva]. (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 és a szerzői jogokat 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]