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

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

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

Энциклопедичный YouTube

    1 / 5

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

    ✪ Объектно-ориентированное проектирование, часть 1 - как проектируются классы

    ✪ Основные принципы объектно-ориентированного программирования. Что такое ООП и зачем оно нужно?

    ✪ Основы ООП в C++

    ✪ Объектно-ориентированное программирование. Классы и объекты. Урок 3

    Субтитры

Основные понятия

Абстракция данных Абстрагирование означает выделение значимой информации и исключение из рассмотрения незначимой. В ООП рассматривают лишь абстракцию данных (нередко называя её просто «абстракцией»), подразумевая набор значимых характеристик объекта, доступный остальной программе. Инкапсуляция Инкапсуляция - свойство системы, позволяющее объединить данные и методы, работающие с ними, в классе. Одни языки (например, С++ , Java или Ruby) отождествляют инкапсуляцию с сокрытием , но другие (Smalltalk , Eiffel , OCaml) различают эти понятия. Наследование Наследование - свойство системы, позволяющее описать новый класс на основе уже существующего с частично или полностью заимствующейся функциональностью. Класс, от которого производится наследование, называется базовым, родительским или суперклассом. Новый класс - потомком, наследником, дочерним или производным классом. Полиморфизм подтипов Полиморфизм подтипов (в ООП называемый просто «полиморфизмом») - свойство системы, позволяющее использовать объекты с одинаковым интерфейсом без информации о типе и внутренней структуре объекта. Другой вид полиморфизма - параметрический - в ООП называют обобщённым программированием . Класс Класс - универсальный, комплексный тип данных , состоящий из тематически единого набора «полей» (переменных более элементарных типов) и «методов» (функций для работы с этими полями), то есть он является моделью информационной сущности с внутренним и внешним интерфейсами для оперирования своим содержимым (значениями полей). В частности, в классах широко используются специальные блоки из одного или чаще двух спаренных методов, отвечающих за элементарные операции с определенным полем (интерфейс присваивания и считывания значения), которые имитируют непосредственный доступ к полю. Эти блоки называются «свойствами» и почти совпадают по конкретному имени со своим полем (например, имя поля может начинаться со строчной, а имя свойства - с заглавной буквы). Другим проявлением интерфейсной природы класса является то, что при копировании соответствующей переменной через присваивание, копируется только интерфейс, но не сами данные, то есть класс - ссылочный тип данных. Переменная-объект, относящаяся к заданному классом типу, называется экземпляром этого класса. При этом в некоторых исполняющих системах класс также может представляться некоторым объектом при выполнении программы посредством динамической идентификации типа данных . Обычно классы разрабатывают таким образом, чтобы обеспечить отвечающие природе объекта и решаемой задаче целостность данных объекта, а также удобный и простой интерфейс. В свою очередь, целостность предметной области объектов и их интерфейсов, а также удобство их проектирования, обеспечивается наследованием. Объект Сущность в адресном пространстве вычислительной системы, появляющаяся при создании экземпляра класса (например, после запуска результатов компиляции и связывания исходного кода на выполнение).

Классификация подвидов ООП

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

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

  • В мейнстримных языках декларируемые принципы нацелены на повышение изначально низкого для императивного программирования коэффициента повторного использования кода . В полиморфно типизированных применение концепций ООП, напротив, означает очевидное его снижение из-за перехода от параметрического полиморфизма к ad hoc полиморфизму . В динамически типизированных языках (Smalltalk , Python , Ruby) эти принципы используются для логической организации программы, и их влияние на коэффициент повторного использования трудно спрогнозировать - он сильно зависит от дисциплины программиста. Например, в CLOS мультиметоды одновременно являются функциями первого класса , что позволяет рассматривать их одновременно и как связанно квантифицированные , и как обобщённые (истинно полиморфные).
  • Традиционные ОО-языки используют номинативную типизацию , то есть допустимость соиспользования объектов разных классов только при условии явного указания родственных отношений между классами. Для полиморфно типизированных языков характерна структурная типизация , то есть согласование классов между собой тем же механизмом, что и согласование числа 5 с типом int . Динамически типизированные языки также занимают здесь промежуточную позицию.

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

История

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

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

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

Первым языком программирования, в котором были предложены основные понятия, впоследствии сложившиеся в парадигму, была Симула , но термин «объектная ориентированность» не использовался в контексте использования этого языка. В момент его появления в 1967 году в нём были предложены революционные идеи: объекты, классы, виртуальные методы и др., однако это всё не было воспринято современниками как нечто грандиозное. Фактически, Симула была «Алголом с классами», упрощающим выражение в процедурном программировании многих сложных концепций. Понятие класса в Симуле может быть полностью определено через композицию конструкций Алгола (то есть класс в Симуле - это нечто сложное, описываемое посредством примитивов).

Взгляд на программирование «под новым углом» (отличным от процедурного) предложили Алан Кэй и Дэн Ингаллс в языке Smalltalk . Здесь понятие класса стало основообразующей идеей для всех остальных конструкций языка (то есть класс в Смолтоке является примитивом, посредством которого описаны более сложные конструкции). Именно он стал первым широко распространённым объектно-ориентированным языком программирования .

В настоящее время количество прикладных языков программирования (список языков), реализующих объектно-ориентированную парадигму, является наибольшим по отношению к другим парадигмам. Наиболее распространённые в промышленности языки (С++, Delphi, C#, Java и др.) воплощают объектную модель Симулы. Примерами языков, опирающихся на модель Смолтока, являются Objective-C, Python, Ruby.

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

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

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

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

Сложности определения

ООП имеет уже более чем сорокалетнюю историю, но, несмотря на это, до сих пор не существует чёткого общепринятого определения данной технологии . Основные принципы, заложенные в первые объектные языки и системы, подверглись существенному изменению (или искажению) и дополнению при многочисленных реализациях последующего времени. Кроме того, примерно с середины 1980-х годов термин «объектно-ориентированный» стал модным , в результате с ним произошло то же самое, что несколько раньше с термином «структурный» (ставшим модным после распространения технологии структурного программирования) - его стали искусственно «прикреплять» к любым новым разработкам, чтобы обеспечить им привлекательность. Бьёрн Страуструп в 1988 году писал, что обоснование «объектной ориентированности» чего-либо, в большинстве случаев, сводится к ложному силлогизму : «X - это хорошо. Объектная ориентированность - это хорошо. Следовательно , X является объектно-ориентированным».

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

Однако общность механизма обмена сообщениями имеет и другую сторону - «полноценная» передача сообщений требует дополнительных накладных расходов, что не всегда приемлемо. Поэтому во многих современных объектно-ориентированных языках программирования используется концепция «отправка сообщения как вызов метода» - объекты имеют доступные извне методы, вызовами которых и обеспечивается взаимодействие объектов. Данный подход реализован в огромном количестве языков программирования, в том числе C++ , Object Pascal , Java , Oberon-2 . Однако, это приводит к тому, что сообщения уже не являются самостоятельными объектами, и, как следствие, не имеют атрибутов, что сужает возможности программирования. Некоторые языки используют гибридное представление, демонстрируя преимущества одновременно обоих подходов - например, CLOS , Python .

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

Особенности реализации

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

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

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

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

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

Контроль доступа Поскольку методы класса могут быть как чисто внутренними, обеспечивающими логику функционирования объекта, так и внешними, с помощью которых взаимодействуют объекты, необходимо обеспечить скрытость первых при доступности извне вторых. Для этого в языки вводятся специальные синтаксические конструкции, явно задающие область видимости каждого члена класса. Традиционно это модификаторы public, protected и private, обозначающие, соответственно, открытые члены класса, члены класса, доступные внутри класса и из классов-потомков, и скрытые, доступные только внутри класса. Конкретная номенклатура модификаторов и их точный смысл различаются в разных языках. Методы доступа Поля класса в общем случае не должны быть доступны извне, поскольку такой доступ позволил бы произвольным образом менять внутреннее состояние объектов. Поэтому поля обычно объявляются скрытыми (либо язык в принципе не позволяет обращаться к полям класса извне), а для доступа к находящимся в полях данным используются специальные методы, называемые методами доступа. Такие методы либо возвращают значение того или иного поля, либо производят запись в это поле нового значения. При записи метод доступа может проконтролировать допустимость записываемого значения и, при необходимости, произвести другие манипуляции с данными объекта, чтобы они остались корректными (внутренне согласованными). Методы доступа называют ещё аксессорами (от англ. access - доступ), а по отдельности - геттерами (англ. get - чтение) и сеттерами (англ. set - запись) . Свойства объекта Псевдополя, доступные для чтения и/или записи. Свойства внешне выглядят как поля и используются аналогично доступным полям (с некоторыми исключениями), однако фактически при обращении к ним происходит вызов методов доступа. Таким образом, свойства можно рассматривать как «умные» поля данных, сопровождающие доступ к внутренним данным объекта какими-либо дополнительными действиями (например, когда изменение координаты объекта сопровождается его перерисовкой на новом месте). Свойства, по сути, не более чем синтаксический сахар , поскольку никаких новых возможностей они не добавляют, а лишь скрывают вызов методов доступа. Конкретная языковая реализация свойств может быть разной. Например, в объявление свойства непосредственно содержит код методов доступа, который вызывается только при работе со свойствами, то есть не требует отдельных методов доступа, доступных для непосредственного вызова. В Delphi объявление свойства содержит лишь имена методов доступа, которые должны вызываться при обращении к полю. Сами методы доступа представляют собой обычные методы с некоторыми дополнительными требованиями к сигнатуре .

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

Проектирование программ в целом

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

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

  • Из каких частей состоит система ;
  • В чём состоит ответственность каждой из ее частей .

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

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

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

Различные ООП-методологии

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

Компонентное программирование

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

Прототипное программирование

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

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

Класс-ориентированное программирование

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

Производительность объектных программ

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

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

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

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

Критика ООП

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

Критические высказывания в адрес ООП:

  • Было показано отсутствие значимой разницы в продуктивности разработки программного обеспечения между ООП и процедурным подходом .
  • Кристофер Дэйт указывает на невозможность сравнения ООП и других технологий во многом из-за отсутствия строгого и общепризнанного определения ООП .
  • Александр Степанов в одном из своих интервью указывал, что ООП «методологически неправильно» и что «…ООП практически такая же мистификация, как и искусственный интеллект…» .
  • Фредерик Брукс указывает, что наиболее сложной частью создания программного обеспечения является «…спецификация, дизайн и тестирование концептуальных конструкций, а отнюдь не работа по выражению этих концептуальных конструкций…». ООП (наряду с такими технологиями как искусственный интеллект , верификация программ, автоматическое программирование, графическое программирование , экспертные системы и др.), по его мнению, не является «серебряной пулей», которая могла бы на порядок величины снизить сложность разработки программных систем. Согласно Бруксу, «…ООП позволяет сократить только привнесённую сложность в выражение дизайна. Дизайн остаётся сложным по своей природе…» .
  • Эдсгер Дейкстра указывал: «…то, о чём общество в большинстве случаев просит - это эликсир от всех болезней. Естественно, "эликсир" имеет очень впечатляющие названия, иначе будет очень трудно что-то продать: „Структурный анализ и Дизайн“, „Программная инженерия“, „Модели зрелости“, „Управляющие информационные системы“ (Management Information Systems), „Интегрированные среды поддержки проектов“, „Объектная ориентированность“, „Реинжиниринг бизнес-процессов “…» .
  • Никлаус Вирт считает, что ООП - не более чем тривиальная надстройка над структурным программированием, и преувеличение её значимости, выражающееся, в том числе, во включении в языки программирования всё новых модных «объектно-ориентированных» средств, вредит качеству разрабатываемого программного обеспечения.
  • Патрик Киллелиа в своей книге «Тюнинг веб-сервера» писал: «…ООП предоставляет вам множество способов замедлить работу ваших программ…».
  • Известная обзорная статья проблем современного ООП-программирования перечисляет некоторые типичные проблемы ООП [ ] .
  • В программистском фольклоре получила широкое распространение критика объектно-ориентированного подхода в сравнении с функциональным подходом с использованием метафоры «Королевства Существительных » из эссе Стива Йегги .

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

Критика рекламы ООП Критикуется явно высказываемое или подразумеваемое в работах некоторых пропагандистов ООП, а также в рекламных материалах «объектно-ориентированных» средств разработки представление об объектном программировании как о некоем всемогущем подходе, который магическим образом устраняет сложность программирования. Как замечали многие, в том числе упомянутые выше Брукс и Дейкстра, «серебряной пули не существует» - независимо от того, какой парадигмы программирования придерживается разработчик, создание нетривиальной сложной программной системы всегда сопряжено со значительными затратами интеллектуальных ресурсов и времени. Из наиболее квалифицированных специалистов в области ООП никто, как правило, не отрицает справедливость критики этого типа. Оспаривание эффективности разработки методами ООП Критики оспаривают тезис о том, что разработка объектно-ориентированных программ требует меньше ресурсов или приводит к созданию более качественного ПО. Проводится сравнение затрат на разработку разными методами, на основании которого делается вывод об отсутствии у ООП преимуществ в данном направлении. Учитывая крайнюю сложность объективного сравнения различных разработок, подобные сопоставления, как минимум, спорны. С другой стороны, получается, что ровно так же спорны и утверждения об эффективности ООП. Производительность объектно-ориентированных программ Указывается на то, что целый ряд «врождённых особенностей» ООП-технологии делает построенные на её основе программы технически менее эффективными, по сравнению с аналогичными необъектными программами. Не отрицая действительно имеющихся дополнительных накладных расходов на организацию работы ООП-программ (см. раздел «Производительность» выше), нужно, однако, отметить, что значение снижения производительности часто преувеличивается критиками. В современных условиях, когда технические возможности компьютеров чрезвычайно велики и постоянно растут, для большинства прикладных программ техническая эффективность оказывается менее существенна, чем функциональность, скорость разработки и сопровождаемость. Лишь для некоторого, очень ограниченного класса программ (ПО встроенных систем, драйверы устройств, низкоуровневая часть системного ПО, научное ПО) производительность остаётся критическим фактором. Критика отдельных технологических решений в ООП-языках и библиотеках Эта критика многочисленна, но затрагивает она не ООП как таковое, а приемлемость и применимость в конкретных случаях тех или иных реализаций её механизмов. Одним из излюбленных объектов критики является язык C++, входящий в число наиболее распространённых промышленных ООП-языков.

Объектно-ориентированные языки

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

Как правило, объектно-ориентированный язык (ООЯ) содержит следующий набор элементов:

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

Некоторые языки добавляют к указанному минимальному набору те или иные дополнительные средства. В их числе:

  • Конструкторы, деструкторы, финализаторы;
  • Свойства (аксессоры);
  • Индексаторы;
  • Средства управления видимостью компонентов классов (интерфейсы или модификаторы доступа, такие как public, private, protected, feature и др.).

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

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

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

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


Об объектах и классах

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

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

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

/** * Класс Треугольник. */ class Triangle { /** * Специальный метод, называемый конструктор класса. * Принимает на вход три параметра: * длина стороны А, длина стороны Б, * угол между этими сторонами(в градусах) */ Triangle(double sideA, double sideB, double angleAB) { this.sideA = sideA; this.sideB = sideB; this.angleAB = angleAB; } double sideA; //Поле класса, хранит значение стороны А в описываемом треугольнике double sideB; //Поле класса, хранит значение стороны Б в описываемом треугольнике double angleAB; //Поле класса, хранит угла(в градусах) между двумя сторонами в описываемом треугольнике /** * Метод класса, который рассчитывает площадь треугольника */ double getSquare() { double square = this.sideA * this.sideB * Math.sin(this.angleAB * Math.PI / 180); return square; } /** * Метод класса, который рассчитывает периметр треугольника */ double getPerimeter() { double sideC = Math.sqrt(Math.pow(this.sideA, 2) + Math.pow(this.sideB, 2) - 2 * this.sideA * this.sideB * Math.cos(this.angleAB * Math.PI / 180)); double perimeter = this.sideA + this.sideB + sideC; return perimeter; } }

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

/** * Именно в этом месте запускается программа */ public static void main(String args) { //Значения 5, 17, 35 попадают в конструктор класса Triangle Triangle triangle1 = new Triangle(5, 17, 35); System.out.println("Площадь треугольника1: "+triangle1.getSquare()); System.out.println("Периметр треугольника1: "+triangle1.getPerimeter()); //Значения 6, 8, 60 попадают в конструктор класса Triangle Triangle triangle2 = new Triangle(6, 8, 60); System.out.println("Площадь треугольника1: "+triangle2.getSquare()); System.out.println("Периметр треугольника1: "+triangle2.getPerimeter()); }

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

Public static void main(String args)

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

Triangle triangle1 = new Triangle(5, 17, 35);

Здесь мы создаем экземпляр triangle1 класса Triangle и тут же задаем ему параметры сторон и угла между ними. При этом, вызывается специальный метод, называемый конструктор и заполняет поля объекта переданными значениями в конструктор. Ну, а строки

System.out.println("Площадь треугольника1: "+triangle1.getSquare()); System.out.println("Периметр треугольника1: "+triangle1.getPerimeter());

выводят рассчитанные площадь треугольника и его периметр в консоль.

Аналогично все происходит и для второго экземпляра класса Triangle .

Понимание сути классов и конструирования конкретных объектов - это уверенный первый шаг к пониманию методологии ООП.

Еще раз, самое важное:

ООП - это способ организации кода программы;

Класс - это пользовательская структура данных, которая воедино объединяет данные и функции для работы с ними(поля класса и методы класса);

Объект - это конкретный экземпляр класса, полям которого заданы конкретные значения.


Три волшебных слова

ООП включает три ключевых подхода: наследование, инкапсуляцию и полиморфизм. Для начала, приведу определения из wikipedia :

Инкапсуляция - свойство системы, позволяющее объединить данные и методы, работающие с ними, в классе. Некоторые языки (например, С++) отождествляют инкапсуляцию с сокрытием, но большинство (Smalltalk, Eiffel, OCaml) различают эти понятия.

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

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

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


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

/** * Класс точки. Базовый класс */ class Point { /** * Пустой конструктор */ Point() {} /** * Метод класса, который рассчитывает площадь фигуры */ double getSquare() { return 0; } /** * Метод класса, который рассчитывает периметр фигуры */ double getPerimeter() { return 0; } /** * Метод класса, возвращающий описание фигуры */ String getDescription() { return "Точка"; } }

У получившегося класса Point пустой конструктор, поскольку в данном примере мы работаем без конкретных координат, а оперируем только параметрами значениями сторон. Так как у точки нет никаких сторон, то и передавать ей никаких параметров не надо. Также заметим, что класс имеет методы Point::getSquare() и Point::getPerimeter() для расчета площади и периметра, оба возвращают 0. Для точки оно и логично.


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

/** * Класс Отрезок */ class LineSegment extends Point { LineSegment(double segmentLength) { this.segmentLength = segmentLength; } double segmentLength; // Длина отрезка /** * Переопределенный метод класса, который рассчитывает площадь отрезка */ double getSquare() { return 0; } /** * Переопределенный метод класса, который рассчитывает периметр отрезка */ double getPerimeter() { return this.segmentLength; } String getDescription() { return "Отрезок длиной: " + this.segmentLength; } }

Class LineSegment extends Point

означает, что класс LineSegment наследуется от класса Point . Методы LineSegment::getSquare() и LineSegment::getPerimeter() переопределяют соответствующие методы базового класса. Площадь отрезка всегда равняется нулю, а площадь периметра равняется длине этого отрезка.

Теперь, подобно классу отрезка, опишем класс треугольника(который также наследуется от класса точки):

/** * Класс Треугольник. */ class Triangle extends Point { /** * Конструктор класса. Принимает на вход три параметра: * длина стороны А, длина стороны Б, * угол между этими сторонами(в градусах) */ Triangle(double sideA, double sideB, double angleAB) { this.sideA = sideA; this.sideB = sideB; this.angleAB = angleAB; } double sideA; //Поле класса, хранит значение стороны А в описываемом треугольнике double sideB; //Поле класса, хранит значение стороны Б в описываемом треугольнике double angleAB; //Поле класса, хранит угла(в градусах) между двумя сторонами в описываемом треугольнике /** * Метод класса, который рассчитывает площадь треугольника */ double getSquare() { double square = (this.sideA * this.sideB * Math.sin(this.angleAB * Math.PI / 180))/2; return square; } /** * Метод класса, который рассчитывает периметр треугольника */ double getPerimeter() { double sideC = Math.sqrt(Math.pow(this.sideA, 2) + Math.pow(this.sideB, 2) - 2 * this.sideA * this.sideB * Math.cos(this.angleAB * Math.PI / 180)); double perimeter = this.sideA + this.sideB + sideC; return perimeter; } String getDescription() { return "Треугольник со сторонами: " + this.sideA + ", " + this.sideB + " и углом между ними: " + this.angleAB; } }

Тут нет ничего нового. Также, методы Triangle::getSquare() и Triangle::getPerimeter() переопределяют соответствующие методы базового класса.
Ну а теперь, собственно, тот самый код, который показывает волшебство полиморифзма и раскрывает мощь ООП:

Class Main { /** * Именно в этом месте запускается программа */ public static void main(String args) { //ArrayList - Это специальная структура данных в java, // позволяющая хранить объекты определенного типа в массиве. ArrayList figures = new ArrayList(); //добавляем три разных объекта в массив figures figures.add(new Point()); figures.add(new LineSegment(133)); figures.add(new Triangle(10, 17, 55)); for (int i = 0; i

Мы создали массив объектов класса Point , а поскольку классы LineSegment и Triangle наследуются от класса Point , то и их мы можем помещать в этот массив. Получается, каждую фигуру, которая есть в массиве figures мы можем рассматривать как объект класса Point . В этом и заключается полиморфизм: неизвестно, к какому именно классу принадлежат находящиеся в массиве figures объекты, но поскольку все объекты внутри этого массива принадлежат одному базовому классу Point , то все методы, которые применимы к классу Point также и применимы к его классам-наследникам.


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


Полный код примера:

Import java.util.ArrayList; class Main { /** * Именно в этом месте запускается программа */ public static void main(String args) { //ArrayList - Это специальная структура данных в java, // позволяющая хранить объекты определенного типа в массиве. ArrayList figures = new ArrayList(); //добавляем три разных объекта в массив figures figures.add(new Point()); figures.add(new LineSegment(133)); figures.add(new Triangle(10, 17, 55)); for (int i = 0; i

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

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

История

Основатели ООП - выдающиеся норвежские ученые Кристен Нигаард (Cristen Nygaard) и Оле-Йохан Даль (Ole-Johan Dahl). Работая над моделированием судовождения, Нигаард понял, что существующие программные средства малоэффективны в создании столь сложных программ, и тогда Нигаард начал разрабатывать концепции нового программирования, позволяющего преодолеть барьеры сложности, и которое впоследствии было названо объектно-ориентированным (сам термин был придуман Аланом Кеем, автором языка Java). Вместе с Оле-Йоханом Далем Нигаард разработал основные положения ООП и практические механизмы их реализации, которые затем были воплощены в первом ООЯ Симула (Simula). Заслуги этих ученых были по достоинству оценены мировым научным сообществом, и в 2001 году Нигаард и Даль стали лауреатами премии имени Алана Тьюринга - своеобразного аналога Нобелевской премии в области computer science.

Язык Симула пользовался известностью в академических кругах, однако по ряду причин не завоевал популярности среди разработчиков коммерческого ПО. Тем не менее основные идеи и возможности ООП были очень привлекательны для программистов. Впоследствии были созданы другие ООЯ: SmallTalk (1980), C++ (1985), Eiffel (1986), Object Pascal (1986) и Delphi (1995), Oberon-2 (1991), Java (1991), Visual Basic (1991) и многие другие. Некоторые из этих языков стали промышленными стандартами в программировании.

Особенности ООП

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

В обыденной жизни люди используют (пусть даже неосознанно) различные приемы “экономии мышления”, позволяющие осмысливать и выражать сложные явления в простых понятиях. Типичными приемами “экономии мышления” являются:

· абстрагирование (отбрасывание несущественных деталей);

· обобщение (выделение общих существенных признаков у разных явлений или предметов);

· классификация (осознание связи между явлениями и степени их схожести).

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

· объекты - особые программные структуры, объединяющие данные и алгоритмы их обработки;

· инкапсуляция - сокрытие подробностей функционирования объектов;

· наследование - “сокращенный” способ создания новых классов;

· полиморфизм - возможность применения нескольких реализаций одной функции.

Объекты и классы

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

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

В некоторых языках (C++, Java) объекты называются экземплярами класса (instances).

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

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

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

С позиций “борьбы со сложностью” инкапсуляция позволяет переложить часть контроля за правильностью работы с объектами на компилятор (компьютер).

Различные ООЯ предлагают разные возможности по инкапсуляции полей и методов (от полного отсутствия и до автоматического сокрытия всех полей). В промышленных ООЯ, таких, как C++, Java, Delphi, Eiffel и т.д., предусмотрены три уровня инкапсуляции полей и методов:

· public - на обращение к публичным полям и методам объектов нет никаких ограничений;

· protected - прямое обращение к защищенным полям и методам возможно только из методов данного класса и методов дочерних классов;

· private - прямое обращение к приватным полям и методам возможно исключительно из методов данного класса.

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

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

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

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

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

Совокупность всех классов-предков и классов-потомков называется иерархией классов .

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

Полиморфизм

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

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

В ООП есть два вида полиморфных методов - перегруженные и виртуальные .

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

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

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

Достоинства виртуальных методов проявляются только при использовании иерархии классов. Типичная схема использования виртуальных методов такова:

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

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

· При вызове для объекта, принадлежащего классу-потомку, полиморфного метода на деле используется виртуальный метод класса-потомка (а не класса-предка).

Яркий пример подобного использования виртуальных методов - система графического оконного интерфейса Delphi или Visual Basic: каждый видимый элемент графического интерфейса - кнопка, ползунок, окно и т.п. - должен быть потомком класса TControl. В классе TControl вводятся общие полиморфные методы отрисовки элементов графического интерфейса, а любой его потомок может нарисовать себя на экране своим собственным способом.

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

Тем не менее ООП не просто вошло в практику преподавания информатики (программирования) многих школ, но и присутствует на страницах школьных учебников (Угринович Н.Д. Информатика и информационные технологии. Учебник для 10–11-х классов, 2005. М.: БИНОМ. Лаборатория Знаний). Кроме того, в пропедевтическом курсе информатики для начальной школы (рабочие тетради авторского коллектива под руководством А.Горячева. 1–4-е классы) также вводятся понятия объекта и его свойств .

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

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

Фактически же ООП в школе рассматривается лишь как неотъемлемая часть визуального и компонентного программирования в современных профессиональных системах программирования, а в качестве объектов используются готовые объектные библиотеки различного уровня - это и библиотеки для построения графического интерфейса Windows-приложений, и многоцелевые универсальные библиотеки типов данных (например, STL в С++). Для примитивного использования этих библиотек достаточно знать и уметь применять несколько простейших правил синтаксиса языка программирования. Однако такие “знания” никоим образом не приближают учащихся ни к профессиональному овладению языком программирования, ни даже к пониманию ООП. Но, видимо, ничего страшного в этом нет. Школьная информатика и в профильной школе не ставит своей целью подготовку профессиональных программистов. Преподавание ООП - это специальная тема, даже на соответствующих специальностях вузов ее часто не изучают в достаточном объеме.

Не отрицая полностью предложение некоторых преподавателей информатики поставить объектно-ориентированный подход во главу угла изучения программирования, в том числе в школе, отметим, что ООП невозможно без таких базовых понятий, как программа, исполнитель, переменная, условие, цикл и т.д. Концепция ООП также включает в себя классическое процедурное программирование (см. “Подпрограммы ”), как механика Эйнштейна - механику Ньютона: достаточно представить себе процедурную программу как единственный объект с опущенным для простоты именем. Поэтому в первую очередь задача курса программирования в школе - научить базовым вещам. И лишь при возможности работы с современными визуальными средами программирования (Delphi, Visual Basic, Visual C++
и т.п.) познакомить с понятием объектов и их использованием в основном с помощью методики обучения программированию “по образцу”.

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

1. Введение в обучающий видеокурс по основам объектно-ориентированного программирования (ООП)

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

2. Основные принципы объектно-ориентированного программирования. Что такое ООП и зачем оно нужно?

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

3. Понятие класса и объекта в ООП

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

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

5. Методы и данные в ООП

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

6. Методы в ООП. Типы методов

Продолжаем изучать тему методов в объектно-ориентированном программировании. Какие виды методов существует и зачем они нужны? Чем методы экземпляров отличаются от методов класса? Конструктор, деструктор, getter, setter – что это такое и зачем эти методы нужны?

7. Каким должен быть хороший метод в ООП

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

8. Наследование в ООП

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

9. Инкапсуляция данных в ООП

Что означает слово «инкапсулирование», и как это относится к методам класса? Как защитить методы от внешнего воздействия, когда и зачем это может понадобиться? На примере рассмотрим методы, которые стоит инкапсулировать.

10. Абстракция в ООП

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

11. Полиморфизм в ООП

Что нужно знать, прежде чем понять эту парадигму? Что означает «полиморфизм»? Развиваем идею абстрагирования классов и методов, а также рассмотрим пример применения полиморфизма.

Всем привет.

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

Начнем.

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

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

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

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

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

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

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

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

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

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

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

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

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

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