Программы для объектно ориентированного программирования

Обновлено: 02.07.2024

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

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

Объектно-ориентированное программирование — это стиль кодирования, который позволяет разработчику группировать схожие задачи в классы. Таким образом код соответствует принципу DRY (don't repeat yourself – не повторяй самого себя) и становится лёгким для сопровождения.

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

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

Что такое объекты и классы

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

В чем заключается разница между классами и объектами


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

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

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

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

Структура класса

Синтаксис для создания класса очень прост: для объявления класса используется ключевое слово class, за которым следует имя класса и набор фигурных скобок (<>):

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

Чтобы увидеть содержимое объекта, используйте var_dump():

Можно протестировать весь процесс, скопировав весь код в файл test.php :

Загрузите страницу в ваш браузер и на экране должна появиться следующая строка:

Вы только что создали свой первый скрипт ООП.

Определение свойств класса

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

Чтобы добавить свойства классу MyClass, используйте такой код в вашем скрипте:

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

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

Так как может существовать несколько реализаций класса, то без ссылки на конкретный объект, свойство не может быть прочитано, потому что скрипт не может определить, из какого объекта следует читать. Стрелка (->) - это конструкция ООП, которая используется для получения доступа к свойствам и методам заданного объекта.

Модифицируйте скрипт в test.php, чтобы прочитать значение свойства, а не выводить информацию обо всем классе:

Обновите страницу в вашем браузере, чтобы увидеть результат работы скрипта:

Определение методов класса

Метод — это функция класса. Индивидуальное действие, которое может выполнить объект, определяется в классе как метод.

Например, создадим методы, которые устанавливают и читают значение свойства $prop1:

Примечание — ООП позволяет объекту ссылаться на самого себя, используя $this. При работе внутри метода, использование $this позволяет использовать имя объекта вне класса.

Чтобы использовать метод, вызовите его как обычную функцию, но сначала укажите объект, которому она принадлежит. Читаем свойство из MyClass, изменяем значение, читаем его снова:

Обновляем страницу в браузере и видим следующее:

Преимущества ООП проявляются при использовании множества объектов одного класса.

Когда вы загрузите страницу в браузер, то увидите следующее:

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

Магические методы в ООП

Чтобы сделать использование объектов проще, PHP имеет несколько магических методов. Это специальные методы, которые вызываются, когда над объектом производятся определённые действия. Таким образом разработчик может выполнить несколько общих задач относительно просто.

Использование конструкторов и деструкторов

Когда создаётся объект, очень часто нужно, чтобы при этом сразу производились установки некоторых свойств. Для выполнения таких задач PHP имеет магический метод __construct(), который вызывается автоматически при создании нового объекта.

Примечание — константа __CLASS__ возвращает имя класса, в котором она вызывается; это одна из магических констант PHP.

Обновляем страницу в браузере и получаем результат:

Для вызова функции в процессе удаления объекта используется магический метод __destruct(). Это очень полезный метод для корректной очистки свойств класса (например, для правильного закрытия соединения с базой данных).

Обновляем страницу в браузере и получаем результат:

При достижении конца файла PHP автоматически освобождает все ресурсы.

Для явного вызова деструктора и удаления объекта Вы можете использовать функцию unset():

Теперь результат работы кода будет выглядеть после загрузки в браузер следующим образом:

Преобразование в строку

Чтобы избежать ошибки, если скрипт попытается вывести MyClass как строку, используется другой магический метод __toString().

Без использования __toString() попытка вывести объект как строку приведёт к фатальной ошибке. Попробуйте использовать функцию echo, чтобы вывести объект без применения магического метода:

Результат работы будет выглядеть следующим образом:

Чтобы избежать ошибки, используйте метод __toString():

В этом случае попытка конвертировать объект в строку приведёт к вызову метода getProperty(). Загружаем скрипт в браузер и смотрим на результат работы:

Подсказка — В дополнение к магическим методам, которые использовались в данном разделе, существует ещё несколько полезных функций. Чтобы получить полный список магических методов смотрите руководство по PHP.

Использование наследования классов

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

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

Перегрузка унаследованных свойств и методов

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

Изменения приведут к следующему выводу при выполнении кода:

Сохранение оригинальной функциональности при перегрузке методов

Чтобы добавить новую функциональность к унаследованному методу при сохранении функций оригинального метода, используйте ключевое слово parent с оператором разрешения видимости (::):

Определение области видимости свойств и методов

Для дополнительного контроля над объектами, методами и свойствами устанавливается область видимости. Таким образом контролируется как и откуда могут быть доступны свойства и методы. Существует три ключевых слова для установки области видимости: public, protected, и private. В дополнение к установке области видимости, методы и свойства могут быть объявлены как static, что позволяет получать к ним доступ без реализации класса.

Примечание — Область видимости — это новое свойство, которое было введено в PHP 5. чтобы узнать о совместимости ООП с PHP 4, смотрите руководство по использованию PHP.

Свойства и методы public (Общие)

Все свойства и методы, которые вы использовали ранее в данной статье были public (общими). Это значит, что доступ к ним можно было получить где угодно, как внутри класса, так и вне класса..

Методы и свойства protected (Защищённые)

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

Объявим метод getProperty()как protected в MyClass и попробуем получить доступ к нему вне класса:

При попытке выполнить скрипт, будет сгенерирована следующая ошибка:

Теперь, создадим новый метод в MyOtherClass для вызова метода getProperty():

При выполнении скрипта результат будет таким:

Методы и свойства private (Частные)

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

Для демонстрации объявим метод getProperty() как private в MyClass, и попробуем вызвать метод callProtected() из
MyOtherClass:

сохраняем скрипт, обновляем страницу в браузере и получаем следующее:

Методы и свойства Static (статические)

Методы и свойства, объявленные с директивой static могут быть доступны без инициации класса. Вы просто используете имя класса, оператор разрешения видимости и имя свойства или метода.

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

Для демонстрации добавим статическое свойство $count и статический метод plusOne() к классу MyClass. Затем установим цикл do. while для вывода увеличивающего значения $count, до тех пор пока оно не станет больше 10:

Примечание — Для доступа к статическим свойствам знак доллара ($) должен стоять после оператора разрешения видимости.

Выполнение скрипта в браузере выдаст следующий результат:

Комментирование с помощью DocBlock

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

DocBlock определяется с помощью блочного комментария, который имеет в начале дополнительную звездочку:

Действительная мощность DocBlocks открывается при использовании тегов, которые начинаются с символа (@) за которым без пробелов следует имя тега и значение. Теги DocBlock позволяют разработчику определять автора файла, лицензию на использование класса, информацию о свойствах и методах, а также много другой полезной информации о коде.

Наиболее популярные теги:

@copyright: Тег указывает год и владельца копирайта на текущий элемент. Формат: 2010 Владелец копирайта.

@var: Содержит тип и описание переменной или метода класса. Формат: тип элемент описание.

@param: Содержит тип и описание параметров функции или метода. Формат: тип $элемент описание элемента.

@return: Тип и описание возвращаемого значения для функции или метода. Формат: тип возвращаемое_значение описание.

Простой класс с комментариями в стиле DocBlocks может выглядеть так::

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

Сравнение объектно-ориентированного и процедурного программирования

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

Аргумент 1: простота применения

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

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

Процедурный подход

Ниже приводится процедурный подход для нашего примера:

Выполнение кода приведёт к следующему результату:

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

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

Подход ООП

Вот подход ООП для решения нашего примера:

Данный код выдаст следующий результат:

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

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

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

Аргумент 2: лучшая организация

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

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

Контроллер может использовать PHP функцию __autoload() для динамического присоединения только тех классов, которые нужны, а не включать все 150 просто на всякий случай:

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

Аргумент 3: легче обеспечивать поддержку

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

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

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

Резюме

Изучение ООП продвигает вас на следующий уровень в программировании. При правильном использовании ООП помогает Вам создавать легко читаемый, простой для поддержки, портативный код, который сохранит для вас (и для тех, кто работает с вами) часы напряженной работы над проектом.

5 последних уроков рубрики "PHP"

Фильтрация данных с помощью zend-filter

Когда речь идёт о безопасности веб-сайта, то фраза "фильтруйте всё, экранируйте всё" всегда будет актуальна. Сегодня поговорим о фильтрации данных.

Контекстное экранирование с помощью zend-escaper

Обеспечение безопасности веб-сайта — это не только защита от SQL инъекций, но и протекция от межсайтового скриптинга (XSS), межсайтовой подделки запросов (CSRF) и от других видов атак. В частности, вам нужно очень осторожно подходить к формированию HTML, CSS и JavaScript кода.

Подключение Zend модулей к Expressive

Expressive 2 поддерживает возможность подключения других ZF компонент по специальной схеме. Не всем нравится данное решение. В этой статье мы расскажем как улучшили процесс подключение нескольких модулей.

Совет: отправка информации в Google Analytics через API

Подборка PHP песочниц

Подборка из нескольких видов PHP песочниц. На некоторых вы в режиме online сможете потестить свой код, но есть так же решения, которые можно внедрить на свой сайт.

ООП провалилось, осваивай функциональные языки прямо сейчас

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

Конечная цель разработки софта состоит в написании надёжного кода. Забагованному и ненадёжному коду не помогут никакие концепции. А лучший путь к надёжности кода – простота. Следовательно, главная задача разработчиков – снижение сложности кода.

ООП провалилось, осваивай функциональные языки прямо сейчас

«Объектно-ориентированные программы предложены как альтернатива правильным. »

– Эдсгер Вибе Дейкстра, один из разработчиков концепции структурного программирования.

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

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

Многие не согласятся, но правда в том, что современное ООП никогда не разрабатывалось правильно. Оно никогда не опиралось на правильные исследовательские институты (в отличие от Haskell/FP). У ООП нет десятилетий строгих научных исследований. Лямбда-исчисления предлагают полную теоретическую основу для функционального программирования. У ООП нет ничего близкого к этому. По большому счёту, ООП «просто случилось».

ООП-код недетерминированный, в отличие от функционального программирования нам не гарантирован одинаковый вывод при одном и том же вводе. В качестве упрощённого примера: 2 + 2 или calculator.Add(2, 2) дают на выходе 4, но иногда могу дать 3, 5 или вообще 1004. Зависимости объекта Calculator могут менять результат вычислений в тонкой, но глубокой манере.

«C++ – ужасный [объектно-ориентированный] язык. Ограничение вашего проекта до C означает, что люди не облажаются с какой-нибудь идиотской "объектной моделью"c&@p.» – Линус Торвальдс, создатель Linux.

Линус Торвальдс известен своей критикой в адрес C++ и ООП. Он прав в ограничении программистов. Чем меньше выбора у программиста, тем гибче становится его код. В цитате выше Линус Торвальдс настоятельно рекомендует использовать хороший фреймворк как базу для кода.

ООП провалилось, осваивай функциональные языки прямо сейчас

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

Хороший фреймворк помогает писать надёжный код. Он должен помогать снижать сложность с помощью:

  1. Модульности и повторного использования.
  2. Правильной изоляции состояний.
  3. Высокого отношения сигнал/шум.

К несчастью, ООП предоставляет разработчикам слишком много инструментов без ограничений. Даже если ООП обещает обратиться к модульности и улучшению повторного использования, ему не удаётся выполнить обещания (далее подробней). ООП-код поощряет использование общих изменяемых состояний, которые снова и снова доказывают свою небезопасность.

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

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

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

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

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

Алан Кэй придумал термин «Объектно-ориентированное программирование» а 1960х. Благодаря своему биологическому бэкграунду он пытался создать коммуникацию между компьютерными программами по подобию живых клеток.

ООП провалилось: осваивай функциональные языки прямо сейчас

Основная идея Алана Кэя состояла в коммуникации между независимыми программами (клетками). Состояние независимых программ никогда не раскрывалось внешнему миру (инкапсуляция).

Вот и всё. ООП никогда не было предназначено для таких вещей, как наследование, полиморфизм, ключевого слова «new» и несчётного количества шаблонов проектирования.

Чистый ООП

Конечно! Erlang – это, возможно, самый надёжный язык в мире. Он поддерживает основную часть мировой инфраструктуры телекома (следовательно, и интернета). Некоторые системы, написанные на Erlang надёжны на 99.9999999%.

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

Что усложняет код? В основном общее изменяемое состояние, ошибочные абстракции, низкое отношение сигнал/шум (часто вызываемое шаблонным кодом). Всё это распространено в ООП.

ООП провалилось: осваивай функциональные языки прямо сейчас

«Я думаю, что большие объектно-ориентированные программы борются со сложностью, возрастающей по мере построения большого графика изменяемых объектов. Ну, знаете, когда вы пытаетесь понять и держите в памяти то, что произойдёт при вызове метода, и какие будут побочные эффекты.» – Ричард Хикки, создатель языка программирования Clojure.

Состояние само по себе безвредно. Но изменяемое состояние – это большой нарушитель. Особенно если оно общее. Что такое изменяемое состояние? Любое состояние, которое может измениться. То есть, переменные или поля в ООП.

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

Проблема параллелизма

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

Не все состояния – зло

Получается, состояния – зло? Нет, изменение состояния, наверное, нормально в случаях настоящей изоляции (не ООП-изоляции).

Вполне нормально иметь неизменяемые объекты передачи данных. Ключевое слово здесь – «неизменяемые». Такие объекты используются для передачи данных между функциями.

Так или иначе, такие объекты сделают методы и свойства ООП абсолютно избыточными. Какой смысл в методах и свойствах объекта, если они не могут быть изменены?

ООП провалилось: осваивай функциональные языки прямо сейчас

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

Инкапсуляция – это троянский конь ООП. Он продаёт идею общего изменяемого состояния под предлогом безопасности. Инкапсуляция позволяет (и даже поощряет) проникновение небезопасного кода в наши исходники.

Проблема глобального состояния

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

Для эффективности кода объекты передают не свои значения, а их ссылки. Вот где «внедрение зависимости» терпит неудачу.

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

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

Методы/Свойства

Методы и свойства, которые предоставляют доступ к определённым полям ничем не лучше прямого изменения полей. Неважно меняете ли вы состояние объекта с помощью красивого свойства метода – результат один: изменённое состояние.

Спойлер: Функциональное программирование. :)

Если такие термины, как функторы и монады ни о чём вам не говорят, то вы не одиноки! Функциональное программирование не было бы таким пугающим имей оно более интуитивные названиями основных идей. Функторы – это то, что можно преобразовать с помощью функции, например, list.map . Монады – просто цепочка связанных вычислений!

Функциональное программирование сделает из вас лучшего разработчика. Вы наконец-то начнёте писать код, который решает проблемы реального мира, вместо того, чтобы тратить время на обдумывание абстракций и шаблонов проектирования.

Возможно, вы ещё не осознали себя функциональным программистом. Вы используете функции в повседневной работе? Да? Тогда вы уже функциональный программист! Осталось научиться эффективному использованию функций.

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

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

Рассказываю об одной из важнейших парадигм в программировании.

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

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

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

Существуют различные типы парадигм, например процедурный, ориентированный на работу с функциями, или логический, подразумевающий решение базовых логических задач в духе «если А = true, то и B = true». Но есть и более интересный подход к решению задач разработки, и это ООП-парадигма.

Что такое ООП?

Объектно-ориентированное программирование, ООП – это одна из парадигм разработки, подразумевающая организацию программного кода, ориентируясь на данные и объекты, а не на функции и логические структуры.

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

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

Структура объектно-ориентированного программирования

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

Объекты

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

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

Объект – это кусок кода, описывающий элемент с конкретным набором характеристик и функций. Например, вы делаете видеоигру, в которой есть персонаж. Ваш герой.

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

Объекты могут описывать других персонажей и средства передвижения.

Методы

Методы – это функции, описанные внутри объекта или класса. Они относятся к конкретному объекту и позволяют взаимодействовать с ними или другими частями кода. Выше мы уже затронули «способности» персонажа-объекта, вот они и являются наиболее понятным описанием методов. Когда ваш персонаж выполняет действие в игре, он задействует метод, описанный в его объекте.

Атрибуты

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

Классы

Это наиболее абстрактная и обобщенная форма в ООП. Что-то в духе шаблона, на базе которого строятся другие элементы структуры кода.

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

Поэтому мы можем создать класс – то есть объект, способный стать базой для других объектов. Например, класс – персонаж. Он умеет ходить, драться, имеет характеристики наподобие уровня здоровья или количества маны, то есть атрибуты, что есть у любых рас и классов в нашей РПГ. А уже человек-воин (объект) с ником Nagibator777 будет содержать более специфичные характеристики и методы, зависящие от решений игрока и других внешних факторов. Класс – это пример абстракции и наследования, упрощающий генерацию новых объектов.

Пример структуры в ООП на базе персонажа в игре

На картинках и схемах эта структура выглядит куда понятнее.

Ключевые принципы ООП

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

Инкапсуляция

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

Данные конкретного объекта или класса хранятся в пределах этого объекта или класса. Вносить в них изменения, используя другие классы, нельзя. У окружения есть право только запрашивать «публичные» методы и атрибуты.

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

Наследование

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

Пример создания класса в JS

Это проще понять на примере со средствами передвижения:

Берем абстрактный класс «Средство передвижения» с возможностью набирать скорость и перевозить людей.

Из него формируем подкласс «Автобус», наследующий базовые характеристики и уточняющий их определенным количеством мест для людей и пределом скорости.

Затем создаем объект «Икарус» с более конкретной скоростью, планировкой, количеством дверей, типом сигнала и другими специфичными параметрами.

Не нужно каждый раз создавать новый класс или объект с полным набором опций. Достаточно воспользоваться конструкцией в духе export class Bus extends Vehicle() и дополнить код конкретикой.

Абстракция

Каждый верхний слой над объектом (классы) более абстрактный, чем его «младшая версия». Это позволяет не переписывать по 10 раз один и тот же объект, указывая одни и те же атрибуты и методы. Напротив, абстрактные классы позволяют создавать все более конкретные классы и вытекающие из них объекты, не описывая реализацию функций заранее (в этом и суть абстракции), а оставляя исключительно базовый шаблон для дальнейших надстроек.

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

Полиморфизм

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

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

Преимущества ООП

Основными преимуществами парадигмы разработчики считают следующие особенности:

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

Реюзабельность кода: благодаря абстракциям, полиморфизму и наследованиям можно не писать один и тот же код много раз, что заметно ускоряет создание нового ПО.

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

Расширяемость: ООП-код легче развивать, дополнять и менять. Этому способствует независимая модульная структура.

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

Безопасность: инкапсулированный код недоступен извне, поэтому «поломать» ООП-программу сложнее.

Гибкость: полиморфизм позволяет быстро адаптировать ООП-код под свои нужды, не описывая новые функции и объекты.

Недостатки ООП

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

Языки, исповедующие объектно-ориентированную парадигму

Существует множество языков программирования, подходящих для применения ООП-парадигмы. Среди них:

Ruby – высокоуровневый язык с динамической типизацией, созданный в середине 90-х японским разработчиком Юкихиро Мацумото.

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

JavaScript – популярный язык с динамической типизацией, одинаково хорошо подходящий для различных парадигм разработки, включая ООП.

Вместо заключения

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

Почти все популярные языки программирования являются объектно-ориентированными. В таблице приведены данные о популярности языков (рейтинг TIOBE) за сентябрь 2019 года [1]:


В этой статье-учебнике:

    развития языков программирования, объясняющие существующую систему ценностей;
  1. на примерах показаны механизмы ООП и их роль в обеспечении ценностей;
  2. приведены аннотации на материалы для дальнейшего изучения.

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

1 Тенденции и ценности

1.1 Повышение уровня абстракции. Инкапсуляция

Говоря о том, что оператор new , файл, сокет или поток является хорошей абстракцией мы имеет ввиду, что он обеспечивает хорошую инкапсуляцию.

1.2 Безопасность и расширяемость

Безопасность осуществляется за счет предоставления хороших абстракций. В качестве примера давайте сравним функцию создания окна, предоставляемую Windows API и библиотекой Qt:

void CreateWindowA(lpClassName, lpWindowName, dwStyle, x, y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam);

Функцию CreateWindowA очень сложно использовать (от нас требуют 11 параметров), при этом легко допустить ошибку, например:

QWidget::QWidget(QWidget *parent, Qt::WindowFlags f);

2 Механизмы

2.1 Модульность

  • предоставляющий удобный интерфейс, максимально скрывающий (инкапсулирующий) детали реализации;
  • максимально независимый от других модулей. Позволяющий повторное использование (по возможности).

Модульность является базовым принципом в программировании (не является особенностью ООП).

2.2 Композиция и наследование

При выборе отношений между своими классами можно руководствоваться следующим базовым правилом:

Нужно учитывать, что наследование обеспечивает лучшую инкапсуляцию (за счет секции protected ), однако является более сильным видом отношения, т.к. такая зависимость устанавливается при компиляции и ее нельзя изменить, в то время как зависимость по включению одного класса может быть заменено на зависимость от другого (см. композиция и аргегация [8]).

2.3 Полиморфизм

Без использования полиморфизма можно накидать примерно такой код:


На вход функции hit , под видом указателя на базовый класс, могут поступать любые Unit-ы (наследники), но это еще не совсем полиморфизм. Используется наш код так:

Мы хотим прописать интерфейс (набор функций) для базового класса и по-разному реализовать его в классах-наследниках. Мы уже знаем, что любой объект (например, выбранный пользователем с помощью мыши) мы можем привести к типу базового класса. Полиморфизм позволяет нам вызвать для нашего объекта функцию, определенную в этом интерфейсе, да так, что выполнится функция в соответствии с реальным типом объекта. То есть:

Полиморфизм = наследование + виртуальные функции.

Читайте также: