Языки программирования без ооп + видео обзор

Содержание
  1. Чистая архитектура. Часть II — Парадигмы программирования
  2. Парадигмы программирования
  3. Обзор парадигм
  4. Структурное программирование
  5. Объектно-ориентированное программирование
  6. Функциональное программирование
  7. Заключение
  8. Современные языки программирования, которые заставят вас страдать: Часть 1, ООП
  9. Современные языки программирования, которые заставят вас страдать: Часть 1, ООП
  10. Си-подобные языки
  11. Python
  12. TypeScript
  13. Javascript
  14. Прощай, объектно-ориентированное программирование
  15. Наследование, падение первого столпа
  16. Проблема обезьян с бананными в джунглях
  17. Решение проблемы обезьян в джунглях
  18. Проблема ромба (The Diamond Problem)
  19. Решение проблемы ромба
  20. Проблема хрупкости базового класса
  21. Решение для хрупкого базового класса
  22. Проблема Иерархии
  23. Решение проблемы иерархии
  24. Инкапсуляция, падение второго столпа
  25. Проблема ссылок
  26. Решение проблемы ссылок
  27. Полиморфизм, падение третьего столпа
  28. Нарушенные обещания
  29. И что тогда?
  30. Видео

Чистая архитектура. Часть II — Парадигмы программирования

Эта серия статей – вольный и очень краткий пересказ книги Роберта Мартина (Дяди Боба) «Чистая Архитектура», выпущенной в 2018 году. Начало тут.

Парадигмы программирования

Дисциплину, которая в дальнейшем стала называться программированием, зародил Алан Тьюринг в 1938 году. В 1945 он уже писал полноценные программы, которые работали на реальном железе.

Первый компилятор был придуман в 1951 году Грейс Хоппер (бабушка с татуировкой Кобола). Потом начали создаваться языки программирования.

Обзор парадигм

Существует три основных парадигмы: структурное, объектно-ориентированное и функциональное. Интересно, что сначала было открыто функциональное, потом объектно-ориентированное, и только потом структурное программирование, но применяться повсеместно на практике они стали в обратном порядке.

Структурное программирование было открыто Дейкстрой в 1968 году. Он понял, что goto – это зло, и программы должны строиться из трёх базовых структур: последовательности, ветвления и цикла.

Объектно-ориентированное программирование было открыто в 1966 году.

Функциональное программирование открыто в 1936 году, когда Чёрч придумал лямбда-исчисление. Первый функциональный язык LISP был создан в 1958 году Джоном МакКарти.

Каждая из этих парадигм убирает возможности у программиста, а не добавляет. Они говорят нам скорее, что нам не нужно делать, чем то, что нам нужно делать.

Все эти парадигмы очень связаны с архитектурой. Полиморфизм в ООП нужен, чтобы наладить связь через границы модулей. Функциональное программирование диктует нам, где хранить данные и как к ним доступаться. Структурное программирование помогает в реализации алгоритмов внутри модулей.

Структурное программирование

Дейкстра понял, что программирование – это сложно. Большие программы имеют слишком большую сложность, которую человеческий мозг не способен контролировать.

Чтобы решить эту проблему, Дейсктра решил сделать написание программ подобно математическим доказательствам, которые также организованы в иерархии. Он понял, что если в программах использовать только if, do, while, то тогда такие программы можно легко рекурсивно разделять на более мелкие единицы, которые в свою очередь уже легко доказуемы.

С тех пор оператора goto не стало практически ни в одном языке программирования.

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

Однако на практике мало кто реально применял аналогию с теоремами для доказательства корректности программ, потому что это слишком накладно. В реальном программировании стал популярным более «лёгкий» вариант: тесты. Тесты не могут доказать корректности программ, но могут доказать их некорректность. Однако на практике, если использовать достаточно большое количество тестов, этого может быть вполне достаточно.

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

ООП – это парадигма, которая характеризуется наличием инкапсуляции, наследования и полиморфизма.

Инкапсуляция позволяет открыть только ту часть функций и данных, которая нужна для внешних пользователей, а остальное спрятать внутри класса.

Однако в современных языках инкапсуляция наоборот слабее, чем была даже в C. В Java, например, вообще нельзя разделить объявление класса и его определение. Поэтому сказать, что современные объектно-ориентированные языки предоставляют инкапсуляцию можно с очень большой натяжкой.

Наследование позволяет делать производные структуры на основе базовых, тем самым давая возможность осуществлять повторное использование этих структур. Наследование было реально сделать в языках до ООП, но в объектно-ориентированных языках оно стало значительно удобнее.

Наконец, полиморфизм позволяет программировать на основе интерфейсов, у которых могут быть множество реализаций. Полиморфизм осуществляется в ОО-языках путём использования виртуальных методов, что является очень удобным и безопасным.

Полиморфизм – это ключевое свойство ООП для построения грамотной архитектуры. Он позволяет сделать модуль независимым от конкретной реализации (реализаций) интерфейса. Этот принцип называется инверсией зависимостей, на котором основаны все плагинные системы.

Инверсия зависимостей так называется, что она позволяет изменить направление зависимостей. Сначала мы начинаем писать в простом стиле, когда высокоуровневые функции зависят от низкоуровневых. Однако, когда программа начинает становиться слишком сложной, мы инвертируем эти зависимости в противоположную сторону: высокоуровневые функции теперь зависят не от конкретных реализаций, а от интерфейсов, а реализации теперь лежат в своих модулях.

Любая зависимость всегда может быть инвертирована. В этом и есть мощь ООП.

Таким образом, между различными компонентами становится меньше точек соприкосновения, и их легче разрабатывать. Мы даже можем не перекомпилировать базовые модули, потому что мы меняем только свой компонент.

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

В основе функционального программирования лежит запрет на изменение переменных. Если переменная однажды проинициализирована, её значение так и остаётся неизменным.

Какой профит это имеет для архитектуры? Неизменяемые данные исключают гонки, дедлоки и прочие проблемы конкурентных программ. Однако это может потребовать больших ресурсов процессора и памяти.

Применяя функциональный подход, мы разделяем компоненты на изменяемые и неизменяемые. Причём как можно больше функциональности нужно положить именно в неизменяемые компоненты и как можно меньше в изменяемые. В изменяемых же компонентах приходится работать с изменяемыми данными, которые можно защитить с помощью транзакционной памяти.

Интересным подходом для уменьшения изменяемых данных является Event Sourcing. В нём мы храним не сами данные, а историю событий, которые привели к изменениям этих данных. Так как в лог событий можно только дописывать, это означает, что все старые события уже нельзя изменить. Чтобы получить текущее состояние данных, нужно просто воспроизвести весь лог. Для оптимизации можно использовать снапшоты, которые делаются, допустим, раз в день.

Заключение

Таким образом, каждая из трёх парадигм ограничивает нас в чём-то:

Структурное отнимает у нас возможность вставить goto где угодно.

ООП не позволяет нам доступаться до скрытых членов классов и навязывает нам инверсию зависимостей.

Источник

Современные языки программирования, которые заставят вас страдать: Часть 1, ООП

Современные языки программирования, которые заставят вас страдать: Часть 1, ООП

Прим. ред. Это перевод статьи Ильи Суздальницкого. Мнение редакции может не совпадать с мнением автора оригинала.

В этой статье автор попытался дать объективную оценку современных популярных (и не очень) языков программирования. Если вы не согласны с автором, делитесь мнением в комментариях, и голосуйте за свои любимые языки программирования в нашем баттле.

Си-подобные языки

Языки программирования без ооп

Особенности языка: C++ может многое. Слишком многое. Это попытка стать универсальным, при этом не будучи хорошим в чём-то одном. В языке есть: оператор goto, указатели, ссылки, ООП, перегрузка операторов и другие не особо полезные фичи.

Почему C++ такой? Полагаю из-за возраста. Язык был создан в далёком 1979 году, когда его создатели не знали на чём нужно фокусироваться. Добавлять в язык больше возможностей считалось хорошей идеей, ведь это увеличивало область применения.

Скорость: C++ славится долгой компиляцией. Она значительно дольше чем у Java, но не так плоха, как у Scala. С другой стороны производительность уже скомпилированных приложений и время их запуска достаточно хороши.

Экосистема\Инструментарий: описание ошибок — не сильная сторона C++, это иллюстрирует следующий твит:

Языки программирования без ооп

В С++ мы не говорим «Пропущен символ *», мы говорим:

Сборка мусора: этой фичи никогда не было в C++. Ручное управление сборкой мусора — источник множества ошибок.

ООП, которое не получилось: во времена создания C++ ООП была крутой новой парадигмой, но при её реализации было допущено несколько критических ошибок. По крайней мере в С++ использовать ООП не обязательно (В отличие от Java).

Сложность изучения: С++ — сложный низкоуровневый язык без автоматического управления памятью. Его сложно изучать новичкам из-за чрезмерного количества функций.

Параллелизм: язык создавался во времена одноядерных вычислений, только в последние десятилетия в него была добавлена рудиментарная поддержка параллелизма.

Обработка ошибок: предпочтительный механизм обработки — выброс и обработка исключений.

Иммутабельность: поддержка отсутствует.

Поддержка NULL: все ссылки могут быть NULL.

Вердикт: неудавшаяся попытка превзойти язык Си. Вероятно, стоит использовать только для системного программирования. Однако и здесь есть лучшие альтернативы. Например, такие современные языки программирования, как Rust и Go.

Языки программирования без ооп

Сборка мусора: это одно из ключевых преимуществ Java над C++, позволяющее избежать множества багов.

Экосистема: Java существует достаточно долго, поэтому она обладает внушительной экосистемой для бэкенд-разработки. Это значительно уменьшает затраты на разработку.

ООП: Подробнее моё мнение об ООП можно узнать в статье ООП — катастрофа на триллион долларов. Вместо этого я процитирую более выдающегося человека: «Мне жаль, что я придумал для этого термин «объекты», и люди сфокусировались на побочной идее. Главная идея — сообщения» — Алан Кей, изобретатель ООП.

Скорость: Java запускается на JVM, что замедляет время старта. Я видел программы которые запускались по 30 секунд и дольше, что неприемлемо для современных приложений. Время компиляции растет на больших проектах, что влияет на продуктивность разработчиков (но всё ещё не так плохо как в Scala). Однако производительность JVM во время выполнения программы действительно хороша.

Сложность изучения: Хотя Java довольно простой язык, писать на нём хороший объектно-ориентированный код действительно сложно.

Параллелизм: Java также как и C++ был создан в эру одноядерных вычислений, и имеет лишь рудиментарную поддержку параллелизма.

Поддержка NULL: все ссылки могут быть NULL.

Обработка ошибок: предпочтительный механизм обработки — выброс и обработка исключений.

Иммутабельность: поддержка отсутствует.

Вердикт: Java был неплохим современным языком программирования в момент своего появления. Его портит сосредоточенность на ООП. Язык очень многословен и страдает от шаблонного кода.

Языки программирования без ооп

Синтаксис: синтаксис C# всегда немного опережал Java. Он меньше страдает от шаблонного кода. Но хоть C# и объектно-ориентированный язык, он тоже страдает от многословности. Приятно видеть как синтаксис C# улучшается с каждым релизом, добавляются: сопоставление с образцом, кортежи и другие возможности.

ООП: C#, как и Java больше сосредоточен на ООП. И снова, вместо того чтобы рассказывать о недостатках ООП, я процитирую более выдающегося человека: «Я считаю, что недостаточное переиспользование больше относится к ООП языкам, чем функциональным. Потому что проблема ООП языков в неявной среде, которую они таскают за собой. Вы хотите банан, но получаете гориллу, держащую банан и целые джунгли» — Джо Армстронг, создатель языка Erlang.

Мультипарадигменность: разработчики утверждают, что C# — мультипарадигменный язык. В частности, говорят что C# поддерживает функциональное программирование. Я считаю, что поддержки функций первого класса не достаточно для того, чтобы считать язык функциональным. Что для этого нужно? Как минимум встроенная поддержка иммутабельных структур данных, сопоставления с образцом, конвейерный оператор для создания цепочек функций и алгебраические типы данных.

Параллелизм: аналогично с C++ и Java.

Поддержка NULL: аналогично с C++ и Java.

Обработка ошибок: аналогично с C++ и Java.

Иммутабельность: аналогично с C++ и Java.

Вердикт: как и в случае с Java я бы порекомендовал более современные языки программирования. C# под капотом — та же Java, с более современным синтаксисом.

Python

Языки программирования без ооп

Экосистема: есть библиотеки почти на любой случай. В отличие от JavaScript, Python нельзя использовать для фронтенда. Это компенсируется множеством библиотек для анализа данных.

Сложность изучения: простой язык, который можно выучить за пару недель.

Типизация: имеет динамическую типизацию.

Скорость: Python — интерпретируемый язык программирования и является одним из самых медленных языков по времени выполнения программы. В случаях когда производительность важна, можно использовать Cython. Скорость запуска программ также страдает, в сравнении с нативными языками.

Инструменты: управление зависимостями в Python разочаровывает. Существует: pip, pipenv, virtiualenv, pip freeze и другие. Для сравнения — NPM в JS это всё что вам нужно.

Параллелизм: имеет только рудиментарную поддержку параллелизма.

Поддержка NULL: Все ссылки в Python могут быть NULL.

Обработка ошибок: предпочтительный механизм обработки — выброс и обработка исключений.

Иммутабельность: поддержка отсутствует.

Вердикт: увы, Python не имеет достаточной поддержки функционального программирования, которое как нельзя лучше подходит для анализа данных (лучше использовать Elixir). Язык не стоит использовать где-то кроме анализа данных (когда нет других альтернатив). Julia вероятно может быть хорошей заменой, но её экосистема ещё не такая зрелая, как у Pyhton.

Языки программирования без ооп

Скорость: язык создавался быстрым. Компиляция на Rust занимает больше времени чем на Go. Выполнение программ немного быстрее чем на Go.

Поддержка NULL: первый язык из нашего списка, использующий современную альтернативу. Вместо NULL значения здесь используется Option.

Обработка ошибок: Rust использует для этого подход современных функциональных языков программирования. Существует специальный тип Result, который показывает, что операция может выдать ошибку. Это очень похоже на Option, но случай None тоже хранит результат.

Управление памятью: нет сборщика мусора.

Параллелизм: из-за отсутствия сборки мусора параллелизм в Rust довольно сложен.

Иммутабельность: не поддерживается.

Низкоуровневый язык: в следствие этого, язык сложен для изучения, а производительность разработчиков меньше, чем на высокоуровневых языках.

Вердикт: Rust хорош для системного программирования, имеет мощную систему типов, Option и современную обработку ошибок. Однако, он всё ещё менее популярен чем TS и JS, потому что не подходит для бэкенда\Web API.

TypeScript

Языки программирования без ооп

Надмножество JS: это плюс, потому что многие уже знают JavaScript. Но с другой стороны, язык тянет за собой всё из JS.

Экосистема: здесь всё также наследуется от JS, что даёт доступ к огромной экосистеме JS. Работать с NPM очень приятно, особенно после Python. Однако, не все JS-библиотеки поддерживают TypeScript.

Система типов: с одной стороны поддерживает алгебраические типы данных, с другой имеет только рудиментарную поддержку приведения типов. Вы будете использовать Any чаще, чем вам того хотелось бы.

Поддержка NULL: в версии 2.0 была добавлена поддержка non-nullable типов, её можно включить с помощью флага –strictNullChecks. Однако это не подразумевается в языке.

Обработка ошибок: предпочтительный механизм обработки — выброс и обработка исключений.

Новые фичи JS: JavaScript быстрее получает клевые обновления. Используя Babel можно использовать даже экспериментальные фичи.

Иммутабельность: JS разработчики могут использовать библиотеки для этого. В TypeScript приходится полагаться на нативные массивы/оператор spread (копирование при записи):

К сожалению нативный оператор spread не производит глубокое копирование, а ручное расширение является громоздким. Копирование больших массивов/объектов плохо сказывается на производительности.
Ключевое слово readonly делает свойства иммутабельными. Однако это всё ещё далеко от адекватной поддержки иммутабельности.

TypeScript&React: если вы занимаетесь фронтендом, то наверняка используете React. Он не создан для работы с TS. React пригоден для функциональных языков.

Нужен ли TypeScript? Мне кажется что шумиха вокруг TS имеет ту же природу, что и популярность Java\C#. Её причина — поддержка большими корпорациями.

Вердикт: хотя TypeScript и позиционируется как «лучше чем JS», его достоинства переоценены.

Языки программирования без ооп

Параллелизм: это киллер фича языка. Как и Erlang\Elixir, Go следует mailbox модели параллелизма. Параллелизм в Go с помощью горутины в случае ошибки убивает всю программу, когда как параллелизм в Elixir убивает один из процессов.

Скорость: Go — очень быстрый язык, как по времени компиляции, так и скорости запуска программ.

Сложность изучения: это простой язык, который можно изучить за месяц.

Обработка ошибок: Go не поддерживает исключения. Вместо этого нужно явно обрабатывать возможные ошибки. Как и Rust, он возвращает два значения: результат вызова и возможную ошибку.

Не ООП: хоть многие со мной не согласятся, я считаю отсутствие ООП фич большим достоинством.

Типизация: отсутствие дженериков в Go приводит к дублированию кода.

Поддержка NULL: к сожалению, Gо использует NULL, а не более безопасные альтернативы.

Иммутабельность: не поддерживается.

Вердикт: Если вы не работаете в Google, тогда Go, вероятно, не лучший выбор. Go — простой язык, подходящий для системного программирования. Он действительно быстрый, лёгкий для изучения и отлично справляется с многопоточностью.

Javascript

Языки программирования без ооп

Экосистема: это сильная сторона JS. Этот язык используют в вебе, CLI, data science, и даже машинном обучении.

Сложность изучения: JavaScript один из самых простых для изучения языков. Его можно освоить за пару недель.

Типизация: JS — динамически типизирован. И это иногда порождает странные вещи вроде:

Иммутабельность: выше уже говорилось о том, что оператор spread снижает производительность. Однако, JS-библиотеки могут помочь.

React не создан для JavaScript: использование PropTypes обязательно в связке React+JS. Однако это означает, что PropTypes должны поддерживаться, что может стать вашим кошмаром.

Также возможны проблемы с производительностью в подобных моментах:

Такой невинный на первый взгляд код, может стать кошмарным, из-за того, что в JS []!=[]. Он заставит огромный список перерендериться при каждом обновлении.

Ключевое слово this: это, возможно, худшее, что есть в JS. Оно порождает неоднозначное поведение. Использование этого ключевого слова часто приводит к странным ошибкам.

Параллелизм: JS поддерживает однопоточный параллелизм в цикле событий. Это устраняет необходимость синхронизации потоков (блокировки). Хотя JavaScript и не ориентирована на параллелизм, работать с ним здесь проще, чем большинстве других языков.

Новые фичи JS: быстро получают поддержку (можно использовать экспериментальные).

Обработка ошибок: предпочтительный механизм обработки — выброс и обработка исключений.

Вердикт: JS — не идеален. Но при должной дисциплине может быть хорошим языком для фуллстек разработки.

Источник

Прощай, объектно-ориентированное программирование

Эта статья отражает личную точку зрения автора и она очень спорная. Не нужно ее воспринимать как истину в последней инстанции. Но темы затронутые в ней очень интересны, с точки зрения вопросов, зачем нужны принципы SOLID. Пожалуй, если бы автор был бы с ними знаком, он не смог бы написать эту статью или сильно ее скорректировал бы.
Оригинальная статья: Charles Scalfani — Goodbye, Object Oriented Programming

Я свято верил в преимущества ООП Наследование, Инкапсуляция и Полиморфизм. Это три основных компонента парадигмы ООП, своего рода это Три Столпа веры.

Я верил в идею Повторного использования кода и возможности использовать мудрость, приобретенную теми, кто был до меня в этом новом и захватывающем ландшафте.

Во мне жила идея, что все объекты реального мира можно отобразить в их аналоги классов мира ООП, и наконец то, что все в мире будет аккуратно поставлено на свои места.

Пожалуй, тогда я не мог быть более наивным.

Наследование, падение первого столпа

На первый взгляд, Наследование является самым большим преимуществом объектно-ориентированной парадигмы. Все упрощенные примеры иерархий форм, которые приводятся в качестве примеров для недавно внушенных, кажется, имеют логический смысл.

Языки программирования без ооп

И повторное использование кода — это как слова молитвы. Я уверовал эту идею и бросился в мир со своим новым знанием.

Проблема обезьян с бананными в джунглях

С религией в моем сердце и проблемами, которые нужно решить, я начал строить иерархии классов и писать код. И все было правильно в этом мире.

Я никогда не забуду тот день, когда я был готов добиться успеха основываясь на вере в повторного использования кода, через наследование от существующий классов. Это был момент, которого я ждал.

Но появился новый проект, и я вспомнил старый класс, который мне как то понравился в одном из моих последних проектов.

Нет проблем. Повторное использование придет мне на помощь. Все, что мне нужно сделать, это просто взять этот класс из другого проекта и использовать его.

Ну … на самом деле, … как оказалось … не только этот класс. Потребовалось использовать родительский класс. Но …и это еще не все. … Там так же потребовался родитель родителей … А потом … Еще понадобились вообще ВСЕ родители. Хорошо … Хорошо … И я справился и с этим. Нет проблем. Отлично. Но затем это отказалось компилироваться. Почему?? А потому что этот объект как оказалось содержал другой объект. Но этот другой объект мне был не нужен.

В итоге оказалось мне был нужен родитель объекта и родитель его родителя и так далее и так далее с каждым содержащимся объектом и ВСЕМИ родителями того, что они содержат вместе с родителями, родителями, родителями… Но были какие то объекты которые мне были не нужны и от которых было не возможно избавиться.

Есть замечательная цитата Джо Армстронга, создателя Erlang:

Проблема с объектно-ориентированными языками заключается в том, что у них есть вся эта неявная среда, которую они носят с собой. Вы хотели банан, но получаете гориллу с бананом и джунглями.

Решение проблемы обезьян в джунглях

Эту проблему можно решить, не создавая слишком глубоких иерархий. Но если наследование является ключом к повторному использованию, то любые ограничения, которые я налагаю на этот механизм, наверняка ограничат преимущества повторного использования. Правильно?

Так что же делать бедному объектно-ориентированному программисту?

Contain и Delegate. Но об этом позже…

Проблема ромба (The Diamond Problem)

Хотя не во всех языках ООП возможно такая проблема ее нужно упомянуть.

Рано или поздно следующая проблема порождает уродливую и, в зависимости от языка, неразрешимую задачу.

Языки программирования без ооп

Представьте следующий псевдокод:

Обратите внимание, что и класс Scanner, и класс Printer реализуют функцию, называемую start.

Так какую функцию запуска наследует класс Copier? Scanner? Printer? Он не может наследовать и ту и другую.

Решение проблемы ромба

Решение достаточно простое. Хотя наверно в реальном коде его лучше не использовать. Да и большинство ОО-языков не позволяют этого сделать.

Но, … что если смоделировать это? Я хочу использовать повторное использование кода!

Тогда вы должны Contain и Delegate.

Обратите внимание, что класс Copier теперь содержит экземпляр Printer и Scanner. Он использует функцию start реализации класса Printer. Он также может использовать класс Scanner.

Эта проблема — еще одна трещина в столпе Наследования.

Проблема хрупкости базового класса

Что бы избежать проблемы ромба, я делаю свои иерархии плоскими и не допускаю их цикличности.

И все было правильно в моем мире. До этого …

Как то мой код отлично работал какое то время, и в вдруг, в какой то день неожиданно перестал работать. Но я не изменял свой код.

Ну, может быть, это случайность … Но подождите … Что-то точно изменилось …

Но этих изменений не было в моем коде. Оказывается, изменение было в классе, от которого я унаследовался.

Как изменения в базовом классе могут нарушить мой код?

Представьте себе следующий базовый класс (он написан на Java, но его легко понять, даже если вы не знаете Java):

ВАЖНО: обратите внимание на закомментированную строку кода. Эта строка будет позже изменена, что и сломает мой код.

Этот класс имеет 2 функции в своем интерфейсе, add() и addAll(). Функция add() добавляет один элемент, а addAll() добавляет несколько элементов, вызывая функцию add.

А вот класс ArrayCount наследующий класс Array:

Класс ArrayCount является специализацией общего класса Array. Единственное поведенческое различие заключается в том, что ArrayCount ведет подсчет количества элементов через переменную count.

Давайте рассмотрим оба этих класса подробнее.

Array.add() добавляет элемент в локальный ArrayList. Array.addAll() вызывает локальное добавление ArrayList для каждого элемента.

ArrayCount.add() вызывает метод add() своего родителя, а затем увеличивает счетчик count. ArrayCount.addAll() вызывает метод addAll() своего родителя, а затем так же увеличивает count на количество элементов.

И все работает отлично.

Теперь переломное изменение. Помеченая строка кода в базовом классе изменяется на следующую:

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

Но владелец не обращает внимания на класс наследующий его класс ArrayCount. Теперь ArrayCount.addAll() вызывает метод addAll() своего родителя, который вызывает метод add(), который был переопределен в классе ArrayCount.

Это приводит к тому, что число увеличивается каждый раз, когда вызывается метод add() класса ArrayCount, а затем увеличивается СНОВА на количество элементов, добавленных в addAll() класса ArrayCount.

ТЕПЕРЬ count СЧИТАЕТСЯ ДВАЖДЫ.

Если это может произойти, и это происходит, то автор класса ArrayCount должен ЗНАТЬ, как реализован базовый класс. И они должны быть проинформированы о каждом изменении в базовом классе, так как это может привести к непредсказуемым последствиям для их производного класса.

Эта огромная трещина навсегда угрожает стабильности драгоценного столба Наследования.

Решение для хрупкого базового класса

Еще раз Contain и Delegate приходят на помощь.

Используя Contain и Delegate, мы переходим от программирования белого ящика к программированию черного ящика. При программировании Белого ящика мы должны знать о реализации базового класса.

При программировании «черного ящика» мы можем полностью не знать о реализации, поскольку мы не можем внедрить код в базовый класс, переопределив одну из его функций. Нам нужно только позаботиться о интерфейсе.

Наследование должно было стать огромным подспорьем при повторном использование кода.

Объектно-ориентированные языки не позволяют легко создавать Contain и Delegate. Они были разработаны, чтобы сделать легким наследование.

Если вы похожи на меня, вы начнете задумываться о том как используется наследование. Но что еще более важно, это должно поколебать вашу уверенность в силе классификации через иерархии.

Проблема Иерархии

Каждый раз, когда я начинаю работать в новой компании, я сталкиваюсь с проблемой, когда создаю место для размещения своих Корпоративных документов, например, Справочник сотрудника.

Должен ли я создать папку с именем Documents и затем создать папку с именем Company в ней?

Или я могу создать папку с именем Company, а затем создать папку с именем Documents в этой папке?

Оба варианта работают. Но что правильно? Какой лучше?

Идея Категориальных Иерархий (Categorical Hierarchies) состоит в том, что существуют Базовые Классы (родители), которые являются более общими, и что Производные Классы (их дети) являются более специализированными версиями этих классов. И даже более специализированные, когда мы продвигаемся по цепочке наследования. (См. Иерархию форм выше)

Но если родитель и ребенок могут произвольно поменяться местами, то явно что-то не так с этой моделью.

Решение проблемы иерархии

Категориальные иерархии не работают. Для чего нужны иерархии?

Для Containment (Сдерживание.).

Если вы посмотрите на реальный мир, вы увидите повсюду иерархии Containment (или исключительного владения).

То, что вы не найдете, это Категориальные иерархий. Объектно-ориентированная парадигма была основана на реальном мире, наполненном объектами. Но тогда она использует неверную модель, а именно. Категориальные Иерархии, которым нет реальной аналогии.

Но реальный мир наполнен иерархиями Containment. Отличным примером иерархии Containment являются ваши носки. Они находятся в ящике для носков, который содержится в одном ящике в вашем комоде, который содержится в вашей спальне, которая находится в вашем доме и т. д.

Каталоги на вашем жестком диске являются еще одним примером иерархии Containment. Они содержат файлы.

Так как же тогда классифицировать?

Что ж, если вы думаете о документах компании, это не имеет значения, куда я их положу. Я могу поместить их в папку «Документы» или в папку «Материалы».

Я классифицирую это с помощью тегов. Я помечаю файл следующими тегами:

У тегов нет порядка или иерархии. (Это тоже решает проблему с ромбом.)

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

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

Прощай, Наследование.

Инкапсуляция, падение второго столпа

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

Переменные состояния объекта защищены от внешнего доступа, т.е. они инкапсулированы в объекте.

Нам больше не придется беспокоиться о глобальных переменных, к которым может обратится кто попало.

Инкапсуляция это безопасность для ваших переменных.

Да здравствует инкапсуляция …

Проблема ссылок

Ради эффективности Объекты передаются в функции НЕ по их значению, а по ссылке.

Это означает, что функции не передадут объект, а передадут ссылку или указатель на объект.

Если объект передается по ссылке на конструктор объекта, конструктор может поместить ссылку на этот объект в закрытую переменную, которая защищена с помощью инкапсуляции.

Но переданный Объект НЕ безопасен!

Почему нет? Потому что какой-то другой кусок кода имеет указатель на объект, а именно код, который называется конструктор. Он ДОЛЖЕН иметь ссылку на объект, иначе он не может передать его конструктору?

Решение проблемы ссылок

Конструктор должен будет клонировать переданный объект. И не поверхностный клон, а глубокий клон, т. е. каждый объект, содержащийся в переданном объекте, и каждый объект в этих объектах и т. д. и т. д.

Не сказать что это эффективно.

А вот и проблема. Не все объекты могут быть клонированы. У некоторых есть связанные с ними ресурсы операционной системы, что делает клонирование бесполезным в лучшем случае или в худшем случае невозможным.

И почти в КАЖДОМ языке OOП есть эта проблема.

Прощай, Инкапсуляция.

Полиморфизм, падение третьего столпа

Полиморфизм был рыжеволосым пасынком Объектно-ориентированной Троицы.

Это своего рода комик Ларри Файн из троицы. (Прим. перевод. В данном случае автор имеет ввиду бесполезного персонажа)

Куда бы они ни пошли, он был там, но он был просто второстепенным персонажем.

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

Интерфейсы дадут вам это. И без всего багажа ООП.

А с интерфейсами нет предела тому, сколько разных типов поведения вы можете смешивать.

Так что без лишних слов мы прощаемся с ОО-полиморфизмом и приветствуем интерфейсный полиморфизм.

Нарушенные обещания

Ну, ООП, конечно, много обещал в первые дни. И на эти обещания все еще клюют наивные программисты, которые сидят в классах, читают блоги и посещают онлайн-курсы.

Мне потребовались годы, чтобы понять, как ООП лгал мне. Я тоже был с широко раскрытыми глазами, неопытным и доверчивым.

Прощай, объектно-ориентированное программирование.

И что тогда?

Здравствуй, Функциональное Программирование. За последние несколько лет было так приятно работать с тобой.

Просто чтобы вы знали, я не принимаю простые обещания за правду. Я много работал с ним, чтобы прийти к этому мнению.

Обжёгшись на молоке, будешь дуть и на воду

Если вам понравилось, нажмите 💚, чтобы другие увидели это здесь, на Medium.

Источник

Видео

5 САМЫХ ЛЁГКИХ языков программирования

5 САМЫХ ЛЁГКИХ языков программирования

Без классов и ООП твой Python код — буээээ. Ну... Или нет?

Без классов и ООП твой Python код — буээээ. Ну... Или нет?

😱 САМЫЕ СЛОЖНЫЕ ЯЗЫКИ ПРОГРАММИРОВАНИЯ

😱 САМЫЕ СЛОЖНЫЕ ЯЗЫКИ ПРОГРАММИРОВАНИЯ

5 лёгких языков программирования, которые интересно учить!

5 лёгких языков программирования, которые интересно учить!

Менее популярные языки программирования

Менее популярные языки программирования

Какой язык программирования выбрать в 2022 году?

Какой язык программирования выбрать в 2022 году?

Какой язык программирования выбрать первым в 2022 году

Какой язык программирования выбрать первым в 2022 году

Какой язык программирования УЧИТЬ ПЕРВЫМ? | Для новичков

Какой язык программирования УЧИТЬ ПЕРВЫМ? | Для новичков

7 языков программирования для Хакера | Какой язык программирования учить в 2022? | UnderMind

7 языков программирования для Хакера | Какой язык программирования учить в 2022? | UnderMind

Какой язык программирования учить в 2022?

Какой язык программирования учить в 2022?
Поделиться или сохранить к себе:
Добавить комментарий

Нажимая на кнопку "Отправить комментарий", я даю согласие на обработку персональных данных, принимаю Политику конфиденциальности и условия Пользовательского соглашения.