Красная или синяя?
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.
Объектно-ориентированный подход делает код понимаемым (прим. ред.: поддерживаемым) по средствам инкапсуляции движущихся частей (прим. ред.: изменяемого состояния).
Функциональное программирование делает код поддерживаемым по средствам минимизации изменяемого состояния.
Это цитата Майкла Фэзерса, автора 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). Что будет если пойти этим путём можно посмотреть у Егора Бугаенко.
Но вместе с водой выкинули и младенца - ООД.
Поэтому единственный способ сделать такой код поддерживамым - это ФП (а дизайн - ООД).
И положа руку на сердце, ваши сервисы - это неймспейсы чистых функций, или пакеты процедур с побочками? Мои всё ещё как правило - пакеты процедур. Где-то гайдлайн такой, где-то инфраструктуры нет, а где-то это тупо быстрее и привычнее.
Но в последнее время проекты с удачно сложившимися звёздами, мне удаётся сделать в ФП-стиле. И оно того стоит.
А какую таблетку выбираете вы?:)