Функционално спрямо обектно ориентирано програмиране

Ей, момчета, извинявай за забавянето между статиите. Ще говоря за разликата между FP (функционално програмиране) и OOP (обектно-ориентирано програмиране) в тази статия и кога е най-добре да използвате всеки от тях.

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

Обектно-ориентирано програмиране

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

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

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

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

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

Функционално програмиране

Ключът към функционалното програмиране е в името: функции. FP е по-скоро за неизменност и композиране на функции, а не за обекти. Нека бързо да преминем основните характеристики:

Функциите са първокласни. Те могат да бъдат предавани, динамично създавани, съхранявани в структури от данни и третирани като всеки друг първокласен обект.

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

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

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

Данните са неизменни. Вместо да променяте структурата на данните, ефективно се създава нова.

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

Общата тема тук е, че няма странични ефекти. Но какво означава това? Как можете да напишете, че няма странични ефекти?

Без странични ефекти означава, че кодът е без гражданство. Няма наблюдаван ефект върху изпълнението на програмата, освен да се изчисли резултатът, като се имат предвид нейните данни. Това се дължи на идеята за референтна прозрачност. Изразът е референтно прозрачен, ако за всички програми всички събития на израза в програмата могат да бъдат заменени с резултат от оценка на израза, без това да влияе на наблюдаваното поведение на програмата.

Например: 1 + 2 винаги ще е равен 3. В този смисъл това е чиста функция, тъй като изходът никога не се променя за дадените входове. Така че, където и да видите 1 + 2, можете просто да го замените с 3 и да не виждате никакви неблагоприятни ефекти в цялостното изпълнение на програмата.

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

Както можете да видите отдясно, x е референтно прозрачен. Без значение колко пъти извиквате обратната или друга методика върху нея, изходната стойност винаги е една и съща. Няма странични ефекти. Като такъв, можете да замените x с действителната му стойност и пак да получите същия резултат. Това е чудесно, когато пишете код, който трябва да работи едновременно; не е нужно да се притеснявате за някакви странични ефекти, водещи до проблеми. Сега нека видим някакъв код със странични ефекти.

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

Нека да разгледаме кода в горния пример. Създадохме функция acceptreWinner, която отпечатва Player с най-висок резултат. Просто нали? Какво ще стане, ако искаме да намерим Играча с най-висок резултат в списък или друга структура на данните? Ами ако искаме да използваме отново части от кода? Не мога да направя точно това с първата дефиниция на acceptreWinner.

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

Наложително програмиране

Тук става въпрос просто за програмиране с изявления, които променят състоянието на програмата. Правехме това досега с OOP и FP. Като разширение можете да смесвате принципите на FP и OOP според нуждите. Те не са взаимно изключващи се. Те са просто възможности за оптимално справяне с определени проблеми.

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

Случаи на употреба

При FP нещото, което трябва да запомните е, че за конкретен вход, изходът винаги ще бъде един и същ, а входът винаги е неизменен. Няма промени в държавата. Така че, винаги когато трябва да напишете безопасен код на нишката, отидете с FP.

При OOP промените в състоянието са норма. Конкретният вход не винаги води до един и същ изход. Например: помислете за търсачка. Google не винаги връща еднакви резултати за един и същ ключ за търсене. Това важи срещу принципите на FP. Така че в този случай използвайте OOP.

Има повече ситуации, при които едната може да е по-добра от другата. Използването на паметта обикновено е по-високо в OOP, защото всичко е обект. FP трябва да създава ново копие на неизменна структура от данни всеки път, когато добавяте нов елемент. Всеки има своите недостатъци.

Дано това беше доста добро описание на разликите между двете. Някъде скоро ще направя запис на дизайнерските модели за FP. Очевидно те ще бъдат различни от тези от OOP. Така че, определено е важно да ги познавате. Така че, бъдете нащрек!

Как FP и OOP ме карат да се чувствам.

редактиране

Искам да отбележа няколко неща:

Java не е изцяло OOP език. Той има примитивни и статични членове, нито един от които не са обекти. Scala обаче е изцяло OOP език. Със сигурност можете да пишете програми, които са микс от FP и OOP в Scala. Но в Scala всичко е обект, дори функции.

И така, защо да го избираме над Java? Е, Scala поддържа всички FP концепции, докато Java поддържа само няколко. Това ви дава достъп до много мощни конструкции като съвпадение на модели и т.н. Имате достъп до всяка библиотека, която е на разположение на Java. И накрая, той работи на JVM. Може да не работи със същата скорост като Java код, но търгувате с няколко микросекунди за цял товар функционалност.

По отношение на случаите на употреба, позволете ми да разширя това, което вече казах.

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

Ако имате много различни свързани типове данни, които споделят функционалност и планирате да създадете повече в бъдеще, вероятно ще трябва да използвате наследяването. Това ще ви позволи да създадете модулен и многократен код. Отидете с OOP.

Ако имате само няколко типа данни, но много операции, които да извършите върху тях и е малко вероятно да добавите нови типове в бъдеще, нямате нужда от наследяване. Отидете с FP.

Ако конкретна операция може да бъде моделирана като математическа операция, отидете с FP. Това може да изглежда объркващо, така че ще разширявам. Кажете, че искате да напишете програма, която добавя нещата заедно. Можете да направите това с OOP, но е много по-лесно да направите това във FP. Незабавянето на даден обект отнема време и пространство. Защо да се занимавам с всичко това?

FP обикновено е чудесен в моделирането на операции, които изглежда следват математически модел (напр. Рекурсивни методи). Ако можете да моделирате програма като математическо доказателство, отидете с FP. Това е честно най-добрият съвет, който мога да ви дам.

Нито една от тези парадигми не е взаимно изключваща се. Можете да пишете по-добри програми, ако използвате комбинация от двете. Просто трябва да знаете кога да използвате кой. Така че, отидете на експеримент!