Красная или синяя?

January 22, 2021

Статья не отредактирована после переезда с телеграфа. Оригинал

Привет! Чёт ~эта неделька опять выдалась сложная~ я прокрастинирую пост о грязных функциях и сайд эффектах, потому что там кой-чего не схоидтся. Поэтому сёня пост в бок с хейтом мейнстримного ``ООП'' - почитывал на досуге код на кложуре, и меня опять малёха бомбануло.

Меня тут осенило. Возьмём вот такой кусочек кода на кложуре (чисто функциональном языке): Сброс пароля в ФП стиле

(my-app.users/reset-password user)

Здесь вызываюется функция reset-password из неймспейса my-app.users с параметром user.

А потом сделаем несколько трансформаций.

Вжух, переносим скобку:

my-app.users/reset-password (user)

Вжух, меняем кебаб на камел кейс:

my_app.use  rs/resetPassword (user)

Вжух, меняем неймспейс на объект:

val users = my_app.UserService()
users.resetPassword(user)

И вишенка, на торте, чтобы по СОЛИДнее было:

val userService: UserService = UserServiceImpl(getDb())
userService.resetPassword(user)

Знакомый код? Если у вас бэк на спринге, то готов поставить тыщу, что у вас где-то есть com.my_company.my_app.services.UserService:)

Пока этот пост отлёживался случайно наткнулся на очередное подтверждение повсеместности такого подхода: Случай из жизни

При том > OO makes code understandable by encapsulating moving parts. > > FP makes code understandable by minimizing moving parts.

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

Функциональное программирование делает код поддерживаемым по средствам минимизации изменяемого состояния.

https://twitter.com/mfeathers/status/29581296216?lang=en,

Это цитата Майкла Фэзерса, автора Working Effectively with Legacy Code и чувака придумавшего акроним SOLID:)

А такой стиль не является ОО, т.к. не инкапсулирует ``двигающиеся части'' (состояние, структуру User в данном случае).

ООПный код был бы примерно такой:

val rs = usersTable.findUser(id)
val user = User(rs)
user.resetPassword()
user.saveTo(db)

И на самом деле это не наша вина - тръу ООП нарушает SRP (в любой интерпретации), loose coupling/high cohesion и вообще заточено под масштабирование кол-ва типов данных в системе, а не фич реализуемых на основе этих типов. А софт, как привило, всё таки обрастает новыми фичами поверх старых данных, а не новыми данными для старых фич. Поэтому у девеолперов был только один разумный выбор, чтобы хоть как-то выжить - дефакто отказаться от ООП.

Справедливости ради масштабироваться ещё наследованием. Но, во-первых, оно вроде уже повсеместно признано не удачной затеей. А, во вторых, не понятно кто от кого должен наследоваться в случае RelationalUser (работа с БД) и JsonUser ((де)сериализация Json). Что будет если пойти этим путём можно посмотреть у Егора Бугаенко.

Но вместе с водой выкинули и младенца - ООД.

Поэтому единственный способ сделать такой код поддерживамым - это ФП (а дизайн - ООД).

И положа руку на сердце, ваши сервисы - это неймспейсы чистых функций, или пакеты процедур с побочками? Мои всё ещё как правило - пакеты процедур. Где-то гайдлайн такой, где-то инфраструктуры нет, а где-то это тупо быстрее и привычнее.

Но в последнее время проекты с удачно сложившимися звёздами, мне удаётся сделать в ФП-стиле. И оно того стоит.

А какую таблетку выбираете вы?:)