ПредишенСледващото

Развитието на модерен софтуер е придружен от поредица от действия, насочени към подобряване на неговото качество. Един такъв метод е да се проектира на договора.

Както и всяка друга, този метод не е панацея за грешки и кодиране на дизайна, но до голяма степен ще бъде официално двете от тези процеси. Формализирането на отношенията обхваща самите разработчици, тъй като този метод се отнася към групата на самостоятелно документиране: спецификация описва директно в програмата, но не и в придружаващите документи.

"Дизайн с договор" - превод на «дизайн от договора * английски термин. Друг термин, който се нуждаем, - «твърдение» - одобрение.

Историята на механизма започва преди половин век. През 1950 г. на конференция в Кеймбридж, Алън Тюринг предложи да бъдат включени в изискванията на програмата, които трябва да бъдат проверени по време на неговото изпълнение, и от които можем да черпят коректността на програмата. Идеята на тези изисквания след това смята Floyd, Хоаре, Дейкстра и други (някой може да припомнят известния трио Хоаре описано в повече от една книга, посветена на развитието на правилни програми).

За да зададете тези изисквания, са разработени официална спецификация езици описание, като VDM и Z, както и обектно-ориентираното версия. Въпреки това, по отношение на програмните езици, те на практика не предоставят подкрепа за описване на изискванията на нивото на езика. Най-ранните езици за програмиране като подкрепа станаха Алгол W. NL поддържат описание, което е предшественик на това, което имаше за цел да Айфеловата, но Clu тези описания са неизправни. Самата концепция за "дизайн с договор" за първи път е въведен от Майер през 1987 г. и потвърдено в Айфел. От тогава той привлича все повече и повече внимание от други езици за програмиране на разработчиците.

Дори и в началото на спецификациите Java включва вградена поддръжка за основните механизми на изпълнение на "договор дизайн", но поради кратките срокове, ограничава освобождаването на първата версия, те са били по-късно падна (като, наистина, и редица други обектно-ориентирани механизми).

3. Предпазва дали "отбранителна програмиране"

"Отбранителна програмиране" е известно от дълго време: Програмата трябва да е написан така, че да е "умен" прояви, както се очаква, а не на очакваните входните данни, това е, за да не се "падне" в никакъв вход. В действителност, ако не е завършен продукт, но библиотека клас, който може да се използва в повече от един проект, и по-различно, този подход не само не допринася за развитието, но, напротив, значително възпрепятства това.

Помислете за един прост пример. Да предположим, че искате да напишете функция за изчисляване на корен квадратен: SQRT (х: REAL): РЕАЛ

-- Връща квадратния корен от "х"

Възниква въпросът дали тази функция се справя негативните аргументи? Следвайки правилото "безопасен", е необходимо да се осигури такова лечение. За съжаление, не е ясно кой от тях. Можете да се върнете към негативните аргументите на себе си, или да се изчисли за тях в основата на противоположните стойностите на аргумента, или да издаде съобщение за грешка (с варианта: конзолата, в диалоговия прозорец), или повишаване на изключения, или. Сега си представим, че е необходимо да се оцени експресията arcsin (SQRT (В (х)) / у)

Представете си, че различни функции са написани от различни хора с помощта на "отбранителна програмиране". За да се отговори на въпроса, какъв е броят на всички възможни варианти на поведение на функцията, когато невалидни аргументи за читателя.

4. Предпоставки и postconditions

-- Връща квадратния корен от "х" - "х" трябва да е неотрицателно

-- Връща квадратния корен от "х" изисква non_negative_x: х GT = 0

Има «изискват» означава предпоставки раздел линия след него - едно от предварителните условия за «non_negative_x» изчислима израз етикет и "х GT = 0 ". Ако няколко предпоставки за нормалното изпълнение на програмата, всички те трябва да бъдат верни. В противен случай тя ще бъде развълнуван изключение.

Сега провери неотрицателност на аргумента се стартира автоматично всеки път, когато ти се обадя SQRT функция, както и нарушение на това условие, ще бъдат докладвани незабавно. Нашата функция е съвсем проста, и това е очевидно предпоставка. В по-сложни случаи, основания за съмнения: той винаги е възможно да се намери подходящ предпоставка? За съжаление, отговорът е отрицателен. Например:

• присвояване предпоставки функция еквивалент изчисляване на функцията, за която се прилага (например, за да се намери стойността, която е равна на предварително определена факторен);

• задача е доста трудно да се напълно формална спецификация;

• формално условие изисква въвеждането на голям брой допълнителни функции, които нямат значение извън посочените предпоставки, и м. П.

-- Връща квадратния корен от "х" изисква non_negative_x: - "х" трябва да бъде - неотрицателно

-- Връща квадратния корен от "х" - Резултати неотрицателно изисква non_negative_x: х GT = 0, но след официално стил, може да бъде презаписано, както следва: SQRT (х: REAL): REAL

-- Връща квадратния корен от "х" изисква non_negative_x: х GT = 0 гарантира non_negative_result: Резултати GT = 0

Сега SQRT функция не само налага ограничения върху аргумента, но и осигурява изпълнението на ограничения по отношение на резултата. С други думи, за изпълнението на тази функция гарантира, че резултатът е неотрицателна за неотрицателна аргумент. Въпреки това, за да функционира с този имот не е само функция на корен квадратен. Например, една функция, която винаги се връща 5, също така е подходящ. Това означава, че след условие, а именно така наречените състояние, разположен в секция и осигуряват оценява достатъчно, след като тялото на функция, а не строго. Трябва да се добави, че резултатът трябва да е равен на квадрата на аргумента. Една опция е показана по-долу: SQRT Cx: REAL): РЕАЛ

-- Връща квадратния корен от "х" изисква non_negative_x: х GT = 0 гарантира non_negative_resuIt: Резултати GT = 0 дефиниция: Резултати л 2 - х

В действителност, ние попита предпоставка в областта на функцията, а postcondition - гамата си от ценности и връзката между аргумент и от резултата. В общия случай е предпоставка определя кога методът може да бъде обикновено се извършват и -Това на postcondition е гарантирана след неговото изпълнение.

Сега е ред на изпълнението на: SQRT (х: REAL): REAL е

-- Връща квадратния корен от "х" изисква non_negative_x: х GT = 0 направи, ако х GT = 0, тогава

-- квадратен корен друго

-- Грешка край работа

Нещо не е наред тук: защото ние просто се опитваме да се избегне всякакъв обработка на грешки. Принципът на "irredundancy", казва: не може да се провери състоянието на тялото на рутината, посочен в това условие. Това е толкова вярно. Какво се случва, ако предварително условие е нарушено? На този въпрос можем да отговорим в следващия раздел.

Първо, нека се върнем към идеята за "дизайн, но в договора." Думата "договор", на първо място се посочва ясно, че това е формализиране на отношенията между страните от някои. В този случай, от едната страна се нарича "доставчика" (доставчик), а другият - на "клиента" (клиента). Доставчикът се задължава да предостави част от функционалността за клиента. Но само когато всички предпоставки. От своя страна, това гарантира, че изпълнението ще се извършва, след като всички пощенски условия. По този начин се прави ясно разделение на права и отговорности.

Това опростява самата програма, като рутинна postcondition може да бъде предпоставка за това след като в В (SQRT (х)).

Осигуряване на условия х GT = 0, влиза проверка В аргумент вече не е необходимо. По дефиниция, SQRT трябва да се върне не-отрицателен резултат.

Друго предимство на договора е, че тя не трябва да се търси виновника в случай на нарушение на преди или след условия. Ако е нарушил предпоставка, а след това, че клиентът не е осигурил нейното изпълнение и грешки - в код на клиента. Ако postcondition е нарушено, а след това, че доставчикът не е изпълнил задълженията си и кода за грешка -в доставчик. Във всеки случай в такива нарушения е развълнуван изключение. Все пак, има някои тънкости.

Ако предварително условие е нарушено, а след това изключение е повдигнат по тялото на клиента, защото доставчикът не може да повлияе на аргументите преминали към него, както и на общото състояние на системата, преди да го чете. Ако postcondition е нарушено, а след това изключение е вдигната в тялото на доставчика, защото той може да има допълнителна информация за това как да се справят с тази ситуация и дори се възстанови нормалното състояние и да се опита все пак да завърши работата си, като се използва, например, различен алгоритъм.

Въпреки това, ние не трябва да мислим, че изявлението (към тях и да включва преди и след условия) може да се използва като средство за обработка на специални случаи. Следния кодов фрагмент може да се счита за желателно, но неуловим цел: read_user_input е

-- Прочетете вход толкова дълго - толкова дълго, тъй като те не са въведени правилно

-- четене гарантира valid_input данни - postcondition: данни - са верни спасяване по - манипулатор повторен опит изключение - повторение изпълнение на тялото - по текущата края процедура

Изглежда, че всичко трябва да работи според очакванията: данните се прочитат, провери postcondition ако не е удовлетворен, а след това изключение е вдигната, манипулатор изключение започва изпълнението на тялото на процедура на първо място. Изход от тази косвена линия се появява само, когато данните са въведени правилно. Но същия ефект може да се постигне с помощта на условни конструкции и инструкции веригата. Следователно, резултатите не могат да се разглеждат като средство за организиране на потока от контрол в програмата. Нарушаването на всякакви твърдения - сигнализира наличието на грешки в програмата. По-конкретно, нарушаването на предварителното условие - да сигнализира за грешка в клиент, postcondition нарушение - сигнал за грешка в доставчика.

Имайте предвид, че предварителните условия и следприватизационен условия важат за интерфейса, но не на изпълнението. В действителност, това е необходимо, че ползвателят на клиент знаеше как да използвате определен клас имоти и какво да очакваме от него. Това е необходимо, за да не по-малко от информацията за името на имущество, типа й, броят на аргументи и техните видове, словесно описание на собствеността. За тази цел, на езика на Айфел въвежда понятието клас от кратката форма, която пропуска всички данни за изпълнението и нетъргуемия собственост на класа. Описанието на всички други функции са включени и предпоставки, както и публикувайте условия. Кратката форма на класа се генерира с помощта на специални инструменти от източник и служи като документация за други разработчици.

6. Твърденията за обекти свойства

Често се налага да работят с обекти през целия им жизнен цикъл, да запазят някои от свойствата на едни и същи. Вземете двойно свързан списък (за простота показано атрибути само класа): клас функция BILINKED_LIST - Достъп до съседни елементи следващия: BILINKED_LIST - на следващия елемент предишна: BILINKED_LIST - предишна т край - клас BILINKED_LIST

За всеки две възли х и у, така че х. Следващата = Y, е също изпълнени y.prev = х. И това свойство се запазва през целия период на списъка двойно свързан. , Има съответната декларация, наречена "класа инвариант", за да изразят този имот. Той описва условията, които винаги трябва да са истина. След добавянето на секцията инвариант клас текст ще бъде, както следва: клас функция BIL.INKED_L.IST - Достъп до съседните елементи следващия: BILINKED_LIST - следващия елемент предишна: BILINKED_LIST - предишния елемент инвариант consistent_next: Следващата / = Void предполага next.prev = Текущ consistent_prev: предишна / = Void предполага prev.next = Текущ край - клас BILINKED_LIST

Тук в момента се отнася за текущия обект (това синоним на Java и C ++).

Тъй като неизменна клас винаги трябва да е вярно, и се проверява за достъп до доставчика на собственост, както и след това. В обобщение, следната схема (тези, които са запознати с Хоаре утроява, тя ще изглежда много подобно на тях):

За да имате тялото, преди да извършвате рутинните трябва да бъде изпълнена клас инвариант и предпоставка на тази рутина. След извършване на рутинна тяло трябва да бъде направен от рутината и неизменна, на postcondition клас.

Така че, след като тялото клас инвариант рутинни е била изпълнена, тя трябва да бъде изпълнена преди неговото изпълнение. Това означава, че неизменна, трябва да се извърши и преди тялото на предишната практика, а оттам и с предишния във връзка с предишния и така нататък. Г. Къде е тази цел верига? За да отговорим на този въпрос, ние ще обсъдим въпроса от другата страна. Какво рутинно се извършва на първо място? Разбира се, процесът на създаване на обект (конструктора).

Точно преди обект на разговор все още не е установен, както и за всеки клас инвариант може да става въпрос. В действителност, процедурата за създаване на малко по-различна схема коректност:

DEFAULT тук означава състояние на обекта и веднага след нулиране, но преди изпълнението на процедурата за създаване на (например, в Айфел в това състояние, всички на обекта зададени атрибути на нула). От изложеното по-горе може да бъде едно прекрасно заключение за ролята на процедурата по създаване на обект: той осигурява клас неизменна. Всички други свойства на обекта, са в по-добра позиция: те само трябва да се поддържа това състояние.

До момента имаме счита примери за договори за работа без да се отчита йерархичната структура на класа наследство. С тази структура нещата стават малко интересни. Ако се разгледат първо неизменна клас. Да предположим, че имаме нужда, вместо обикновен списък подредени. Възможно изпълнение е показан по-долу: клас SORTED_LIST inehrit

BILINKED_LIST - Списък е двойно свързан съпоставими - Списък на елементите се сортират инвариант prev_is_less:

-- Предишна елемент е не по-голяма от сегашната предишна / = Празнотата предполага предишна LT = Current next_is_greater:

-- След елемент е не по-малко от сегашната следващата / = Празнотата предполага следващия GT = Current край - клас S0RTED_LIST

Очевидно е, че клиентът клас BILINKED_LIST на и съчетания, които са наследени от BILINKED_LIST, имат право да очакват, че добавянето на едно дете да BILINKED_LIST няма да ги засегне (те могат дори да не знае за такова потомство). Ето защо, неизменна, клас всъщност е обединението на "и" директни инварианти и инварианти на своите предци. В резултат на това, потомък не може да се счупи договори прародител.

Въз основа на същия принцип се основават преди и след условия. Съгласно друг пример. Да предположим, че искате да се приложи един клас да сортирате масива. Най-простото сортиране, които могат само да сортират масиви с различни елемента, може да изглежда по следния начин: клас SIMPLE_S0RTER [G -gt; Съпоставими] функция - вид Сортиране (данни: ARRAY [G]) е

-- данни Филтър масив не - Възходящо изискват non_void_data: данни / = отпада - масив съществува different_elements: all_different (данни) - всички елементи на масива са различни гарантира sorted_data: is_sorted (данни) - на изхода на масива се сортира край - клас SIMPLE_S0RTER

Клиентът може да използва променлива от тип SIPLE_S0RTER сортировач, за да сортирате данни: sorter.sort (my_data)

Защото на клиента "знае" динамична променлива тип сортировач, тя може да задоволи само тези предпоставки, които са обявени в SIMPLE_SORTER клас. Ето защо, всяка промяна в предварителните условия за договор може само "опростяване", да го направим по-нежно. По този начин, когато предпоставка императивни свойства клас са комбинирани чрез операция "ИЛИ". За да се подчертае това, се добавя една дума да се изисква думата друго. От друга страна, клиентът може да се очаква, че след условие ще бъде изпълнено, независимо от динамичното тип сортировач. Следователно пощенски условия, както и инварианти, трябва да се обединят на "I". В този случай, се уверете, добави след след това. Например, един клас, който вече ви позволява да сортирате масиви с повтарящи се елементи, ако те не са равни на Void, и гарантира, че нещо ще бъде стабилна, може да изглежда така: клас ROBUST.SORTER [G -gt; Съпоставими] наследи

SIMPLE_SORTER предефиниране вид - вид подтиснатия процедура - в този край клас функция за сортиране (данни: ARRAY [G]) е

-- Подрежда масив данни Възходящ-PO изисква друго non_void_data: данни / = Void - масив съществува non_void_elements: не разполага (данни, Void) - няма празни елементи гарантират тогава stable_data: is_stable (стар клонинг (данни), данни) - - сортиране стабилен край - клас R0BUST_S0RTER

Пълен предпоставка процедура вид в клас R0BUST_S0RTER е както следва:

(Data / = Void и Теа all_different (данни)) или друго (данни / = Void и след това не е (данни, Void))

Но му пост-условие (стар дизайн прави в зависимост от стойността на стоящи след израза изчислява преди процедурата): is_sorted (данни) и след това is_stable (стар клон (на данни), данни)

Друг аспект на използването на декларации е цената, която трябва да плати за тях по време на изпълнение. В крайна сметка, размерът се увеличава програма и пада на скорост на изпълнение, както и най-вероятно много съществено. Отговорът е прост: с пускането на програмата е съставена по такъв начин, че нито одобрение не е включена в модула за обект. Твърденията са от значение само по време на отстраняване на грешки, и като средство за самостоятелно документация. Тяхното нарушение е грешка на сигнала в програмата. Когато за отстраняване на грешки е пълна, не твърдения за нарушения не се случват, а от твърденията не са (или по-скоро, не трябва да имат) странични ефекти, а след тяхното изключване от програмата не влияе на нейното действие.

Свързани статии

Подкрепете проекта - споделете линка, благодаря!