Средства объектно ориентированного программирования. Объектно-ориентированные языки

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

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

Процедурные языки

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

Разделение на функции

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

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

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

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

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

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

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

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

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

Неограниченный доступ

В программе, написанной, например, на C, есть два вида данных. Локальные скрыты внутри функции и другими процедурами не используются.

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

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

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

Например, в программе учета кто-то решит, что код учитываемого предмета должен состоять не из 5 цифр, а из 12. Это потребует изменить с short на long. Теперь связанные с кодом функции должны быть изменены для работы с новым форматом.

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

Моделирование реального мира

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

Атрибуты

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

Поведение

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

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

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

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

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

Сегодня наиболее широко используемый программирование) - C++ (плюс-плюс). В Java отсутствуют некоторые функции, такие как указатели, шаблоны и множественное наследование, что делает его менее мощным и универсальным, чем C++. C# еще не достиг популярности C++.

Следует отметить, что так называемые функции-члены в C++ называются методами в некоторых других ОО-языках, таких как Smalltalk. Элементы данных называются атрибутами. Вызов метода объекта является посылкой ему сообщения.

Аналогия

Можно представить объекты отделами компании. В большинстве организаций сотрудники не работают один день с кадрами, на следующий начисляя зарплату, а затем неделю занимаясь розничной торговлей. У каждого отдела есть свой персонал с четко возложенными на него обязанностями. Есть и собственные данные: показатели заработной платы, продаж, учет сотрудников и т. д. Люди в отделах работают со своей информацией. Разделение компании, таким образом, облегчает контроль за ее деятельностью и поддерживает целостность данных. Бухгалтерия отвечает за Если необходимо знать общую сумму заработной платы, выплачиваемой в южном филиале в июле, не нужно рыться в архиве. Достаточно направить записку ответственному лицу, подождать, пока этот человек получит доступ к данным и отправит ответ с требуемой информацией. Это гарантирует соответствие регламенту и отсутствие постороннего вмешательства. Таким же образом объект в ООП обеспечивает организацию приложения.

Следует помнить, что ориентация на объекты не касается подробностей работы программы. Большинство инструкций C++ соответствует операторам процедурных языков, таких как С. Действительно, функции-члены в C++ очень похожи на функции в С. Только более широкий контекст позволит установить, является ли инструкция процедурной или объектно-ориентированной.

Объект в ООП: определение

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

Какие вещи становятся объектами в ООП? Ниже представлены типичные категории.

Физический объект в ООП - это:

  • транспорт в моделях движения потока;
  • электрические элементы в программах схемотехники;
  • страны в модели экономики;
  • самолет в системе управления воздушным движением.

Элементы среды компьютера пользователя:

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

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

Класс

Объекты в ООП - это члены классов. Что это значит? Языки программирования имеют встроенные типы данных. Тип int, т. е. целое число, предопределен в C++. Можно объявлять сколько угодно переменных int.

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

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

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

В жизни классы разделены на подклассы. Например, животные делятся на земноводных, млекопитающих, птиц, насекомых и т. д.

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

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

Повторное использование

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

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

Создание новых типов данных

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

position1 = position + origin,

где и origin - пары независимых численных величин. Создание класса, включающего в себя эти два значения, и объявление переменных его объектами создает новый тип данных.

Полиморфизм, перегрузка

Операторы = (равно) и + (плюс), используемые в позиционной арифметике выше, не действуют так же, как с встроенными типами, такими как int. Объекты position и др. не предопределены, а заданы программным путем. Каким образом эти операторы знают, как с ними обращаться? Ответ заключается в том, что для них можно задать новые модели поведения. Эти операции будут функциями-членами класса Position.

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

Книга об ООП «Объектно-ориентированное программирование для чайников» позволит всем желающим ознакомиться с данной темой подробнее.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

$obj = new MyClass;

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

Var_dump($obj);

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

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

Object(MyClass)#1 (0) { }

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

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

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

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

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

Echo $obj->prop1;

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

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

prop1; // Выводим свойство?>

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

Свойство класса

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

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

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

prop1 = $newval; } public function getProperty() { return $this->prop1 . "
"; } } $obj = new MyClass; echo $obj->prop1; ?>

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

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

prop1 = $newval; } public function getProperty() { return $this->prop1 . "
"; } } $obj = new MyClass; echo $obj->getProperty(); // получаем значение свойства $obj->setProperty("Новое свойство."); // Устанавливаем новое значение echo $obj->getProperty(); // Снова читаем значение, чтобы увидеть изменения?>

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

Свойство класса Новое свойство

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

prop1 = $newval; } public function getProperty() { return $this->prop1 . "
"; } } // Создаем два объекта $obj = new MyClass; $obj2 = new MyClass; // Получаем значения $prop1 из обоих объектов echo $obj->getProperty(); echo $obj2->getProperty(); // Устанавливаем новые значения свойств для обоих объектов $obj->setProperty("Новое значение свойства"); $obj2->setProperty("Свойство принадлежит второму объекту"); // Выводим значения $prop1 для обоих echo $obj->getProperty(); echo $obj2->getProperty(); ?>

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

Свойство класса Свойство класса Новое значение свойства Свойство принадлежит второму объекту

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

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

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

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

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

Для иллюстрации концепции, добавим конструктор к классу MyClass. Он будет выводить сообщение при создании нового объекта класса:

"; } public function setProperty($newval) { $this->prop1 = $newval; } public function getProperty() { return $this->prop1 . "
"; } } // Создаем новый объект $obj = new MyClass; // Получаем значение свойства $prop1 echo $obj->
"; ?>

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

Создан объект класса "MyClass"! Свойство класса Конец файла.

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

Выведем сообщение, когда будет удаляться объект класса, с помощью магического метода:
__destruct() in MyClass:

"; } public function __destruct() { echo "Объект класса "", __CLASS__, "" удален.
"; } public function setProperty($newval) { $this->prop1 = $newval; } public function getProperty() { return $this->prop1 . "
"; } } // Создаём новый объект $obj = new MyClass; // Получаем значение свойства $prop1 echo $obj->getProperty(); // Выводим сообщение о достижении конца файла echo "Конец файла.
"; ?>

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

Создан объект класса "MyClass"! Свойство класса Конец файла. Объект класса "MyClass" удален.

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

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


"; } public function setProperty($newval) { $this->prop1 = $newval; } public function getProperty() { return $this->prop1 . "
"; } } // Создаем новый объект $obj = new MyClass; // Получаем значение свойства $prop1 echo $obj->getProperty(); // Удаляем объект unset($obj); // Выводим сообщение о достижении конца файла echo "Конец файла.
"; ?>

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

Создан объект класса "MyClass"! Свойство класса Объект класса "MyClass" удален. Конец файла.

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

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

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

"; } public function __destruct() { echo "Объект классса "", __CLASS__, "" удален.
"; } public function setProperty($newval) { $this->prop1 = $newval; } public function getProperty() { return $this->prop1 . "

"; ?>

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

Создан объект класса "MyClass"! Catchable fatal error: Object of class MyClass could not be converted to string in /Applications/XAMPP/xamppfiles/htdocs/testing/test.php on line 40

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

"; } public function __destruct() { echo "Объект классса "", __CLASS__, "" удален.
"; } public function __toString() { echo "Используем метод toString: "; return $this->getProperty(); } public function setProperty($newval) { $this->prop1 = $newval; } public function getProperty() { return $this->prop1 . "
"; } } // Создаем новый объект $obj = new MyClass; // Выводим объект как строку echo $obj; // Удаляем объект unset($obj); // Выводим сообщение о достижении конца файла echo "Конец файла.
"; ?>

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

Создан объект класса "MyClass"! Используем метод toString: Свойство класса Объект класса "MyClass" удален. Конец файла.

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

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

"; } public function __destruct() { echo "Объект классса "", __CLASS__, "" удален.
"; } public function __toString() { echo "Используем метод toString: "; return $this->getProperty(); } public function setProperty($newval) { $this->prop1 = $newval; } public function getProperty() { return $this->prop1 . "
"; } } class MyOtherClass extends MyClass { public function newMethod() { echo "Из нового метода класса " . __CLASS__ . ".
"; } } // Создаем новый объект $newobj = new MyOtherClass; // Используем новый метод echo $newobj->newMethod(); // Используем метод из родительского класса echo $newobj->getProperty(); ?>

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

Создан объект класса "MyClass"! Из нового метода класса "MyOtherClass". Свойство класса Объект класса "MyClass" удален.

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

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

"; } public function __destruct() { echo "Объект классса "", __CLASS__, "" удален.
"; } public function __toString() { echo "Используем метод toString: "; return $this->getProperty(); } public function setProperty($newval) { $this->prop1 = $newval; } public function getProperty() { return $this->prop1 . "
"; } } class MyOtherClass extends MyClass { public function __construct() { echo "Новый конструктор в классе " . __CLASS__ . ".

"; } } // Создаем новый объект $newobj = new MyOtherClass; // Выводим объект как строку echo $newobj->newMethod(); // Используем метод из родительского класса echo $newobj->getProperty(); ?>

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

Новый конструктор в классе "MyOtherClass". Из нового метода класса "MyOtherClass". Свойство класса Объект класса "MyClass" удален.

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

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

"; } public function __destruct() { echo "Объект классса "", __CLASS__, "" удален.
"; } public function __toString() { echo "Используем метод toString: "; return $this->getProperty(); } public function setProperty($newval) { $this->prop1 = $newval; } public function getProperty() { return $this->prop1 . "
"; } } class MyOtherClass extends MyClass { public function __construct() { parent::__construct(); // Вызываем конструктор родительского класса echo "Новый конструктор в классе " . __CLASS__ . ".
"; } public function newMethod() { echo "Из нового метода класса " . __CLASS__ . ".
"; } } // Создаем новый объект $newobj = new MyOtherClass; // Выводим объект как строку echo $newobj->newMethod(); // Используем метод из родительского класса echo $newobj->getProperty(); ?>

Выше приведённый код при выполнении выведет на экран сообщения из обеих конструкторов нового и родительского класса:

Создан объект класса "MyClass"! Новый конструктор в классе "MyOtherClass". Из нового метода класса "MyOtherClass". Свойство класса Объект класса "MyClass" удален.

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

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

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

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

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

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

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

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

"; } public function __destruct() { echo "Объект классса "", __CLASS__, "" удален.
"; } public function __toString() { echo "Используем метод toString: "; return $this->getProperty(); } public function setProperty($newval) { $this->prop1 = $newval; } protected function getProperty() { return $this->prop1 . "

"; } public function newMethod() { echo "Из нового метода класса " . __CLASS__ . ".
"; } } // Создаем новый объект $newobj = new MyOtherClass; // Пробуем вызвать защищенный метод echo $newobj->getProperty(); ?>

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

Создан объект класса "MyClass"! Новый конструктор в классе "MyOtherClass". Fatal error: Call to protected method MyClass::getProperty() from context "" in /Applications/XAMPP/xamppfiles/htdocs/testing/test.php on line 55

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

"; } public function __destruct() { echo "Объект классса "", __CLASS__, "" удален.
"; } public function __toString() { echo "Используем метод toString: "; return $this->getProperty(); } public function setProperty($newval) { $this->prop1 = $newval; } protected function getProperty() { return $this->prop1 . "
"; } } class MyOtherClass extends MyClass { public function __construct() { parent::__construct(); echo "Новый конструктор в классе " . __CLASS__ . ".
"; } public function newMethod() { echo "Из нового метода класса " . __CLASS__ . ".
"; } public function callProtected() { return $this->getProperty(); } } // Создаём новый объект $newobj = new MyOtherClass; // Вызываем защищённый метод из общего метода echo $newobj->callProtected(); ?>

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

Создан объект класса "MyClass"! Новый конструктор в классе "MyOtherClass". Свойство класса Объект класса "MyClass" удален.

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

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

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

"; } public function __destruct() { echo "Объект классса "", __CLASS__, "" удален.
"; } public function __toString() { echo "Используем метод toString: "; return $this->getProperty(); } public function setProperty($newval) { $this->prop1 = $newval; } private function getProperty() { return $this->prop1 . "
"; } } class MyOtherClass extends MyClass { public function __construct() { parent::__construct(); echo "Новый конструктор в классе " . __CLASS__ . ".
"; } public function newMethod() { echo "Из нового метода класса " . __CLASS__ . ".
"; } public function callProtected() { return $this->getProperty(); } } // Создаём новый объект $newobj = new MyOtherClass; // Используем метод из родительского класса echo $newobj->callProtected(); ?>

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

Создан объект класса "MyClass"! Новый конструктор в классе "MyOtherClass". Fatal error: Call to private method MyClass::getProperty() from context "MyOtherClass" in /Applications/XAMPP/xamppfiles/htdocs/testing/test.php on line 49

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

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

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

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

"; } public function __destruct() { echo "Объект классса "", __CLASS__, "" удален.
"; } public function __toString() { echo "Используем метод toString: "; return $this->getProperty(); } public function setProperty($newval) { $this->prop1 = $newval; } private function getProperty() { return $this->prop1 . "
"; } public static function plusOne() { return "count = " . ++self::$count . ".
"; } } class MyOtherClass extends MyClass { public function __construct() { parent::__construct(); echo "Новый конструктор в классе " . __CLASS__ . ".
"; } public function newMethod() { echo "Из нового метода класса " . __CLASS__ . ".
"; } public function callProtected() { return $this->getProperty(); } } do { // Вызываем plusOne без инициации класса MyClass echo MyClass::plusOne(); } while (MyClass::$count < 10); ?>

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

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

Count = 1. count = 2. count = 3. count = 4. count = 5. count = 6. count = 7. count = 8. count = 9. count = 10.

(как расшифровывается ООП) – это, прежде всего, парадигма программирования.
Парадигма программирования определяет то, как программист видит выполнение программы.
Так, для парадигмы ООП характерно, что программист рассматривает программу в виде набора взаимодействующих объектов, в то время как, например, в функциональном программировании программа представляется в виде последовательности вычисления функций. Процедурное программирование или, как его еще правильно называют, классическое операциональное, подразумевает написание алгоритма для решения задачи; при этом ожидаемые свойства конечного результата не описываются и не указываются. Структурное программирование в основном придерживается тех же принципов, что и процедурное, лишь немного дополняя их полезными приемами.
Парадигмы непроцедурного программирования, к которым можно отнести объектно-ориентированную парадигму, имеют совершенно другие идеи.
Определение Гради Буча гласит: “Объектно-ориентированное программирование – это методология программирования, которая основана на представлении программы в виде совокупности объектов, каждый из которых является реализацией определенного класса (типа особого вида), а классы образуют иерархию на принципах наследуемости”.
Структурное и объектно-ориентированное программирование строятся на таком научном методе как декомпозиция — метод, который использует структуру задачи и позволяет разбить решение общей большой задачи на решение последовательности меньших задач. Декомпозиция ООП происходит не по алгоритмам, а по объектам, использующимся при решении задачи. Данная декомпозиция уменьшает размер программных систем благодаря повторному использованию общих механизмов. Известно, что системы визуального программирования или системы, построенные на принципах объектно-ориентированного программирования, являются более гибкими и легче эволюционируют со временем.

История развития ООП берет свое начало в конце 60-х годов. Первым объектно-ориентированным языком был язык программирования Simula, созданный в компьютерном центре в Норвегии. Язык предназначался для моделирования ситуаций реального мира. Особенностью Simula было то, что программа, написанная на языке, была организована по объектам программирования. Объекты имели инструкции, называемые методами, и данные, которые назывались переменными; методы и данные определяли поведение объекта. В процессе моделирования объект вел себя согласно своему стандартному поведению и, в случае необходимости, изменял данные для отражения влияния назначенного ему действия.

Сегодня существует достаточное количество объектно-ориентированных языков программирования , наиболее популярными из которых в настоящее время являются C++, Delphi, Java, Visual Basic, Flash. Но, кроме того, многие языки, которые принято причислять к процедурной парадигме, тоже обладают свойствами ООП, имея возможность работать с объектами. Так, объектно-ориентированное программирование в C — это большой раздел программирования на данном языке, то же самое касается ООП в python и многих других структурных языках.

Говоря об ООП, часто всплывает еще одно определение — визуальное программирование . Оно дополнительно предоставляет широкие возможности использования прототипов объектов, которые определяются как классы объектов.
События. Во многих средах визуального программирования реализована характеристика (помимо инкапсуляции, полиморфизма и наследования) объекта – событие. Событиями в объектно-ориентированном программировании называется возможность обработки так называемых сообщений (или событий), получаемых от операционной системы Windows или самой программы. Данный принцип характерен для всех компонентов среды, которые обрабатывают различные события, возникающие в процессе выполнения программы. По сути, событие — это некоторое действие, которое активизирует стандартную реакцию объекта. Событием может рассматриваться, например, щелчок по кнопке мыши, наведение курсора мыши на пункт меню, открытие вкладки и т.п. Очередность выполнения тех или иных действий определяется как раз таки событиями, возникающими в системе, и реакцией на них объектов.
Классы и объекты в ООП — различные понятия. Понятие класса в ООП – это тип данных (такой же как, например, Real или String), а объект – конкретный экземпляр класса (его копия), хранящийся в памяти компьютера как переменная соответствующего типа.
Класс является структурным типом данных. Класс включает описание полей данных, а также процедур и функций, которые работают с этими полями данных. Метод ООП – это и есть такие процедуры и функции применительно к классам.
Классы имеют поля (как тип данных запись — record), свойства, которые похожи на поля, но имеют дополнительные описатели, определяющие механизмы записи и считывания данных и методы — подпрограммы, которые направленны на изменение полей и свойств класса.

Основные принципы ООП

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

Принцип наследования ООП и подклассы

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

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

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

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

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

Две методики

Все компьютерные программы состоят из двух элементов: кода и данных. Более того, программа концептуально может быть организована вокруг своего кода или своих данных. Иными словами, организация одних программ определяется тем, “что происходит”, а других - тем, “на что оказывается влияние”. Существуют две методики создания программ. Первая из них называется моделью, ориентированной на процессы и характеризует программу как последовательность линейных шагов (т.е. кода). Модель, ориентированную на процессы, можно рассматривать в каче­стве кода, воздействующего на данные. Такая модель довольно успешно применяется в процедурных языках вроде С. Но, как отмечалось в главе 1, подобный подход по­рождает ряд трудностей в связи с увеличением размеров и сложности программ.

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

Абстракция

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

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

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

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

Три принципа ООП

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

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

Механизм, связывающий код и данные, которыми он манипулирует, защищая оба эти компонента от внешнего вмешательства и злоупотреблений, называется инкап­суляцией. Инкапсуляцию можно считать защитной оболочкой, которая предохраня­ет код и данные от произвольного доступа со стороны другого кода, находящегося снаружи оболочки. Доступ к коду и данным, находящимся внутри оболочки, строго контролируется тщательно определенным интерфейсом. Чтобы провести анало­гию с реальным миром, рассмотрим автоматическую коробку передач автомобиля. Она инкапсулирует немало сведений об автомобиле, в том числе величину ускоре­ния, крутизну поверхности, по которой совершается движение, а также положение рычага переключения скоростей. Пользователь (в данном случае водитель) может оказывать влияние на эту сложную инкапсуляцию только одним способом: переме­щая рычаг переключения скоростей. На коробку передач нельзя воздействовать, например, с помощью индикатора поворота или дворников. Таким образом, рычаг переключения скоростей является строго определенным, а по существу, единствен­ным, интерфейсом с коробкой передач. Более того, происходящее внутри коробки передач не влияет на объекты, находящиеся вне ее. Например, переключение пере­дач не включает фары! Функция автоматического переключения передач инкапсу­лирована, и поэтому десятки изготовителей автомобилей могут реализовать ее как угодно. Но с точки зрения водителя все эти коробки передач работают одинаково. Аналогичный принцип можно применять и в программировании. Сильная сторона инкапсулированного кода состоит в следующем: всем известно, как получить доступ к нему, а следовательно, его можно использовать независимо о подробностей реали­зации и не опасаясь неожиданных побочных эффектов.

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

При создании класса определяются код и данные, которые образуют этот класс. Совместно эти элементы называются членами класса. В частности, определенные в классе данные называются перемени ымичленами, или переменными экземпляра, а код, оперирующий данными, - методами-членами, или просто методами. (То, что програм­мирующие на Java называют методами, программирующие на C/C++ называют функ­циями) В программах, правильно написанных на Java, методы определяют, каким об­разом используются переменные-члены. Это означает, что поведение и интерфейс класса определяются методами, оперирующими данными его экземпляра.

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

Рис. 1.

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

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

Как правило, большинство людей воспринимают окружающий мир в виде ие­рархически связанных между собой объектов, подобных животным, млекопитаю­щим и собакам. Если требуется привести абстрактное описание животных, можно сказать, что они обладают определенными свойствами: размеры, уровень интел­лекта и костная система. Животным присущи также определенные особенности поведения: они едят, дышат и спят. Такое описание свойств и поведения составля­ет определение класса животных.

Если бы потребовалось описать более конкретный класс животных, например млекопитающих, следовало бы указать более конкретные свойства, в частности тип зубов и молочных желез. Такое определение называется подклассом животных, которые относятся к суперклассу (родительскому классу) млекопитающих. А поскольку млекопитающие - лишь более точно определенные животные, то они на­следуют все свойства животных. Подкласс нижнего уровня иерархии классов наследу­ет все свойства каждого из его родительских классов (рис. 2).

Рис. 2.

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

Рис. 3. Лабрадор полностью наследует инкапсулированные свойства всех родительских классов животных

Полиморфизм

Полиморфизм (от греч. “много форм”) - это принцип ООП, позволяющий ис­пользовать один и тот же интерфейс для общего класса действий. Каждое действие зависит от конкретной ситуации. Рассмотрим в качестве примера стек, действую­щий как список обратного магазинного типа. Допустим, в программе требуются стеки трех типов: для целочисленных значений, для числовых значений с плаваю­щей точкой и для символов. Алгоритм реализации каждого из этих стеков остается неизменным, несмотря на отличия в данных, которые в них хранятся. В языке, не являющемся объектно-ориентированным, для обращения со стеком пришлось бы создавать три разных ряда служебных программ под отдельными именами. A ejava, благодаря принципу полиморфизма, для обращения со стеком можно определить общий ряд служебных программ под одними и теми же общими именами.

В более общем смысле принцип полиморфизма нередко выражается фразой “один интерфейс, несколько методов”. Это означает, что можно разработать об­щий интерфейс для группы связанных вместе действий. Такой подход позволяет уменьшить сложность программы, поскольку один и тот же интерфейс служит для указания общего класса действий. А выбор конкретного действия (т.е. метода) де­лается применительно к каждой ситуации и входит в обязанности компилятора. Это избавляет программиста от необходимости делать такой выбор вручную. Ему нужно лишь помнить об общем интерфейсе и правильно применять его.

Если продолжить аналогию с собаками, то можно сказать, что собачье обоня­ние - полиморфное свойство. Если собака почувствует запах кошки, она залает и погонится за ней. А если собака почувствует запах своего корма, то у нее нач­нется слюноотделение, и она поспешит к своей миске. В обоих случаях действует одно и то же чувство обоняния. Отличие лишь в том, что именно издает запах, т.е. в типе данных, воздействующих на нос собаки! Этот общий принцип можно реализовать, применив его к методам в программе на Java.

Совместное применение полиморфизма, инкапсуляции и наследования

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

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

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

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

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

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

Всем привет.

Неделя статей на хабре посвященная ООП. Последняя статья вызвала у меня кучу эмоций и, к сожалению, очень плохих эмоций. Мне очень не понравилась статья. Почему? Потому что в ней передаются какие-то отрицательные эмоции об использовании ООП. Эмоции вызваны лишь тем, что человек не до конца понимает всю силу ООП и хочет убедить всех в том что ООП зло. Самое печальное что люди начинают прислушиваться и кидаться ужасными доводами, не имеющими ничего общего с действительностью. Я думаю что студентам такие статьи противопоказаны больше чем GoF, которых я бы давал как можно раньше. :)

Начнем.

Что такое ООП. ООП - это и ОО программирование и проектирование. Одно без другого бессмысленно чуть более чем полностью. Создано ООП для проектирования/программирования программных продуктов. Не для моделирования процессов. Не для проектирования протоколов, а именно для программных продуктов, для их реализации. Для упрощения системы, которая будет реализовывать протокол или бизнес-процесс или что-то еще.

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

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

Как же эти инструменты работают? Да проще пареной репы, потому что это все основано на привычных нам вещах. Люблю простые примеры из жизни:

1. Наследование. Есть пекарь. Есть печь электрическая и газовая. Ваша задача смоделировать процесс приготовления пищи пекарем в каждой из печи. Решая задачу в лоб, у нас будет много дублирования кода из-за того, что сам процесс передачи пищи в печь и сама работа с печами идентичны для обеих печей. Но если мы включаем объектное мышление, и вспоминаем про инструмент наследование, то получаем примерно следующее (диаграмму лень рисовать, сорри):
Есть печь (абстрактная печь). У нее есть поведение - включить, выключить, увеличить или уменьшить температуру, положить чего-то, достать чего-то и состояние - температура в печи, включена или выключена. Это отличный пример абстрактного объекта в котором соблюдены принципы инкапсуляции (при реализации я их обязательно буду соблюдать). И есть пекарь, конкретный такой пекарь Иван. Он умеет работать с абстрактной печью. Т.е. смотреть температуру, включать выключать и т.д. вы поняли. Сила наследования в том, что нам не придется переписывать нашего Ивана для каждой из печей, будь то электро или газовая печь. Я думаю всем ясно почему? Получается что инструмент применен правильно.
2. Полиморфизм. Печи ведь по-разному работают. Газовая потребляет газ, электро печь - электричество. Используя полиморфизм мы легко меняем поведение в наследниках абстрактной печи.
3. Инкапсуляция. Основная фишка инкапсуляции в том, что я не должен знать, что происходит внутри моей печи. Допустим, я вызываю не метод включить печь, а меняю ее свойство включена на значение true. Что произойдет в этот момент? Если принцип инкапсуляции не соблюден, то я буду вынужден печи сказать начинай потреблять горючее, т.к. я тебя включил. Т.е. пекарь знает, что печь потребляет горючее, знает, как печь работает. Или, например, мы не можем установить температуру печи ниже или выше определенного уровня. Если не соблюдать принцип инкапсуляции, то мы должны будем говорить печи проверь-ка текущую температуру, пойдет те такая? Т.е. пекарь опять слишком много знает о печи. Геттеры и сеттеры это средства языка, которые помогут нам легко реализовать отслеживание изменений состояния. Все. Если геттеры и сеттеры пустые, значит так надо на моем уровне абстракции. Геттеры и сеттеры - не могут мешать реализации инкапсуляции, криво реализовать инкапсуляцию может проектировщик/программист.

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

Еще стали добавлять абстракцию, как еще один столп ООП. Я думаю, что это скорее верно, но уж очень попахивает КЭПом.

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

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

Отдельно про DTO. DTO - это паттерн. Он позволяет создать объект, который передаст информацию другому слою, другой системе, короче куда-то чего-то передаст. Почему он не может быть рассмотрен мною как объект для меня вообще загадка. Где противоречие то? Является контейнером только? Ну и что?? Это же объект в рамках рассмотренной мною объектной модели на заданном уровне абстракции, где DTO - объект и часть декомпозиции.

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

Еще говорят что некоторые вещи нельзя представить в виде объектов и их взаимодействия. Я уверен что это не так. Просто необходимо выбрать уровень абстракции верно. Будь то реализация протокола, слоя доступа к БД, подключения плагинов, менеджера задач, бизнес процесса, системы проектирования бизнес процессов т.е. все что угодно можно представить как объекты и их взаимодействие. Все можно реализовать как объекты и взаимодействие между ними. Хорошо это или плохо чаще всего зависит лишь от вашего умения мыслить объектно.

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

P.S. В комментах к прошлой статье я явно много перегибал палку при обращении к некоторым людям. Приношу свои извинения.