KNOW INTUIT, Лекция, Принципи на дизайна на класа

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

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

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

Оригиналните дизайни се тестват първо върху болиди от Формула 1, преди те да бъдатстават собственост на автомобилната индустрия. След като се оказаха полезни при разработването на успешна библиотека от компоненти за многократна употреба, разглежданите идеи също ще осигурят предимство на всяка OO-FS, независимо дали е била предназначена за многократна употреба от самото начало.

Странични ефекти във функциите

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

Нашият отговор е не. Но защо? Обосновката изисква разбиране на ролята на страничните ефекти, осъзнаване на разликата между „добър“ и „лош“ страничен ефект. Нека разгледаме този въпрос в светлината на познанията ни за класовете - техния произход от ADT, концепцията за абстрактна функция и ролята на инварианта на класа.

Команди и заявки

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

Дефиницията на заявката не казва дали обектите могат да се променят по време на заявката. За командите отговорът е очевиден - да, защото това е предназначението им. За заявките въпросът има смисъл само ако са изпълнени от функции, тъй като достъпът до атрибута не променя нищо. Модифицирането на обекти, извършвано от функция, се нарича неинстраничен ефект (s >функция със страничен ефект в допълнение към основната роля -връщането на отговор на заявка, промяна на обекта, играе в същото време допълнителна роля, която често всъщност е основната. Но трябва ли да се толерират страничните ефекти?

Форми на странични ефекти

Нека да определим кои конструкции могат да доведат до странични ефекти. Операциите, които модифицират обекти, са: присвояване a := b , опит за присвояване a = b , create statement create a . Ако целта a е атрибут, тогава изпълнението на операцията ще присвои нова стойност на нейното поле на обекта, съответстващ на целта на текущото извикване на подпрограма.

Ние се интересуваме само от присвоявания, където a е атрибут; ако a е локален обект, тогава неговата стойност се използва само по време на изпълнение на подпрограмата и няма постоянен ефект; ако a е Резултат, присвояването оценява резултата от функцията, но не действа върху обекти.

Обърнете внимание, че докато прилагахме принципите на скриване на информация, ние внимателно избягвахме всякакви индиректни форми на модификация на обекти при проектирането на OO нотацията. По-специално, синтаксисът изключва присвояването на формата obj. attr := b , чиято цел е да се постигне чрез извикване на obj .set_attr (b) , където процедурата set_attr (x. ) извършва присвояване на атрибута attr := x (вижте Лекция 7 от курса „Основи на обектно-ориентираното програмиране“).

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

Определение: специфичен страничен ефект

Функция предизвиква специфичен страничен ефект, ако тялото й съдържа:

  • присвояване, опит за присвояване или инструкция за създаване, чиято цел е атрибут;
  • извикване на процедура.

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

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

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

Референтна прозрачност

Защо се интересуваме от страничните ефекти на функциите? В крайна сметка в природата на софтуера е да променя нещата в процеса на изпълнение.

Ако позволим на функциите да променят обекти като команди, губим много от техните прости математически свойства. Както беше отбелязано в дискусията за ADT (вижте Лекция 6 от курса по Основи на обектно-ориентираното програмиране), математиците знаят, че техните операции върху обекти не променят обекти (Computing 2 1/2 не променя числото 2). Тази неизменност е основната разлика между света на математиката и света на компютрите.

Някои програмни подходи се стремят към тази неизменност - Lisp в неговата така наречена "чиста" форма, функционални езици за програмиране като FP езика на Backus, други приложни езици. Но в практическото разработване на софтуер промените в обектите са в основата на изчисленията.

Неизменността на обектите има важна практическа последица, известна катореферентна прозрачности дефинирана по следния начин:

Определение: референтна прозрачност

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

Ако x е 3, можем да използваме x вместо 3 и обратно, във всеки референтно прозрачен израз. (Само академиците на Лапута в „Пътешествията на Гъливер“ на Суифт пренебрегнаха референтната прозрачност – те винаги носеха неща със себе си, представяйки ги при всяко споменаване.) Референтната прозрачност се нарича още „замяна на равни с равни“.

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

Стойността на sneaky при извикване винаги е 0; но 0 и подъл не са взаимозаменяеми, например:

няма да отпечата нищо, но ще отпечата "Нещо странно!" при замяна на 0 с подъл.

Поддръжката за референтна прозрачност в изразите е важна, защото ви позволява да правите заключения от текста на програмата. Един от централните проблеми на софтуерния дизайн е ясно формулиран от E. Dijkstra ([Dijkstra 1968]). Състои се в сложността на динамичното поведение (милиони различни изчисления дори за прости програми), генерирано от статичния текст на програмата. Следователно е изключително важно да се запази валидираната форма на извод, предоставена от математиката. Загубата на референтна прозрачност означава и загуба на основни свойства, които са толкова вкоренени в нашето съзнание и практика, че ние дори не го осъзнаваме. Например n + n не е еквивалентно на 2* n, ако n е дадено от функция като sneaky:

Ако attr се инициализира на нула, тогава 2* n ще върне 2, докато n + n ще върне 3.

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

Същото правило може да бъде неофициално изразено по следния начин: "задаването на въпрос не променя отговора".