náhled|vpravo|360px|Schéma návrhu podle kontraktu Návrh podle kontraktu (), nebo též kontraktové programování je relativně dlouho známým, avšak stále nedoceněným přístupem k tvorbě spolehlivého programového vybavení.

Předepisuje, aby návrháři softwaru definovali formální, přesné a verifikovatelné specifikace rozhraní pro softwarové komponenty, které rozšiřují běžnou definici abstraktních datových typů o předběžné podmínky, následné podmínky a invarianty. Tyto specifikace se označují jako „smlouvy“ („kontrakty“), v souladu s pojmovou metaforou s podmínkami a závazky obchodních kontraktů.

DbC přístup předpokládá, že všechny klientské komponenty, které volají operaci na serverové komponentě splní předběžné podmínky specifikované jako požadovaný pro tuto operaci.

Tam, kde je tento předpoklad považován za příliš riskantní (jako v případě vícekanálových nebo distribuovaných výpočtů), používá se inverzní přístup, což znamená, že serverová komponenta testuje, zda všechny relevantní předběžné podmínky platí (před zpracováním požadavku klientské komponenty nebo v jeho průběhu), a pokud tomu tak není, odpoví vhodným chybovým hlášením.

Historie Tento termín poprvé použil Bertrand Meyer v souvislosti se svým návrhem programovacího jazyka Eiffel a poprvé jej popsal od roku 1986 v různých článcíchMeyer, 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. a ve dvou po sobě následujících vydáních (v letech 1988 a 1997) své knihy Object-Oriented Software Construction. Společnost Eiffel Software požádala o registraci obchodní známky Design by Contract v prosinci 2003, a známku vlastní od prosince 2004.

Návrh podle kontraktu má kořeny v práci na formální verifikaci, formální specifikaci a Hoareho logice. Mezi původní příspěvky patří například: Jasná metafora pro vedení procesu návrhu Aplikace na dědičnost, zejména formalismus pro redefinici a dynamickou vazbu Aplikace na zpracovávání výjimek Spojení s automatickou softwarovou dokumentací

Popis Ústřední myšlenkou DbC je metafora o tom, jak prvky softwarového systému vzájemně spolupracují na základě vzájemných závazků a výhod. Tato metafora pochází z obchodního života, kde se „klient“ a „dodavatel“ dohodnou na „kontraktu“, která definuje např. toto:

Dodavatel musí poskytnut určitý produkt (závazek) a má právo očekávat, že klient zaplatí jeho odměnu (výhodu). Klient musí zaplatit poplatky (závazek) a je oprávněn získat produkt (výhodu). Obě strany musí splnit určité závazky, např. zákony a předpisy, které se vztahují na všechny kontrakty.

Podobně, pokud metoda třídy v objektově orientovaném programování poskytuje určitou funkčnost, může:

Očekávat, že určitá podmínka bude při vstupu zaručena každým klientským modulem, který ji volá: předběžná podmínka metody je závazek klienta, a výhoda pro dodavatele (samotnou metodu), protože ho zbavuje nutnosti řešit případy, kdy předběžná podmínka není splněna. Zaručení určité vlastnosti při výstupu: následná podmínka metody je závazek pro dodavatele, a výhoda (hlavní výhoda volání metody) pro klienta. Zachovat určitou vlastnost, předpokládanou při vstupu a zaručenou při výstupu: invariant třídy.

kontrakt je sémanticky ekvivalentní s Hoareho trojicí, která formalizuje závazky. Lze ji shrnout do „tří otázek“, na které musí designer v kontraktu opakovaně odpovídat: Co kontrakt očekává? Co kontrakt zaručuje? Co kontrakt zachovává?

Mnoho programovacích jazyků má nástroje pro vytváření takovýchto tvrzení (asercí). DbC však považuje tyto kontrakty za natolik zásadní pro korektnost softwaru, že by měly být součástí procesu návrhu. DbC v podstatě prosazuje, aby se nejprve napsala tvrzení. Kontrakty mohou být zapsány pomocí komentářů v kódu, vynuceny sadou testů nebo obojím, a to i v případě, že by pro kontrakty neexistovala žádná speciální jazyková podpora.

Pojem kontraktu se rozšiřuje až na úroveň metod a procedur; kontrakt pro každou metodu bude obvykle obsahovat následující informace: Přijatelné a nepřijatelné vstupní hodnoty nebo typy, a jejich významy Návratové hodnoty nebo typy, a jejich významy Hodnoty nebo typy chyb a výjimek, které se mohou objevit, a jejich významy Vedlejší účinky Předběžné podmínky Následné podmínky Invarianty Záruky výkonu (vzácněji), například na spotřebovaný čas nebo prostor

Podtřídy v hierarchii dědičnosti mohou oslabovat předběžné podmínky (ale ne je posilovat) a posilovat následné podmínky a invarianty (ale ne je oslabovat). Tato pravidla se blíží behaviorálnímu podtypování.

Všechny vztahy tříd jsou mezi klientskými a dodavatelskými třídami. Klientská třída se zavazuje volat dodavatelské funkce, pokud výsledný stav dodavatele není porušen klientským voláním. Dodavatel je následně povinen poskytnout návratový stav a data, která neporušují stavové požadavky klienta.

Například vyrovnávací paměť pro data dodavatele může vyžadovat, aby v ní byla data přítomna v okamžiku volání funkce mazání. Následně dodavatel klientovi zaručí, že když funkce mazání skončí svou práci, datová položka bude skutečně smazána z vyrovnávací paměti. Dalšími návrhovými kontrakty jsou koncepty invariantu třídy. Invariant třídy zaručuje (pro lokální třídu), že stav třídy bude v rámci specifikovaných tolerancí zachován na konci každého provedení funkce.

Při používání kontraktů by se dodavatel neměl pokoušet ověřit, zda jsou podmínky kontraktu splněny — tato praxe je známa jako ofenzivní programování — obecnou myšlenkou je, že kód musí „selhat natvrdo“, přičemž ověření kontraktu je záchrannou sítí.

Vlastnost „tvrdého selhání“ zjednodušuje ladění chování kontraktu, protože zamýšlené chování každé metody je jasně specifikováno.

Tento přístup se podstatně liší od defenzivního programování, kde dodavatel je odpovědný za vyhodnocení, co dělat, když předběžná podmínka není splněna. Nejčastěji dodavatel vyhodí výjimku, aby klienta informoval, že předběžná podmínka nebyla splněna, a v obou případech — u DbC i defenzivního programování — musí klient zjistit jak na to reagovat. V takových případech DbC dodavateli usnadňuje práci.

Návrh podle kontraktu také definuje kritéria korektnosti softwarového modulu: Pokud je invariant třídy a předběžná podmínka pravdivá předtím, než je dodavatel zavolán klientem, pak invariant a následná podmínka bude splněno po dokončení služby. Při volání dodavatele by softwarový modul neměl porušovat předběžné podmínky dodavatele.

Návrh podle kontraktu může také usnadňovat opětovné použití kódu, protože kontrakt pro každou část kódu je plně dokumentovaný. Kontrakty modulu lze považovat za určitou formu dokumentace chování modulu.

Důsledky pro výkon Během provádění bezchybného programu nesmí být podmínky kontraktu nikdy porušené. Kontrakty se proto při vývoji softwaru obvykle kontrolují pouze v režimu ladění. V ostré verzi softwaru jsou kontroly smluv vypnuty, aby se maximalizoval výkon.

V mnoha programovacích jazycích jsou kontrakty implementovány pomocí asercí. Aserce jsou implicitně vypuštěny při vytváření produkčního softwaru v jazycích C/C++, a podobně jsou deaktivovány v jazycích C# a Java.

Spuštění interpretu Pythonu s parametrem „-O“ (jako „optimalizovat“) také způsobí, že generátor kódu nebude emitovat žádný bytecode pro aserce.Official Python Docs, assert statement

Kompilátor Eiffelu umožňuje detailní nastavení ne/zahrnutí asercí do výsledného programu jednak podle typu aserce (předběžné podmínky, následné podmínky, invarianty, varianty), jednak podle části programu, což umožňuje diferencovaně vyvážit požadavky výkonu a bezpečnosti.

Vztah k testování softwaru Návrh podle kontraktu nenahrazuje obvyklé strategie testování, např. unit testing, integrační testování a systémové testování. Spíše doplňuje externí testování interními autotesty, které lze aktivovat jak pro izolované testy, tak pro produkční kód během fáze testování.

Výhodou interních autotestů je, že mohou odhalit chyby dříve, než se projeví jako neplatné výsledky pozorované klientem. To umožňuje dřívější a podrobnější detekci a odstraňování chyb.

Použití asercí lze považovat za formu testovacího orákula, způsob testování implementace návrhu podle kontraktu.

Pokročilejší podpora návrhu podle kontraktu zahrnuje nástroje, které na základě kontraktu automaticky generují masu unit testů, ověřujících, že pro všechny platné vstupy komponenta vrací platné výstupy bez porušení invariantů.

Podpora v programovacích jazycích

Jazyky s nativní podporou K jazykům, které implementují většinu DbC vlastností nativně patří: Ada 2012 Ciao Clojure Cobra jazyk D Dafny Eiffel Fortress Kotlin Mercury Oxygene (dříve Chrome a Delphi Prism) Racket (včetně kontraktů vyššího řádu, a zdůrazňuje, že porušení kontraktu musí identifikovat viníka a musí obsahovat přesné vysvětleníFindler, Felleisen Contracts for Higher-Order Functions) Sather ScalaSilné typování jako další "contract enforcing" pro jazyk Scala, viz diskuze na scala-lang.org/. SPARK (pomocí statické analýzy programů v Adě) Vala Vienna Development Method (VDM)

Kromě toho má standardní kombinace metod v Common Lisp Object System kvalifikátory metod :before, :after a :around, které mimo jiné umožňují zápis kontraktů jako pomocných metod.

Jazyky s podporou třetích stran

Pro existující programovací jazyky bez nativní podpory návrhu podle kontraktu byly vyvinuty různé knihovny, preprocesory a jiné nástroje: Ada, používá GNAT pragmata pro předběžné a následné podmínky. C DBC pro C Preprocesor GNU Nana eCv nástroje pro formální verifikaci C++: Boost.Contract eCv++ nástroje pro formální verifikaci Digital Mars C++ překladač pomocí CTESK rozšíření jazyka C Knihovna Loki poskytuje mechanismus pojmenovaný ContractChecker, které ověřuje, zda třída splňuje návrh podle kontraktu. DBC C++ Návrh podle kontraktu pro C++ C# (a jiné .NET jazyky), pomocí Code Contracts (a Microsoft Research projekt integrovaný do .NET 4.0) Groovy pomocí GContracts Go pomocí dbc nebo gocontracts Java: aktivní: OVal s AspectJ Contracts for Java (Cofoja) Java Modeling Language (JML) Bean Validation (pouze předběžné a následné podmínky) valid4j SafeR (s bezpečnými referencemi) Neaktivní/neznámé: Jtest (aktivní, ale zdá se, že DbC již není podporováno) iContract2/JContracts Contract4J jContractor C4J Google CodePro Analytix SpringContracts pro Spring Framework Jass Modern Jass (následník je Cofoja) p. 2 JavaDbC pomocí AspectJ JavaTESK s využitím rozšíření Javy chex4j pomocí javassist vysoce přizpůsobitelné java-on-contracts JavaScript, pomocí decorator-contracts, AspectJS (konkrétně AJS_Validator), Cerny.js, ecmaDebug, jsContract, dbc-code-contracts nebo jscategory. Common Lisp pomocí maker nebo metaobject protokolu CLOS. Nemerle pomocí maker. Nim pomocí maker. Perl pomocí CPAN modulů Class::Contract (autor Damian Conway) nebo Carp::Datum (autor Raphael Manfredi). PHP pomocí PhpDeal, Praspel nebo Stuart Herbert's ContractLib. Python pomocí balíčků jako deal, icontract, PyContracts, dpcontracts nebo zope.interface. Trvalá úprava Pythonu pro podporu Návrhu podle kontraktu byla navržena v PEP-316 v roce 2003, ale byla odložena. Ruby pomocí DesignByContract Briana McCallistera, Ruby DBC ruby-contract nebo contracts.ruby. Rust pomocí crate contracts. Swift pomocí cocoapod Jima Boyda. Tcl pomocí objektově orientovaného rozšíření XOTcl.

Odkazy Reference

Literatura Mitchell, Richard, a McKim, Jim: Design by Contract: by example, Addison-Wesley, 2002 popisující DBC způsobem blízkým původnímu modelu.