(Бұл эссе On Lisp кітабының кіріспесінен алынған.)
Бағдарламаның функционалды элементтері тым үлкен болмауы керек деген қағида — бағдарламалау стиліндегі ежелден келе жатқан ұстаным. Егер бағдарламаның қандай да бір құрамдас бөлігі оңай түсінуге болатын шектен асып кетсе, ол үлкен қала қашқындарды қалай жасырса, қателерді де солай оңай жасыратын күрделілік үйіндісіне айналады. Мұндай бағдарламалық жасақтаманы оқу, тестілеу және қателерін түзету (debug) қиынға соғады.
Осы ұстанымға сәйкес, үлкен бағдарламаны бөлшектерге бөлу керек, ал бағдарлама неғұрлым үлкен болса, соғұрлым көбірек бөлінуі тиіс. Бағдарламаны қалай бөледі? Дәстүрлі тәсіл «жоғарыдан төменге қарай жобалау» (top-down design) деп аталады: сіз «бұл бағдарламаның мақсаты — мына жеті нәрсені орындау, сондықтан мен оны жеті негізгі ішкі бағдарламаға (subroutine) бөлемін. Бірінші ішкі бағдарлама мына төрт нәрсені орындауы керек, сондықтан оның өзі төрт ішкі бағдарламадан тұрады» және тағы сол сияқты айтасыз. Бұл процесс бүкіл бағдарлама қажетті деңгейдегі бөлшектенуге жеткенше жалғасады — әрбір бөлік маңызды бірдеңе жасауға жетерліктей үлкен, бірақ дербес бір бүтін ретінде түсінуге болатындай шағын болады.
Тәжірибелі Lisp бағдарламашылары өз бағдарламаларын басқаша бөледі. Олар жоғарыдан төменге қарай жобалаумен қатар, «төменнен жоғарыға қарай жобалау» (bottom-up design) деп атауға болатын қағиданы ұстанады — яғни тілді есептің ыңғайына қарай өзгертеді. Lisp-те сіз бағдарламаны жай ғана тілдің деңгейіне қарай төмендеп жазбайсыз, сонымен қатар тілдің өзін бағдарламаңыздың деңгейіне дейін көтересіз. Бағдарлама жазып отырып: «Шіркін, Lisp-те пәлендей оператор болса ғой», — деп ойлауыңыз мүмкін. Сөйтесіз де, оны жазып шығасыз. Кейіннен жаңа операторды қолдану бағдарламаның басқа бір бөлігінің дизайнын жеңілдететінін түсінесіз және осылай жалғаса береді. Тіл мен бағдарлама қатар дамиды. Екі соғысушы мемлекеттің шекарасы сияқты, тіл мен бағдарлама арасындағы шек қайта-қайта сызылып, ақырында есептің табиғи шекаралары — таулар мен өзендердің бойымен тұрақталады. Соңында бағдарламаңыз тіл дәл сол үшін арнайы жасалғандай болып көрінеді. Ал тіл мен бағдарлама бір-біріне мінсіз үйлескен кезде, нәтижесінде түсінікті, шағын әрі тиімді код аласыз.
Төменнен жоғарыға қарай жобалау — бұл бір бағдарламаны жай ғана басқа ретпен жазу емес екенін баса айтқан жөн. Төменнен жоғарыға қарай жұмыс істегенде, әдетте мүлдем басқа бағдарлама шығады. Біртұтас монолитті бағдарламаның орнына сіз абстрактілі операторлары көп кеңейтілген тілді және сол тілде жазылған әлдеқайда шағын бағдарламаны аласыз. Тіреуіш арқалықтың (lintel) орнына арка аласыз.
Әдеттегі кодта тек есеп жүргізу (bookkeeping) болып табылатын бөліктерді абстракциялап тастасаңыз, қалған бөлігі әлдеқайда қысқа болып шығады; тілді неғұрлым жоғары деңгейге көтерсеңіз, жоғарыдан төмен қарай оған дейін жүріп өту керек арақашықтық соғұрлым азаяды. Бұл бірнеше артықшылық береді:
Жұмыстың көбін тілге жүктеу арқылы төменнен жоғарыға қарай жобалау шағынырақ және бейімделгіш бағдарламалар тудырады. Қысқа бағдарламаны аса көп құрамдас бөліктерге бөлудің қажеті жоқ, ал құрамдас бөліктердің аз болуы бағдарламаларды оқуды немесе өзгертуді жеңілдетеді. Құрамдас бөліктердің аздығы олардың арасындағы байланыстардың да аз болуын білдіреді, яғни ондағы қателердің ықтималдығы да азаяды. Өндірістік дизайнерлер машинадағы қозғалмалы бөлшектердің санын азайтуға ұмтылатыны сияқты, тәжірибелі Lisp бағдарламашылары да бағдарламаларының көлемі мен күрделілігін азайту үшін төменнен жоғарыға қарай жобалауды қолданады.
Төменнен жоғарыға қарай жобалау кодты қайта пайдалануға жол ашады. Екі немесе одан да көп бағдарлама жазған кезде, бірінші бағдарлама үшін жазған көптеген утилиталарыңыз кейінгілерінде де пайдалы болады. Утилиталардың үлкен қорын жинақтағаннан кейін, жаңа бағдарламаны жазу таза Lisp-пен басынан бастағандағыға қарағанда әлдеқайда аз күш-жігерді талап етеді.
Төменнен жоғарыға қарай жобалау бағдарламаларды оқуды жеңілдетеді. Абстракцияның бұл түрі оқырманнан әмбебап операторды түсінуді талап етеді; ал функционалды абстракция мысалы оқырманнан арнайы мақсаттағы ішкі бағдарламаны түсінуді талап етеді. [1]
Бұл тәсіл сізді кодтағы заңдылықтар мен үлгілерді (patterns) үнемі іздеуге мәжбүрлейтіндіктен, төменнен жоғарыға қарай жұмыс істеу бағдарлама дизайны туралы идеяларыңызды айқындауға көмектеседі. Егер бағдарламаның бір-бірінен алыс екі құрамдас бөлігі құрылымы жағынан ұқсас болса, сіз бұл ұқсастықты байқап, бағдарламаны оңайырақ етіп қайта жобалауыңыз мүмкін.
Төменнен жоғарыға қарай жобалау белгілі бір деңгейде Lisp-тен басқа тілдерде де мүмкін. Қай жерде кітапханалық функцияларды көрсеңіз, сол жерде төменнен жоғарыға қарай жобалау орын алып жатыр. Алайда, бұл салада Lisp әлдеқайда кең мүмкіндіктер береді, және тілді кеңейту Lisp стилінде салыстырмалы түрде үлкен рөл атқарады — соншалықты, Lisp жай ғана басқа тіл емес, бағдарламалаудың мүлдем бөлек тәсілі болып табылады.
Әрине, бұл әзірлеу стилі шағын топтар жаза алатын бағдарламаларға көбірек сәйкес келетіні рас. Дегенмен, сонымен бірге, ол шағын топ жасай алатын нәрселердің шегін кеңейтеді. The Mythical Man-Month кітабында Фредерик Брукс бағдарламашылар тобының өнімділігі оның көлеміне қарай сызықты түрде өспейтінін алға тартты. Топтың көлемі ұлғайған сайын, жекелеген бағдарламашылардың өнімділігі төмендейді. Lisp бағдарламалау тәжірибесі бұл заңды неғұрлым оптимистік түрде тұжырымдауға болатынын көрсетеді: топ көлемі кішірейген сайын, жеке бағдарламашылардың өнімділігі артады. Шағын топ, салыстырмалы түрде айтқанда, жай ғана шағын болғаны үшін ұтады. Ал егер шағын топ Lisp ұсынатын тәсілдерді де пайдаланса, ол айқын басымдықпен жеңіске жете алады.
Жаңа: On Lisp кітабын тегін жүктеп алыңыз.
[1] «Бірақ сіздің барлық жаңа утилиталарыңызды түсінбейінше, бағдарламаны ешкім оқи алмайды». Мұндай пікірлердің неліктен көбіне қате болатынын білу үшін 4.8 бөлімін қараңыз.