Буквени типове срещу класове в TypeScript

В моите беседи за TypeScript често ме питат как да използвам буквални типове и функционални стилове вместо класическо обектно-ориентирано програмиране. Възможно ли е дори да го направите? Заслужава ли да се проучи това решение? Краткият отговор определено е „да“, в TypeScript не е нужно да използваме OOP и класове, ако решим да го направим, можем да използваме това, което се нарича „буквални типове“.

Ето как работи.

Вместо да посочваме класове, можем да създадем тип със стриктни дискриминанти (дискриминиращи типове съюз), като така:

въведете XyzType = {kind: “x-kind”} | {kind: “y-kind} | {вид: “z-kind”}

Ще обясня как могат да се използват тези буквални типове в програмата, но първо, нека да разгледаме как би било направено в OOP.

Да речем, че бихме искали да създадем следната йерархия.

Както се вижда от горната диаграма на клас UML, трябва да въведем пет класа. Абстрактният основен клас е по-общ (в случая „Форма“), а останалите четири класа са специализирани конкретни.

Ето реализацията на това в TypeScript, използвайки OOP стил.

Въпросът е как ще прехвърлим това към функционално решение, основано на низове.

Просто, ще създадем дискриминиращи типове съюз, за ​​да определим родов тип, за който се грижим.

Ето и реализацията:

Изглежда по-сбито в сравнение с OOP решението. В дело е така. И така, какво печелим, какво губим при този подход?

Придобиване на:

  • Кратко представяне на намерението
  • По-малко свързване между код и данни
  • Данните са готови да се включат (т.е. можете да я JSON.stringify за прехвърляне или записване във файлова система и т.н.)

Разтоварване:

  • Управление на държавата в рамките на клас
  • Капсулиране на данни
  • Кохезивно изпълнение
Функцията е просто специална връзка между нейния вход (и) и съответния изход.

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

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

Може би в различен пост мога да вляза в подробности за плюсовете и минусите на тези две различни парадигми. Но засега просто трябва да подчертая, че и в двата подхода има предимства и недостатъци. Както знаете, JavaScript (и като суперсет от него TypeScript) е език за програмиране на много парадигми, поддържащ както функционални, така и обектно-ориентирани стилове. Простият факт е, че кодът, който ще създадем или тези, които вече са в реалния свят, са предимно комбинация от двете. Следователно като програмисти трябва да сме наясно с плюсовете и минусите на двата стила и съответно да намерим подходящо решение за нашите проблемни домейни.