Ах, абстракцията. Излезте от дома, влезте в първия програмистки бар и кажете на някой „абстракция“. Това може да се развие само по два начина. Или ще се абстрахира от вас и ще продължи да си пие latte-то или (по-лошо) ще събере момчетата, ще ви издърпат навън и итеративно ще ви разработят час при зъболекаря. Ако го пробвате, задължително ми пишете. Не забравяйте да пратите адреса.
Но все пак имаше някаква истина в горното. Ако не сте заклет OOA/D архитект (вероятно не, защото иначе ще сте твърде заети с проектиране за да ми четете блога), то думата „абстракция“ вероятно предизвиква тръпки по гръбнака ви. В училище ме учеха, че това се нарича пежоративно, но ми се подиграват, като ползвам тази дума. Поради което ще кажа, че има отрицателна конотация. Също толкова криптично, но с по-голям шанс да сте го чували.
И тъй. Това което е по-малко очевидно, е че има две доста близки до абстракцията концепции – енкапсулация и индиректност. Но често чувам хора да употребяват едното, като имат предвид другото. Затова ще ви разкажа набързо картинката в моята глава.
На думи
Абстракция е механизъм, с който си свеждаме една сложна концепция до няколко прости интрумента да боравим с нея. Вземете за пример думата врата. Какво представлява една врата? Може да я отваряте, може да я затваряте, а може и да блъскате по нея, пияни, и да се надявате да ви пуснат вкъщи. Ако има ключалка, то може да я отключвате и заключвате. И това, горе-долу, описва вратите по света. Нали?
Не съвсем. Погледнете така – вратата е някаква материална конструкция. Може да е с панти, може да е плъзгаща се. Може да е дървена, може да е метална, може да е от няколко материала. Може да се отваря навън или навътре (или настрани). А на още по-ниско ниво представлява ужасно сложна конструкция от атоми.
Но думата врата ни позволява да забравим за всичко това и да си мислим с няколко прости операции и състояния (отворена, заключена, отключване и т.н.). Този процес се нарича абстракция. Сходен пример е ключалка – някаква комплексна система (механична или електронна), но ние можем да се абстрахираме от това и опростим до отключена и заключена (или разбита).
Обърнете внимание, че повечето врати не ви изолират от детайлите си. Може да огледате пантите, виждате какъв е материала, може да намерите дръжката и т.н. Не е съвсем същото с ключалката – механизъма е добре скрит и ако не сте опитен ключар (или крадец), си нямате идея какво точно ще стане като завъртите ключа.
Това е пример за енкапсулация. Скривате сложното явление и оставяте на потребителя само простия модел. Той няма начин да се запознае с детайлите (или по-точно, ще е много зор – трябва да разбива вратата ключалката, да гледа байт-код и т.н.).
А сега си представете, че вашата врата има малък терминал и се отключва с код. Вместо вие да го правите директно (с ключ), натраквате кодът и електронната система го прави вместо вас. Това пък е пример за индиректност. Вместо вие да поемате инициативата, поставяте някой по средата и му я делегирате.
Сега, тезата ми е абстракция, енкапсулация и индиректност са три различни неща. Макар да не създават разбиване на концепциите (трихотомия?), не могат да се използват взаимно-заменяемо
Ин и Ян
Като човеци, много обичаме да класифицираме нещата като „добри“ и „лоши“. Съответно, ето моята класификация.
Абстракцията (в горния смисъл) е полезна почти винаги. Има такова нещо като твърде много абстракция, но дори в кариерата си на обсесивно-компулсивен Java програмист не съм я достигал. Винаги може да се престараете с даването на твърде много имена, но много по-рано изниква нещо друго, което да ви завърже ръцете. Обикновено. През останалото време, абстракцията ви е сред най-добрите приятел.
Енкапсулацията е нож с две остриета. От една страна ви позволява да изолирате клиентския код от имплементацията, което ви позволява да я замените по-късно. Отвсякъде плюс. От друга, понякога клиента се интересува от конкретната имплементация. Може му е далеч по-удобна в „суров вид“ или може би иска да заобиколи някакво ограничение, което вие (неволно) сте изградили. Което прави положението сиво. Добър компромисен вариант за мен е този в Ruby и Python – докато нормално използвате публичния интерфейс, има механизми да прекрачите енкапсулацията. Добавя някаква допълнително търкане, но само колкото да забележите, че правите нещо не съвсем както се очаква.
Индиректността си я представям по-скоро като необходимото зло. Понякога за да не създадете свързаност между две неща имате нужда да въведете междинен слой. Което прави нещата по-тромави. Определено има такова нещо като твърде много индиректност и е проблем на много, много „enterprise“ неща в Java света*.
Множествена теория
Но както казах, тези думички не създават разбиване. Ако изкодите нещо съдържащо едно от трите, много е вероятно да откриете някое от другите две. Убеден съм, че изобретателят на ключалката въобще не е мислил в тази терминология, но все пак неговото творение целящо енкапсулация „неволно“ влачи със себе си абстракция. Разбира се, думи като напитка са си просто абстракция. Което идва да покаже, че често тези явления се появяват заедно.
…и в заключение
абстракция != енкапсулация. енкапсулация != индиректност. индиректност != абстракция. Донякъде са сходни, до някъде не. Не ги ползвайте взаимно-заменяемо.
* – В контраст, изглежда проблемът в PHP света е твърде малко абстракция.
Може би е хубаво да споменеш и http://www.joelonsoftware.com/articles/LeakyAbstractions.html 🙂
Аз си имам едно просто правило, на практика следствие на закона за продънените абстракции, но изведен от практиката: за да можеш да работиш качествено с едно ниво на абстракция, трябва да познаваш системата поне едно ниво по-надолу.
Защо толкова много се говори за дизайн, абстракция и енкапсулация? Според мен на първо място в програмирането е имплементацията. Нещата трябва да се степенуват. И при най-добрия дизайн на кода и разбираемост за „потребителят“ на кода, ако имплементацията не е оптимизирана то този код не струва. Може би ще ме попиташ какво означава имплементация в крайна сметка. Имплементацията е зависима от езика на който се решава дадена задача и от мисленето на програмиста. Самото решаване на задачата е начина по който програмиста описва решението и. Една задача може да се реши по различни начини, и точно това прави сложността на решението да варира. За мен добрата имплементация е когато задачата се решава с минимум процесорно време и минимум заета памет. Другия въпрос който много ме терзае, е защо програмистите се наричат потребители? Какво прави един програмист потребител? И тук идва последното ми питане, което е вселдствие на една друга статия тук. Според теб не е ли удачно един програмист да си смени професията, когато се превърне в потребител?
Програмистът става потребител когато използва код написан от друг програмист. В смисъла на „потребител на дадена библиотека“.
Това е толкова демоде. Вече компютрите имат повече от 512KB памет. Нали. Добрата имплементация е тази която работи добре. Не е нужно да е оптимална, ако е достатъчно бърза. И когато имаме две-три достатъчно бързи имплементации, поне на мен ми се струва разумно да изберем най-добрата сред тях по други критерии – четимост и maintability.
И по-напълно несвързана линия, ако един програмист не влиза в кожата на потребител на приложението което прави, е съвсем удачно да си смени работата.