Через час-два тред про функции. Многим кажется, что функции это просто, но нет. Хорошие функции выглядят просто, но реализовать их тяжело. Какими руководствоваться правилами? Об этом и поговорим. #functions
Поехали! Обычно, от функций ожидают сокращения дублирования кода. Да, функции устраняют дублирование, но лишь в дополнение к тому, зачем они нужны. Настоящий смысл функции – повышение уровня абстракции. Звучит немного абстрактно, поэтому раскроем подробнее =>
Мир сложная штука, но эта сложность спрятана за простыми и понятными вещами. Например, работать с клавиатурой может даже ребенок, но лишь потому что она хорошо спроектирована и прячет от нас детали реализации. Нам не нужно знать физических законов для ее использования
Клавиатура, в свою очередь, состоит из деталей, которые тоже имеют достаточно высокий уровень абстракции: микросхема, мембраны, корпус, индикаторы. Все это на поверхности и достаточно далеко от реально протекающих процессов внутри
То о чем мы говорим, называется слоями абстракции. Любую систему можно разбить на набор слоев, в которых каждый следующий слой, строится на базе предыдущего слоя, что создает ощущение простоты. Мы не думаем о системе до самой глубины, нам достаточно знать про текущий слой
Из этого вытекает логичное, но, почему-то, редко обсуждаемое правило. На самом верхнем уровне, проектирование кода должно идти от слоев, которые 1) имеют четко выраженную ответственность 2) оперируют только абстракциями более низкого уровня 3) не прыгают через уровни
В самом общем виде эта мысль описана в SOC en.wikipedia.org/wiki/Separatio… Примерами здесь служат: модель OSI, HTML/CSS, Миксины и многое другое. Это очень близко нашей теме, хотя и не один в один. Плюс хорошая адаптация к коду от Мартина: Принцип Single Level Of Abstraction
При этом тот же Мартин рекомендует в своей книге "делайте функции настолько мелкими настолько это возможно". Это довольно вредный совет, потому что начинается код ради кода и, в итоге, все усложняется. Функции не должны быть маленькими, функции должны соответствовать абстракции
Возникает вопрос, а на что конкретно ориентироваться чтобы создавать функции хорошо? И у меня есть ответ. Существует довольно много простых и не очень принципов, которые определяют как лучше проектировать функции. Большая часть из них универсальна и подходит для всех языков
Начнем с самого главного правила в программировании: отделение чистого кода от кода с побочными эффектами или "функциональное ядро и императивная оболочка". Чтобы понять о чем тут идет речь, поговорим о том, что такое чистые функции.
Чистая функция – детерминированная функция, без побочных эффектов. Детерминированность означает возврат одного и того же результата на один и тот же вход в рамках одного запуска программы. При следующем запуске результат может быть другим, но постоянным до остановки.
Побочные эффекты – влияние функции на внешнее окружение: модификация глобальных переменных, аргументов переданных по ссылке, ввод/вывод (чтение и запись файлов, сеть, консоль и т.п), вызов внутри себя других функций с побочными эффектами
Чистые функции имеют непосредственное отношение к понятию "бизнес-логика", алгоритмической части программы, которая занимается сутью бизнеса, ради которого она написана. Например много логики в автопилотах, бухгалтерском софте, логистическом, банковском и так далее.
Эта логика, сама по себе, не имеет отношения к коду. Она, часто, существует на уровне документов и спецификаций, ее понимают заказчики бизнеса и ради нее, собственно, софт и пишется.
А выполнение HTTP-запросов, взаимодействие с базой данных, запись и чтение файлов, все это обвязка, которая интегрирует логику в нужную среду и дает возможность эту логику запускать, сохранять и воспроизводить результаты ее работы. Два больших независимых и определяющих все слоя
Из этого правила следует, что код выполняющий побочные эффекты нужно располагать как можно выше по стеку вызовов. Идеальная цепочка: прочитали => вычислили => записали. Вычисление ничего не должно знать про окружающую среду. Чуть подробнее в статье: ru.hexlet.io/blog/posts/sov…
Кстати, хороший пример Data Mapper (в противовес Active Record), в котором персистентность выделена в отдельный слой, чтобы не мешаться с бизнес-логикой. Другой вопрос насколько это хорошо работает (в реальности бывает сложно из-за кучи абстракций)
И в целом все это применимо к MVC. M – тоже требует такого хорошего деления и просто введения сервисов тут не достаточно. Не счесть сколько раз я сталкивался с тем, что любую логику просто пихают в сервисы. Хотя сервисы это в первую очередь Service layer martinfowler.com/eaaCatalog/ser…
Другой важный принцип проектирования функций Command-Query Separation (CQS). Он не совсем про слои, но очень в тему. Обычно звучит так: "задавая вопрос, не изменяй ответ". То есть геттеры не должны менять состояние, это противоречит их сути и может ломать детерминированность
А команды (любые функции на изменение) не должны возвращать данные. Это создает сложный в понимании код. Хотя справедливости ради, не всегда возможно избавиться от возврата данных у команд. Например открытие соединений или файлов всегда возвращают что-то
Отличный пример того, как рельсы клали на это. Метод .valid? у моделей запускает колбеки типа before_validation(), внутри которых меняют состояние налево и направо. Что приводит к офигеть какому неожиданному коду, когда ты просто проверил валидацию, а у тебя улетел http-запрос
В сети довольно много статей про это dan-manges.com/blog/2011/acti… Оно касается не только рейлс, похожее есть и в других местах. Рекомендую почитать и задуматься)
И так поехали тред про конечные автоматы (fsm, state machines). Одной из ключевых тем в разработке софта касающуюся всех программистов без исключения. Будет много примеров для разных языков. После этого треда вы, вероятно, увидите мир другими глазами. Лайк, шер, алишер, погнали!
Существует заблуждение что автоматы это что-то, из математики, не имеющее отношение к реальной жизни. Их используют в лексическом анализе для написания парсеров. Многие помнят лабы на си, где нужно было разбирать строчку. Да это тоже автоматы, но совсем другие.
Мы же поговорим об автоматах с точки зрения моделирования бизнес-процессов, которые мы программисты по большей части и программируем. Сразу предупрежу, что тема автоматов ну очень глубокая, они бывают совсем разные и к ним нужен разный подход. Поэтому что-то останется за кадром
Сегодня последний тред из мифов в ООП #oopmyths Поговорим о том, что обычно считают ООП кодом и что влияет на его структуру больше всего. Какие вещи в ООП универсальны сквозь большинство языков. Включайтесь)
Ну и традиционно вопрос. Может ли код быть одновременно объектно-ориентирован и функционален?
Несмотря на любую теорию, на практике, многие программисты если не видят в коде конструкций вида something.do(data), то они не считают код объектно ориентированным. Проверял это много раз. Все это сопровождается философией в стиле "это поведение", а иначе нет.
Начинаем третий про наследование и отношения. Во второй части был опрос про отношения, на который правильно ответило только 29 процентов! И кажется что не все поняли первый тред. #oopmyths
Начнем как обычно с опроса. Вопрос: "Объяснять ООП надо через аналогию с реальным миром". Именно так делается в подавляющем большинстве источников и сервисах типа стековерфлоу.
Строго говоря, отношение частное-общее про типы, а не классы. В статье про сабтайпинг об этом написано внизу: en.wikipedia.org/wiki/Subtyping. И принцип лисков тоже не про классы. Наследование классов это всего лишь способ убрать дублирование кода. Один из худших способов.
Итак) Второй тред посвященный ООП и мифам вокруг него. По итогам первого треда уточню, что разбор ООП идет не с точки зрения систем типов, а с точки зрения организации кода и влиянии на архитектуру. Лайк, шер, будет много интересного, поехали!
Отношение общее-частное это:
В первом треде мы разобрали что под полиморфизмом в ооп понимают полиморфизм подтипов. Остальные виды полиморфизмов тоже важны, но они не играют определяющую роль с точки зрения парадигмы. Полиморфизм подтипов, если по простому, сводится к разным реализациям нужного поведения.
Под типами и подтипами часто понимаются интерфейсы (и их аналоги типа протоколов) и отношения между интерфейсами. Иногда говорят "наследование интерфейсов". Соответственно классы и наследование классов тут не причем. И принципов Лисков не имеет отношения к классам.
Сегодня я хочу попробовать формат коллективных твиттеров у себя в твиттере. Потвичу пару часов о мифах вокруг ООП. Лайк, шер приветствуется. Задание на самопроверку:
Какой вид полиморфизма имеется ввиду когда речь идет про полиморфизм в ООП?
Перед тем как я буду выдавать какие-то утверждения и прикладывать пруфы. У меня есть десяток мифов, которые я бы хотел разобрать задав вопросы тут. Поэтому продолжаю спрашивать:
Нужно ли наследование для реализации полиморфизма подтипов?
Может ли язык называться ООП если в нем нет классов?
Поиграем в игру. 1 лайк - 1 факт из жизни президентского полка. Я служил с 2004 по 2006 год в первой роте, той что стоит на МНС в Александровском Саду (сейчас немного другая структура).
Писать начну вечером. Структура такая:
1. Что такое ПП и какие задачи выполняет. 2. Как туда попадают. 3. Жизнь в полку.
Президентский полк - особое подразделение, которое не подчиняется министерству обороны, а является подразделением ФСО. Полное наименование: Президентский полк Службы коменданта Московского Кремля Федеральной службы охраны Российской Федерации