alt=|bélyegkép|Szerződésalapú programozás A szerződésalapú tervezés, más néven szerződésalapú programozás (, 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 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,Meyer, Bertrand: Design by Contract, Technical Report TR-EI-12/CO, Interactive Software Engineering Inc., 1986Meyer, Bertrand: Design by Contract, in Advances in Object-Oriented Software Engineering, eds. D. Mandrioli and B. Meyer, Prentice Hall, 1991, pp. 1–50Meyer, Bertrand: Applying "Design by Contract", in Computer (IEEE), 25, 10, October 1992, pp. 40–51, also available online 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. A védjegy jelenlegi tulajdonosa az Eiffel Software.
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 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 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# é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.Official Python Docs, assert statement
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 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 Azok a nyelvek, amelyek a legtöbb DbC-funkciót natív módon valósítják meg Ada 2012 Ciao Clojure Cobra D Eiffel Fortress Kotlin Mercury Nice Oxygene (korábban Chrome és Delphi Prism)) Racket (beleértve a magasabb rendű szerződéseket, és hangsúlyozva, hogy a szerződésszegéseknek a vétkest kell hibáztatniuk, és pontos magyarázattal kell szolgálniukFindler, Felleisen Contracts for Higher-Order Functions) Sather ScalaStrong typing as another "contract enforcing" in Scala, see discussion at scala-lang.org/. SPARK (az Ada-programok statikus elemzésén keresztül) Vala VDM
Nyelvek harmadik fél támogatásával 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,https://www.boost.org/doc/libs/master/libs/contract/doc/html/index.html 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 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-velhttps://github.com/drblez/dbc Java: Aktív: OVal és AspectJ Contracts for Java (Cofoja) Java Modeling Language (JML) Bean Validation (csak elő- és utófeltételek) valid4j Inaktív/ismeretlen: Jtest (aktív, de úgy tűnik, hogy a DbC-t már nem támogatják)https://www.parasoft.com/wp-content/uploads/pdf/JtestDataSheet.pdf iContract2/JContracts Contract4J jContractor C4J Google CodePro Analytix SpringContracts a Spring keretrendszerhez Jass Modern Jass (utódja Cofoja) p. 2 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”. A Ruby, Brian McCallister DesignByContractje, a Ruby DBC ruby-contract vagy a contracts.ruby útján.