Какой объект является верхушкой иерархии наследования и должен иметь уникальное поведение

Обновлено: 02.07.2024

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

Содержание

История

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

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

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

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

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

Информация должна быть проверяема, иначе она может быть поставлена под сомнение и удалена.
Вы можете отредактировать эту статью, добавив ссылки на авторитетные источники.
Эта отметка установлена 18 октября 2012.

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

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

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

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

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

Тимоти Бадд пишет [3] [4] :

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

Определить ООП можно и многими другими способами [5] .

Концепции

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

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

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

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

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

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

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

Подходы к проектированию программ в целом

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

Объектно-ориентированное проектирование состоит в описании структуры и поведения проектируемой системы, то есть, фактически, в ответе на два основных вопроса:

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

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

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

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

Родственные методологии

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

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

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

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

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

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

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

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

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

Критика ООП

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

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

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

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

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

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

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

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

  • Конструкторы, деструкторы, финализаторы.
  • Свойства (аксессоры).
  • Индексаторы.
  • Интерфейсы (например, в Java используются также как альтернатива множественному наследованию — любой класс может реализовать сколько угодно интерфейсов).
  • Переопределение операторов для классов.
  • Средства защиты внутренней структуры классов от несанкционированного использования извне. Обычно это модификаторы доступа к полям и методам, типа public, private, обычно также protected, иногда некоторые другие.

§ 28. Агрегация и наследование. Рассмотрим пример. Положим, мы создаем программу – редактор векторной графики. Для представления геометрических фигур используются следующие классы: многоугольник Polygon , треугольник Triangle , круг Circle . Вершины многоугольников и центр круга представляются классом точки Point . Положим также, мы реализуем для всех фигур метод смещения void Move (float dx, float dy) , который должен перемещать все точки фигуры на указанные величины по X и Y. Например, этот метод будет использоваться при перемещении фигуры курсором. Проанализируем следующий код.

Сравните методы Move в классах Circle , Polygon и Triangle . Хотя их код отличается, эти методы логически выполняют одно и то же: перебирают все поля-точки класса соответствующей фигуры и вызывают для каждой из этих точек метод Move . Изменив способ хранения данных в классах Triangle и Circle на массив точек, мы сделаем код методов Move одинаковым во всех трех классах:

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

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

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

На следующем рисунке показана диаграмма объектов и соответствующее распределение памяти для экземпляра класса Circle (прямоугольники с подчеркнутым названием в нотации UML обозначают объекты, при этом после двоеточия указывается их тип – класс, а перед двоеточием необязательно может быть указано имя объекта):

Разберем синтаксис, используемый в классе Circle5 .

После имени наследующего, или производного, класса Circle5 через двоеточие указывается имя наследуемого, или базового, класса Shape . При создании экземпляра производного класса Circle5 сначала создается экземпляр базового класса Shape с использованием конструктора, указываемого после двоеточия в объявлении конструктора производного класса. В приведенном примере при вызове конструктора Circle5() сначала создается объект типа Shape с помощью указанного конструктора base(1) , что обозначает вызов конструктора Shape(int) с аргументом 1, то есть создание фигуры с одной точкой. Сравните конструкторы в приведенном выше коде, в первом приближении они условно эквивалентны.

Открытые поля и методы объекта типа Shape доступны в методах класса Circle5 как если бы они были объявлены в нем же. При этом они вызываются через неявное обращение к переменной base , обозначающей экземпляр базового класса, по аналогии с тем, как мы обращаемся к полям класса из его методов через неявное обращение к переменной this , обозначающей экземпляр того же класса. Сравните это с вариантом реализации без наследования, где мы объявляем и инициализируем поле Shape base явно – поведение программы в обоих случаях будет идентично. Приведем еще пример:

Таким образом, наш класс Circle5 не просто неявно включает в себя экземпляр Shape base , но наследует его поведение (методы) и состояние (поля). Закрытые методы и поля базового класса ( vertices ) не видны, как и в случае агрегации, однако, как мы покажем в следующем параграфе, возможно отметить поля базового класса, чтобы они были видны в производном, но не видны пользователям этого производного класса.

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

Сформулируем следующее определение.

Отношение наследования изображается в нотации UML не закрашенной треугольной стрелкой в направлении от производного класса к базовому.

На следующем рисунке показана диаграмма объектов и соответствующее распределение памяти для экземпляра класса Circle :

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

§ 29. Защищенные поля и методы. В рассмотренных в предыдущем параграфе примерах массив точек Point[] vertices в классе Shape не виден производным классам, так как он объявлен закрытым private . Однако, для многих задач нам хотелось бы иметь доступ к состоянию и поведению (или к части состояния и поведения – к некоторым полям и методам) базового класса из производного, но не раскрывать это состояние и поведение для пользователей производного. То есть объявить поле или метод, которое было бы открытым ( public ) для методов производного класса, но закрытым ( private ) для других классов.

Например, для класса Circle5 мы бы хотели объявить метод public Point GetCenter() , возвращающий точку-центр круга. Но делать массив Shape.vertices открытым или создавать открытый метод получения точек public Point Shape.GetVertex(int) мы бы не хотели, так как тогда этот массив или соответствующий метод оказались бы видны пользователям класса. Чего, в свою очередь, мы бы хотели избежать, так как наличие у класса круга открытого поля-массива вершин или метода, возвращающего вершину по индексу, было бы нелогично:

Эта задача решается механизмом защищенных ( protected ) полей и методов.

Защищенные (protected) поля и методы – поля и методы, которые видны в методах класса, в котором они объявлены и в методах всех производных классов, но не видны извне этих классов.

Защищенные поля и методы помечаются модификатором видимости protected . В следующей таблицы приведена сводная информация по модификаторам видимости полей и методов:

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

§ 30. Многоуровневое наследование. В объектно-ориентированном программировании допускается многоуровневое наследование (multilevel): базовые классы могут быть производными по отношению к другим классам.

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

Соответствующая диаграмма классов:

Поле fieldB класса B не видно в классе A , так как это базовый, а не производный класс, по отношению к B , но оно видно в классах C , D1 и D2 , так как все они производные по отношению к классу B . Аналогично наследуется поле fieldC .

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

§ 32. Механизм создания экземпляра производного класса. В примере с геометрическими фигурами в § 28 мы уже указали, что при создании объекта производного класса сначала вызывается конструктор базового класса, а потом – конструктор производного. Приведем рассмотренный фрагмент кода повторно:

Оператор new сначала определяет, какой конструктор базового класса Shape соответствует вызванному конструктору производного класса Circle5 . Здесь мы вызвали конструктор по умолчанию класса Circle5 , и в определении этого конструктора указано, что ему соответствует конструктор Shape(int) базового класса. Поэтому сначала вызывается конструктор Shape(int) , а затем выполняется тело конструктора Circle5() . Однако такое описание не отвечает на следующие вопросы: 1) какова последовательность выполнения конструкторов при многоуровневом наследовании и 2) когда и в какой последовательности выполняется выделение памяти и инициализация полей классов?

При создании экземпляра класса C получим следующий вывод в консоль:

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

Если указанный конструктор базового класса не существует, то это приведет к ошибке компиляции:

Также отметим, что конструкторы, как поля и методы, могут быть объявлены открытыми ( public ), закрытыми ( private ) или защищенными ( protected ). Пример использования закрытых конструкторов мы рассматривали в главе 2.6. Здесь же обратим внимание, что производный класс не может обратиться к закрытому конструктору базового класса, так же как он не может обращаться к закрытым полям и методам базового класса.

В результате получим следующее распределение памяти:

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

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

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

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

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

Вопросы и задания

Дайте определения следующим терминам, а также сопоставьте русские и английские термины: агрегация, наследование, базовый класс, производный класс, суперкласс, подкласс, защищенные поля, методы и конструкторы, многоуровневое наследование, множественное наследование; aggregation, inheritance, base class, derived class, superclass, subclass, protected field, methods and constructors, multilevel inheritance, multiple inheritance.

Наследуются ли конструкторы классов?

Какой модификатор видимости имеет ключевое слово base ? Допустимо ли обращение к базовому классу базового класса через выражение base.base ?

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

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

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


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

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

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

  • абстрагирование
  • инкапсуляция
  • модульность
  • иерархия
  • типизация
  • параллелизм
  • устойчивость

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

Объекты и классы — основные абстракции предметной области.

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

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

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

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

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

Тип — точная характеристика некоторой совокупности однородных объектов, включающая структуру и поведение.

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

Параллелизм — это свойство, отличающее активные объекты от пассивных.

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

Сохраняемость (устойчивость) — способность объекта существовать во времени, переживая породивший его процесс, и (или) в пространстве, перемещаясь из своего первоначального адресного пространства.

Устойчивость — способность объекта сохранять свое существование во времени и/или пространстве (адресном, в частности при перемещении между узлами вычислительной системы). В частности, устойчивость объектов может быть обеспечена за счет их хранения в базе данных.


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

Объект — осязаемая сущность (tangible entity) — предмет или явление (процесс), имеющие четко выраженные границы, индивидуальность и поведение.

Любой объект обладает состоянием, поведением и индивидуальностью.

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

Индивидуальность — это свойства объекта, отличающие его от всех других объектов.

Структура и поведение схожих объектов определяют общий для них класс.

Объект в JavaScript создаётся с помощью функции Object.create. Эта функция из родителя и опционального набора свойств создаёт новую сущность. Пока что мы не будем беспокоиться о параметрах.

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

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

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

Конструктор класса — специальный блок инструкций, вызываемый при создании объекта.

var s = new String();

Деструктор — специальный метод класса, служащий для деинициализации объекта (например освобождения памяти).

Атрибут — поименованное свойство класса, определяющее диапазон допустимых значений, которые могут принимать экземпляры данного свойства. Атрибуты могут быть скрыты от других классов, это определяет видимость атрибута: рublic (общий, открытый); private (закрытый, секретный); protected (защищенный).

Дескриптор — это атрибут объекта со связанным поведением (англ. binding behavior), т.е. такой, чьё поведение при доступе переопределяется методами протокола дескриптора.

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

Метод — это функция или процедура, принадлежащая какому-то классу или объекту.

Различают простые методы и статические методы (методы класса):

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

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

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

  • открытый (public) интерфейс — общий интерфейс для всех пользователей данного класса;
  • защищённый (protected) интерфейс — внутренний интерфейс для всех наследников данного класса;
  • закрытый (private) интерфейс — интерфейс, доступный только изнутри данного класса.

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

Полиморфизм — способность скрывать множество различных реализаций под единственным общим именем или интерфейсом.

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

Интерфейс — это совокупность операций, определяющих набор услуг класса или компонента. Интерфейс не определяет внутреннюю структуру, все его операции открыты.

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

Компонент представляет собой физическую реализацию проектной абстракции и может быть: компонентом исходного кода (cpp-шник); компонентом времени выполнения (dll, ActiveX и т. п.); исполняемый компонентом (exe-шник). Компонент обеспечивает физическую реализацию набора интерфейсов. Компонентная разработка (component-based development) представляет собой создание программных систем, состоящих из компонентов (не путать с объектно-ориентированным программированием (ООП).

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

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

-средством организации модели в процессе разработки, повышения ее управляемости и читаемости;

-единицей управления конфигурацией.

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

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

Объекты в ООП — это объекты реального мира

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

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

Схема взаимодействия объектов

Схема взаимодействия объектов

Приведенная ситуация является ярким примером сущности понятия «объект в ООП«. Сложно дать четкое определение этому понятию, приведу цитату этого определения Ивара Якобсона:

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

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

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

Состояние объекта в ООП

Идентификация объектов в ООП

На самом деле существует два вопроса: равны ли два объекта или тождественны.

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

Интерфейс объекта в ООП

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

Время жизни объекта в ООП

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

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

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

Временная диаграмма сохранения и восстановления объекта

Временная диаграмма сохранения и восстановления объекта

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

Временная диаграмма создания объекта в базе данных

Временная диаграмма создания объекта в базе данных

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

Композиция объектов в ООП

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

Включение объектов и ссылка на объект

Включение объектов и ссылка на объект

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

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