Введение

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

Пример

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

Класс против типа

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

Конструкция и реализация

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

Структура

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

Поведение

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

Определения подкласса

Концептуально суперкласс является супермножеством своих подклассов. Например, типичная иерархия классов включает в себя в качестве суперкласса и , в то время как будет подклассом. Это все отношения подмножеств в теории множеств, то есть все квадраты — прямоугольники, но не все прямоугольники — квадраты. Распространенная концептуальная ошибка — ошибочно принять отношение части за подкласс. Например, автомобиль и грузовик — это оба вида транспортных средств, и уместно моделировать их как подклассы класса транспортного средства. Однако было бы ошибкой моделировать части автомобиля как отношения подкласса. Например, автомобиль состоит из двигателя и кузова, но нецелесообразно моделировать двигатель или кузов как подкласс автомобиля. В объектно-ориентированном моделировании такие отношения обычно моделируются как свойства объекта. В этом примере класс будет иметь свойство, называемое , которое будет типизировано для хранения коллекции объектов, таких как экземпляры , , и т. д. Языки объектного моделирования, такие как UML, включают возможности моделирования различных аспектов отношения "часть-целое" и других видов отношений, таких как кардинальность объектов, ограничения на входные и выходные значения и т. п. Эта информация может быть использована инструментами разработчика для генерации дополнительного кода помимо основных определений данных для объектов, например, для проверки ошибок в методах get и set. Важный вопрос при моделировании и реализации системы классов объектов заключается в том, может ли класс иметь один или несколько суперклассов. В реальном мире, с реальными множествами, редко встретишь множества, которые не пересекаются более чем с одним другим множеством. Однако, хотя некоторые системы, такие как Flavors и CLOS, предоставляют возможность иметь более одного родителя, реализация этого во время выполнения вносит сложность, которую многие в объектно-ориентированном сообществе считают противоречащей целям использования объектных классов. Понимание того, какой класс будет отвечать за обработку сообщения, может стать сложным при работе с более чем одним суперклассом. При неосторожном использовании эта функция может привести к той же системной сложности и неоднозначности, которых классы были разработаны, чтобы избежать. Большинство современных объектно-ориентированных языков, таких как Smalltalk и Java, требуют единого наследования во время выполнения. Для этих языков множественное наследование может быть полезно для моделирования, но не для реализации. Однако объекты семантической паутины имеют несколько суперклассов. Волатильность Интернета требует такого уровня гибкости, и технологические стандарты, такие как язык веб-онтологий (OWL), разработаны для его поддержки. Схожий вопрос заключается в том, может ли иерархия классов изменяться во время выполнения. Языки, такие как Flavors, CLOS и Smalltalk, поддерживают эту функцию в рамках своих протоколов метаобъектов. Поскольку классы сами по себе являются объектами первого класса, можно динамически изменять их структуру, отправляя им соответствующие сообщения. Другие языки, ориентированные на строгую типизацию, такие как Java и C++, не позволяют изменять иерархию классов во время выполнения. Объекты семантической паутины обладают возможностью изменения классов во время выполнения. Обоснование аналогично обоснованию разрешения нескольких суперклассов: Интернет настолько динамичен и гибок, что для управления этой изменчивостью требуются динамические изменения в иерархии.

Ортогональность понятия класса и наследование

Хотя обычно считается, что языки, ориентированные на классы, поддерживают наследование, наследование не является неотъемлемой частью концепции классов. Некоторые языки, часто называемые "объектно-ориентированными языками", поддерживают классы, но не поддерживают наследование. Примеры объектно-ориентированных языков включают более ранние версии Visual Basic.

В рамках объектно-ориентированного анализа

В объектно-ориентированном анализе и в Unified Modeling Language (UML) ассоциация между двумя классами представляет собой взаимодействие между классами или их соответствующими экземплярами. Ассоциации имеют направление; например, двунаправленная ассоциация между двумя классами указывает на то, что оба класса осведомлены об их взаимосвязи. Ассоциации могут быть именованы в соответствии с их названием или назначением. Роль ассоциации присваивается концу ассоциации и описывает роль соответствующего класса. Например, роль "подписчик" описывает способ, которым экземпляры класса "Человек" участвуют в ассоциации "подписан на" с классом "Журнал". Также, "Журнал" имеет роль "подписанного журнала" в той же ассоциации. Кратность роли ассоциации описывает, сколько экземпляров соответствует каждому экземпляру другого класса в данной ассоциации. Распространенные кратности: "0..1", "1..1", "1..*" и "0..*", где "*" обозначает любое количество экземпляров. Большинство объектно-ориентированных языков программирования позволяют программисту указать, какие классы считаются абстрактными и запрещают их инстанцирование. Например, в Java, C# и PHP используется ключевое слово abstract. В C++ абстрактный класс – это класс, имеющий как минимум один абстрактный метод, заданный соответствующим синтаксисом языка (чистая виртуальная функция в терминологии C++).

Местные и внутренние

В некоторых языках программирования классы могут быть объявлены не только в глобальной области видимости, но и в других областях. Существуют различные типы таких классов. Внутренний класс – это класс, определенный внутри другого класса. Отношения между внутренним классом и содержащим его классом также можно рассматривать как разновидность ассоциации классов. Обычно внутренний класс не связан с экземплярами внешнего класса и не создается одновременно с ним. В зависимости от языка, обращение к классу извне содержащего класса может быть как разрешено, так и запрещено. Связанным понятием являются внутренние типы, также известные как внутренние типы данных или вложенные типы, которые представляют собой обобщение концепции внутренних классов. C++ является примером языка, поддерживающего как внутренние классы, так и внутренние типы (с помощью объявлений typedef). Другой тип – локальный класс, который определяется внутри процедуры или функции. Это ограничивает использование имени класса областью видимости, в которой он объявлен. В зависимости от семантических правил языка, на локальные классы могут накладываться дополнительные ограничения по сравнению с нелокальными. Распространенным ограничением является запрет доступа методов локального класса к локальным переменным содержащей функции. Например, в C++ локальный класс может обращаться к статическим переменным, объявленным в содержащей функции, но не может получить доступ к автоматическим переменным этой функции.

Метаклассы

Метаклассы — это классы, экземплярами которых являются классы. Метакласс описывает общую структуру набора классов и может реализовывать шаблон проектирования или описывать определенные виды классов. Метаклассы часто используются для описания фреймворков. В некоторых языках, таких как Python, Ruby или Smalltalk, класс также является объектом, поэтому каждый класс является экземпляром уникального метакласса, встроенного в язык. Common Lisp Object System (CLOS) предоставляет метаобъектные протоколы (MOP) для реализации этих классов и метаклассов.

Не подклассифицируемые (или герметичные)

Неподклассные классы или запечатанные классы позволяют программистам проектировать классы и иерархии классов, где на каком-то уровне иерархии дальнейшее наследование запрещено (отдельный класс также может быть объявлен как неподклассный, предотвращая создание какой-либо иерархии). Это отличается от абстрактных классов, которые предполагают, стимулируют и требуют наследования для своего использования. Неподклассный класс является конкретным по умолчанию. Неподклассный класс создается путем объявления класса как `sealed` в C# или как `final` в Java или PHP. Например, класс `final` в Java обозначается как таковой. Неподклассные классы могут позволять компилятору (в компилируемых языках) выполнять оптимизации, недоступные для подклассных классов.

Открытый класс

Открытый класс можно изменять. Обычно, исполняемую программу нельзя изменять пользователям. Разработчики часто могут изменять некоторые классы, но обычно не могут изменять стандартные или встроенные классы. В Ruby все классы открыты для изменений. В Python классы можно создавать во время выполнения, и все их можно модифицировать впоследствии. Категории в Objective-C позволяют программисту добавлять методы к существующему классу, не перекомпилируя его и даже не имея доступа к его исходному коду.

Миксины

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

Частично

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

Не назван

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

Представление в режиме выполнения

Как тип данных, класс обычно рассматривается как конструкция времени компиляции. Язык или библиотека также может поддерживать прототипные или фабричные метаобъекты, представляющие информацию о классах во время выполнения, или даже метаданные, обеспечивающие доступ к средствам рефлексивного программирования (рефлексии) и возможность манипулирования форматами структур данных во время выполнения. Многие языки различают этот вид информации о классах во время выполнения и сам класс, поскольку эта информация не требуется во время выполнения. Некоторые динамические языки не проводят чёткого различия между конструкциями времени выполнения и времени компиляции и, следовательно, могут не различать метаобъекты и классы. Например, если Human является метаобъектом, представляющим класс Person, то экземпляры класса Person могут быть созданы с использованием средств метаобъекта Human.