Объектная модель
Понимание объектно-ориентированного программирования должно объединить состояние и поведение — данные и операции на данных — в высокоуровневом модуле, объекте, и оказать ему поддержку языка. Объект является группой связанных функций и структуры данных, служащей тем функциям. Функции известны как методы объекта, и поля его структуры данных являются его переменными экземпляра. Методы переносят переменные экземпляра и скрывают их от остальной части программы, поскольку рисунок 3-1 иллюстрирует.
Если Вы когда-либо занимались каким-либо видом трудной проблемы программирования, вероятно, что Ваш проект включал группы функций, работающих над определенным видом данных — неявные «объекты» без поддержки языка. Объектно-ориентированное программирование делает эти функциональные группы явными и разрешает Вам думать с точки зрения группы, а не ее компонентов. Единственный путь к данным объекта, единственный интерфейс, через его методы.
Путем объединения и состояния и поведения в едином блоке, объект становится больше, чем любой один; целое действительно больше, чем сумма его частей. Объект является своего рода самодостаточной «подпрограммой» с юрисдикцией по определенной функциональной области. Это может играть законченную модульную роль в большем проектировании программы.
Например, если необходимо было записать программу, смоделировавшую домашнее использование воды, Вы могли бы изобрести объекты представлять различные компоненты системы доставки воды. Можно было бы быть a Faucet объект, который имел бы методы, чтобы запустить и остановить поток воды, установил скорость потока, возвращает сумму воды, использованной в установленный срок и т.д. Выполнять эту работу, a Faucet объекту были бы нужны переменные экземпляра для отслеживания то, открыто ли касание или закрывается, сколько воды используется, и куда вода прибывает из.
Безусловно, программируемое Faucet объект может быть более умным, чем реальный (он походит на механический кран с большим количеством приборов и присоединенных инструментов). Но даже реальный кран, как любой системный компонент, показывает и состояние и поведение. Для эффективного моделирования системы Вам нужны модули программирования, как объекты, это также комбинирует состояние и поведение.
Программа состоит из сети соединенных объектов, призывающих друг друга для решения части проблемы (как проиллюстрировано на рисунке 3-2). Каждый объект имеет определенную роль для игры в общем замысле программы и в состоянии связаться с другими объектами. Объекты связываются через сообщения, которые являются запросами для выполнения методов.
Объекты в сети все не будут тем же. Например, в дополнение к Faucet объекты, программа, которую могло бы также иметь использование воды моделей Pipe объекты, которые могут поставить воду Faucet и Valve объекты отрегулировать поток среди каналов. Мог быть a Building возразите для координирования ряда каналов, клапанов, и кранов, некоторых Appliance объекты — соответствие посудомоечным машинам, туалетам и стиральным машинам — который может включить и выключить клапаны, и возможно некоторых User объекты работать устройства и краны. Когда a Building объект спрашивают, сколько воды используется, это могло бы призвать каждого Faucet и Valve возразите для создания отчетов о его текущем состоянии. Когда пользователь запустит устройство, устройство должно будет включить клапан для получения воды, которой оно требует.
Обменивающаяся сообщениями метафора
Каждая парадигма программирования идет со своей собственной терминологией и метафорами. Жаргон, связанный с объектно-ориентированным программированием, приглашает Вас думать о том, что продолжается в программе с определенной точки зрения.
Существует тенденция, например, думать об объектах как об агентах и обеспечить их человекоподобными намерениями и возможностями. Заманчиво иногда говорить об объекте, решающем, что сделать о ситуации, прося у других объектов информацию, занявшись самоанализом о себе для получения требуемой информации, делегировав ответственность перед другим объектом, или управляя процессом.
Вместо того, чтобы думать с точки зрения функций или методов, выполняющих работу, как Вы были бы на языке процедурного программирования, эта метафора просит, чтобы Вы думали об объектах как о выполнении их методов. Объекты не являются пассивными контейнерами для состояния и поведения, но, как говорят, являются агентами действия программы.
Эта метафора фактически очень полезна. Объект походит на агента в нескольких отношениях: Это имеет определенную роль для игры в общем замысле программы, и в той роли это может действовать справедливо независимо от других частей программы. Это взаимодействует с другими объектами, поскольку они играют свои собственные роли, но это является автономным и до некоторой степени может действовать самостоятельно. Как агент на сцене, это не может отклониться от сценария, но роль, которую это играет, может быть многоаспектной и сложной.
Идея объектов как агенты соответствует приятно основной метафоре объектно-ориентированного программирования — идея, что объекты связываются через сообщения. Вместо того, чтобы вызвать метод, поскольку Вы были бы функция, Вы отправляете сообщение в объект, запрашивающий его выполнять один из его методов.
Несмотря на то, что могут потребоваться некоторые привыкающие к, эта метафора приводит к полезному способу смотреть на методы и объекты. Это абстрагирует методы далеко от определенных данных, они действуют на и концентраты на поведении вместо этого. Например, в интерфейсе объектно-ориентированного программирования, a start метод мог бы инициировать работу, archive метод мог бы заархивировать информацию и a draw метод мог бы произвести изображение. Точно то, какая работа инициируется, какая информация архивируется, и какое изображение нарисовано, не показано именем метода. Различные объекты могли бы выполнить эти методы по-разному.
Таким образом методы являются словарем абстрактных способов поведения. Для вызова одних из тех способов поведения необходимо сделать его бетоном путем соединения метода с объектом. Это сделано путем именования объекта как получатель сообщения. Объект, который Вы выбираете в качестве получателя, определяет точную работу, инициирующуюся, данные, архивирующиеся, или нарисованное изображение.
Поскольку методы принадлежат объектам, они могут быть вызваны только через определенный получатель (владелец метода и структуры данных, метод будет действовать на). Различные получатели могут иметь различные реализации того же метода. Следовательно, различные получатели могут сделать разные вещи в ответ на то же сообщение. Результат сообщения не может быть вычислен из сообщения или одного только имени метода; это также зависит от объекта, получающего сообщение.
Путем разделения сообщения (требуемое поведение) от получателя (владелец метода, который может реагировать на запрос), обменивающаяся сообщениями метафора отлично получает идею, что способы поведения могут быть абстрагированы далеко от их определенных реализаций.
Классы
Программа может иметь больше чем один объект того же вида. Программа, что использование воды моделей, например, могло бы иметь несколько кранов и каналов и возможно ряд устройств и пользователей. Объекты того же вида, как говорят, являются элементами того же класса. Все элементы класса в состоянии выполнить те же методы и иметь соответствие наборов переменных экземпляра. Они также совместно используют общее определение; каждый вид объекта определяется только один раз.
В этом объекты подобны структурам C. Объявление структуры определяет тип. Например, объявление
struct key { |
char *word; |
int count; |
}; |
определяет struct key ввести. После того, как определенный, имя структуры может использоваться для создания любого числа экземпляров типа:
struct key a, b, c, d; |
struct key *p = malloc(sizeof(struct key) * MAXITEMS); |
Объявление является шаблоном для своего рода структуры, но это не создает структуру, которую может использовать программа. Это предпринимает другой шаг для выделения памяти для фактической структуры того типа, шаг, который может быть повторен любое число раз.
Точно так же определение объекта создает шаблон для своего рода объекта. Это определяет класс объектов. Шаблон может использоваться для создания любого числа подобных объектов — экземпляры
класса. Например, было бы единственное определение Faucet класс. Используя это определение, программа могла выделить как многие Faucet экземпляры, поскольку этому было нужно.
Определение класса походит на определение структуры, в котором оно размечает расположение элементов данных (переменные экземпляра), становящиеся частью каждого экземпляра. Каждому экземпляру выделили память для ее собственного набора переменных экземпляра, хранящих значения, определенные к экземпляру.
Однако определение класса отличается от описания структуры, в котором оно также определяет методы, указывающие поведение элементов класса. Каждый экземпляр характеризуется его доступом к методам, определенным для класса. Два объекта с эквивалентными структурами данных, но различными методами не принадлежали бы тому же классу.
Модульный принцип
Программисту C модуль является не чем иным как файлом, содержащим исходный код. При повреждении большого (или даже «не так большой») программа в различные файлы является удобным способом разделить его на управляемые части. Когда программа соединяется, каждая часть может работаться на независимо и компилироваться одна, и затем интегрировалась с другими частями.
Используя static указатель класса памяти для ограничения объема имен только к файлам, где они объявляются, улучшает независимость исходных модулей. Этот вид модуля является модулем, определенным файловой системой. Это - контейнер для исходного кода, не логическая единица языка. То, что входит в контейнер, до каждого программиста. Можно использовать их для группировки логически связанных частей кода, но Вы не имеете к. Файлы походят на секции костюмера; можно поместить носки в одну секцию, нижнее белье в другом, и т.д., или можно использовать другую схему организации или просто принять решение перепутать все.
Языки объектно-ориентированного программирования поддерживают использование контейнеров файла для исходного кода, но они также добавляют логический модуль к языку — определения классов. Как Вы ожидали бы, часто имеет место, что каждый класс определяется в его собственном исходном файле — логические модули являются соответствующими к контейнерным модулям.
В Objective C, например, было бы возможно определить часть Valve класс, взаимодействующий с Pipe объекты в том же файле, определяющем Pipe класс, таким образом создавая контейнерный модуль для Pipe- связанный код и разделение Valve класс больше чем в один файл. Valve определение класса все еще действовало бы как модульный модуль в конструкции программы — это все еще будет логический модуль — независимо от того, в каком количестве файлов исходный код был расположен.
Механизмы, делающие логические единицы определений классов языка, обсуждены в некоторой подробности под Механизмами Абстракции.
Возможность многократного использования
Основная цель объектно-ориентированного программирования состоит в том, чтобы сделать код, который Вы пишете максимально допускающий повторное использование — для имения его служат многим различным ситуациям и приложениям — так, чтобы можно было избежать повторно реализовывать, даже если только немного по-другому, что-то это было уже сделано.
Возможность многократного использования под влиянием факторов, таких как они:
-
Насколько надежный и без ошибки код
-
Насколько ясный документация
-
Насколько простой и прямой интерфейс программирования
-
Как эффективно код выполняет свои задачи
-
Насколько полный набор функций
Эти факторы не применяются только к объектной модели. Они могут использоваться для оценки возможности многократного использования любого кода — стандарт C функции, а также определения классов. Эффективные и хорошо задокументированные функции, например, были бы более допускающими повторное использование, чем недокументированные и ненадежные.
Тем не менее, общее сравнение показало бы, что определения классов предоставляют себя повторно используемому коду способами, которыми не делают функции. Существуют различные вещи, которые можно сделать для создания функций более допускающими повторное использование — например, передающие данные как параметры вместо того, чтобы принять в частности названные глобальные переменные. Несмотря на это, оказывается, что только маленькое подмножество функций может быть обобщено вне приложений, для которых они были первоначально разработаны. Их возможность многократного использования по сути ограничивается по крайней мере тремя способами:
-
Имена функций являются глобальной переменной; каждая функция должна иметь уникальное имя (за исключением объявленных
static). Это требование именования мешает полагаться в большой степени на код библиотеки при создании сложной системы. Интерфейс программирования было бы трудно изучить и столь обширный, что он не мог легко получить значительные обобщения.Классы, с другой стороны, могут совместно использовать интерфейсы программирования. Когда те же соглашения о присвоении имен используются много раз, большая функциональность может быть упакована с относительно маленьким и легким для понимания интерфейсом.
-
Функции выбраны из библиотеки по одному. Это до программистов, чтобы привередливо выбрать отдельные функции, в которых они нуждаются.
Напротив, объекты стали пакетами функциональности, не отдельными методами и переменными экземпляра. Они предоставляют интегрированные услуги, таким образом, пользователи объектно-ориентированной библиотеки не увязнут, соединяя их собственные решения проблемы.
-
Функции обычно связываются к определенным видам структур данных, разработанных для определенной программы. Взаимодействие между данными и функцией является неизбежной частью интерфейса. Функция полезна только для тех, кто соглашается использовать тот же вид структур данных, которые это принимает как параметры.
Поскольку это скрывает свои данные, объект не имеет этой проблемы. Это - одна из причин принципала, что классы могут быть снова использованы более легко, чем функции.
Данные объекта защищены и не будут затронуты никакой другой частью программы. Методы могут поэтому доверять его целостности. Они могут быть уверены, что внешний доступ не поместил данные в нелогичное или несостоятельное состояние. В результате структура данных объектов более надежна, чем один переданный функции, и методы могут зависеть от нее больше. Допускающие повторное использование методы следовательно проще записать.
Кроме того, потому что данные объекта скрыты, класс может быть повторно реализован для использования различной структуры данных, не влияя на ее интерфейс. Все программы, использующие класс, могут взять новую версию, не изменяя исходного кода; никакое перепрограммирование не требуется.
Механизмы абстракции
К этой точке объекты были представлены как модули, воплощающие высокоуровневые абстракции и как когерентных ролевых игроков в приложении. Однако они не могли использоваться этот путь без поддержки различных механизмов языка. Два из самых важных механизмов являются инкапсуляцией и полиморфизмом.
Инкапсуляция сохраняет реализацию объекта из его интерфейса, и полиморфизм следует из предоставления каждого класса его собственное пространство имен. Следующие разделы обсуждают каждый из этих механизмов поочередно.
Инкапсуляция
Для разработки эффективно на любом уровне абстракции необходимо быть в состоянии оставить подробные данные реализации и думать с точки зрения модулей что группа те подробные данные под единым интерфейсом. Для модуля программирования, чтобы быть действительно эффективным, барьер между интерфейсом и реализацией должен быть абсолютным. Интерфейс должен инкапсулировать реализацию — т.е. скрыть ее от других частей программы. Инкапсуляция защищает реализацию от непреднамеренных действий и от непреднамеренного доступа.
В C ясно инкапсулируется функция; его реализация недоступна другим частям программы, и защищенный от любых действий мог бы быть взят за пределами организации функции. В Objective C так же инкапсулируются реализации метода, но что еще более важно так переменные экземпляра объекта. Они скрыты в объекте и невидимые снаружи. Инкапсуляцию переменных экземпляра иногда также вызывают сокрытием информации.
Могло бы казаться, сначала, что сокрытие информации в переменных экземпляра могло бы ограничить Вашу свободу как программиста. Фактически, это дает Вам больше комнаты для действия и освобождает Вас от ограничений, которые могли бы иначе быть наложены. Если какая-либо часть реализации объекта могла бы просочиться и стать доступной или беспокойство к другим частям программы, она свяжет руки оба из лица, осуществляющего внедрение объекта и тех, кто использовал бы объект. Ни один не мог сделать модификации, сначала не сверяясь с другим.
Предположим, например, что Вы интересуетесь Faucet объект, разрабатываемый для программы, что использование воды моделей и Вы хотите включить его в другую программу, которую Вы пишете. Как только интерфейс к объекту решен, Вы не должны быть заинтересованы, когда другие работают над ним, исправляют ошибки и находят лучшие способы реализовать его. Вы извлекаете пользу из этих улучшений, но ни один из них не влияет на то, что Вы делаете в своей программе. Поскольку Вы зависите исключительно от интерфейса, ничто, что они делают может повредить Ваш код. Ваша программа изолируется от реализации объекта.
Кроме того, несмотря на то, что те, которые реализуют Faucet объект мог бы интересоваться тем, как Вы используете класс и могли бы попытаться удостовериться, что это удовлетворяет Ваши потребности, они не должны быть обеспокоены способом, которым Вы пишете свой код. Ничто, что Вы делаете, не может коснуться реализации объекта или ограничить их свободу внести изменения реализации в будущих выпусках. Реализация изолируется от чего-либо, что Вы или другие пользователи объекта могли бы сделать.
Полиморфизм
Возможность различных объектов ответить, каждого ее собственным способом, к идентичным сообщениям вызывают полиморфизмом.
Полиморфизм следует из факта, что каждый класс живет в своем собственном пространстве имен. Имена, присвоенные в определении класса, не конфликтуют с именами, присвоенными нигде снаружи. Это - истина обе из переменных экземпляра в структуре данных объекта и методов объекта:
-
Так же, как поля структуры C находятся в защищенном пространстве имен, переменные экземпляра объекта - также.
-
Имена методов также защищены. В отличие от имен функций C, имена методов не являются глобальными символами. Имя метода в одном классе не может конфликтовать с именами методов в других классах; два совсем других класса могут реализовать тождественно названные методы.
Имена методов являются частью интерфейса объекта. Когда сообщение отправляется, запрашивая, чтобы объект сделал что-то, сообщение называет метод, который должен выполнить объект. Поскольку различные объекты могут иметь методы с тем же именем, значение сообщения должно быть понято относительно определенного объекта, получающего сообщение. То же сообщение, отправленное в два различных объекта, может вызвать два отличных метода.
Основное преимущество полиморфизма - то, что он упрощает интерфейс программирования. Это разрешает соглашениям быть установленными, который может быть снова использован в классе после класса. Вместо того, чтобы изобрести новое имя для каждой новой функции Вы добавляете к программе, те же имена могут быть снова использованы. Интерфейс программирования может быть описан как ряд абстрактных способов поведения, вполне кроме классов, реализующих их.
Например, предположите, что Вы хотите сообщить о сумме воды, используемой Appliance возразите за установленный срок времени. Вместо того, чтобы определить amountConsumed метод для Appliance класс, amountDispensedAtFaucet метод для a Faucet класс и a cumulativeUsage метод для a Building класс, можно просто определить a waterUsed метод для каждого класса. Эта консолидация сокращает количество методов, используемых для того, что является концептуально той же работой.
Полиморфизм также разрешает коду быть изолированным в методах различных объектов, а не собран в единственной функции, перечисляющей все возможные случаи. Это делает код, который Вы пишете более расширяемый и допускающий повторное использование. Когда новый случай приходит, Вы не должны повторно реализовывать существующий код; необходимо только добавить новый класс с новым методом, оставив код, который это уже записало один.
Например, предположите, что у Вас есть код, отправляющий a draw обменивайтесь сообщениями к объекту. В зависимости от получателя сообщение могло бы произвести одно из двух возможных изображений. Когда Вы хотите добавить третий случай, Вы не должны изменить сообщение или изменить существующий код; Вы просто позволяете другому объекту быть присвоенным как получатель сообщения.
Наследование
Самый простой способ объяснить что-то новое состоит в том, чтобы запуститься с чего-то понятого. Если Вы хотите описать, какова шхуна, помогает, знают ли Ваши слушатели уже, какова парусная шлюпка. Если Вы хотите объяснить, как клавесин работает, лучше, если можно предположить, что аудитория уже посмотрела в фортепьяно, или видела гитару, играемую, или по крайней мере знакома с идеей музыкального инструмента.
Если Вы хотите определить новый вид объекта, то же является истиной; описание более просто, если оно может запуститься с определения существующего объекта.
С этим в памяти, языки объектно-ориентированного программирования разрешают Вам базировать новое определение класса на классе, уже определенном. Базовый класс вызывают суперклассом; новый класс является своим подклассом. Определение подкласса указывает только, как оно отличается от суперкласса; все остальное взято, чтобы быть тем же.
Ничто не копируется от суперкласса до подкласса. Вместо этого эти два класса соединяются так, чтобы подкласс наследовал все методы и переменные экземпляра его суперкласса, очень поскольку Вы хотите, чтобы понимание Вашего слушателя шхуны наследовало то, что они уже знают о парусных шлюпках. Если бы определение подкласса было пусто (если бы оно не определяло переменных экземпляра или собственных методов), то эти два класса были бы идентичны (за исключением их имен) и совместно использовали бы то же определение. Это походило бы на объяснение, что скрипка путем высказывания, что это - точно то же как скрипка. Однако причина объявления подкласса не состоит в том, чтобы генерировать синонимы; это для создания чего-то, по крайней мере, немного различного от его суперкласса. Например, Вы могли бы расширить поведение скрипки позволить ему играть мятлик в дополнение к классической музыке.
Иерархии классов
Любой класс может использоваться в качестве суперкласса для нового определения класса. Класс может одновременно быть подклассом другого класса и суперкласса для его собственных подклассов. Любое число классов может таким образом быть соединено в иерархии наследования, такого как то, изображенное на рисунке 3-3.
Каждая иерархия наследования начинается с корневого класса, не имеющего никакого суперкласса. От корневого класса иерархия переходит вниз. Каждый класс наследовался от его суперкласса и, через его суперкласс, от всех классов выше его в иерархии. Каждый класс наследовался от корневого класса.
Каждый класс является накоплением всех определений классов в его цепочке наследования. В примере выше, класс D наследовался и от C — его суперкласса — и от корневого класса. Элементам класса D определили методы и переменные экземпляра во всех трех классах — D, C, и корень.
Как правило, каждый класс имеет всего один суперкласс и может иметь неограниченное количество подклассов. Однако на некоторых языках объектно-ориентированного программирования (хотя не в Objective C), класс может иметь больше чем один суперкласс; это может наследоваться через многократные источники. Вместо того, чтобы иметь единственную иерархию, переходящую вниз как показано на рисунке 3-3, множественное наследование позволяет некоторым ответвлениям иерархии (или различных иерархий) слияние.
Определения подкласса
Подкласс может сделать три вида изменений в определении, которое он наследовал через его суперкласс:
-
Это может развернуть определение класса, которое это наследовало путем добавления новых методов и переменных экземпляра. Это - наиболее распространенная причина определения подкласса. Подклассы всегда добавляют новые методы и добавляют новые переменные экземпляра, если методы требуют его.
-
Это может изменить поведение, которое это наследовало путем замены существующего метода новой версией. Это сделано путем простой реализации нового метода с тем же именем как один, это наследовано. Новая версия переопределяет наследованную версию. (Унаследованный метод не исчезает; это все еще допустимо для класса, определившего его и другие классы, наследовавшие его.)
-
Это может совершенствовать или расширить поведение, которое это наследовало путем замены существующего метода новой версией, но это все еще сохраняет старую версию путем слияния его в новый метод. Подкласс отправляет сообщение для выполнения старой версии в организации нового метода. Каждый класс в цепочке наследования может внести часть поведения метода. В то время как версия C включает версию, определенную в корневой класс, на рисунке 3-3, например, класс D мог бы переопределить метод, определенный в классе C, и включить версию C.
Подклассы таким образом имеют тенденцию заполнять суперопределение класса, делая его более определенным и специализированным. Они добавляют, и иногда заменяют, кодируют, а не вычитают его. Обратите внимание на то, что методы обычно не могут лишаться наследства, и переменные экземпляра не могут быть удалены или переопределены.
Использование наследования
Классические примеры иерархии наследования заняты от животного и растения taxonomies. Например, мог быть класс, соответствующий Pinaceae (сосна) семья деревьев. Его подклассы могли быть Fir, Spruce, Pine, Hemlock, Tamarack, DouglasFir, и TrueCedar, соответствие различным родам, составляющим семью. Pine класс мог бы иметь SoftPine и HardPine подклассы, с WhitePine, SugarPine, и BristleconePine как подклассы SoftPine, и PonderosaPine, JackPine, MontereyPine, и RedPine как подклассы HardPine.
Редко существует причина программировать таксономию как это, но аналогия является хорошей. Подклассы имеют тенденцию специализировать суперкласс или адаптировать его к особому назначению, очень поскольку разновидность специализирует род.
Вот некоторое типичное использование наследования:
-
Многократное использование кода. Если два или больше класса имеют некоторые общие черты, но также и отличаются до некоторой степени, общие элементы могут быть помещены в определение единого класса, которое наследовали другие классы. Общий код совместно используется, и должны только быть реализованным один раз.
Например,
Faucet,Valve, иPipeобъекты, определенные для программы, что использование воды моделей, вся потребность соединение с водным источником и должно быть в состоянии записать скорость потока. Эти общности могут быть закодированы один раз в классе чтоFaucet,Valve, иPipeклассы наследовались от. AFaucetобъект, как могут говорить, является своего родаValveобъект, поэтому возможно,Faucetкласс наследовал бы большую часть того, от чего этоValve, и добавьте мало собственное. -
Установка протокола. Класс может объявить много методов, которые его подклассы, как ожидают, реализуют. Класс мог бы иметь пустые версии методов, или он мог бы реализовать частичные версии, которые должны быть включены в методы подклассов. В любом случае его объявления устанавливают протокол, за которым должны следовать все его подклассы.
Когда различная реализация классов столь же названные методы, программа лучше способна использовать полиморфизм в своем проекте. Установка протокола, который должны реализовать подклассы, помогает осуществить эти соглашения.
-
Поставка универсальной функциональности. Одно лицо, осуществляющее внедрение может определить класс, содержащий много основного, общего кода для решения проблемы, но это не заполняет все подробности. Другие лица, осуществляющие внедрение могут тогда создать подклассы для адаптации универсального класса их определенных потребностей. Например,
Applianceкласс в программе, что использование воды моделей могло бы определить универсальное использующее воду устройство, которое подклассы превратятся в определенные виды устройств.Наследование является таким образом и способом сделать чью-либо задачу программирования проще и способ разделить уровни реализации.
-
Создание небольших модификаций. Когда наследование используется, чтобы поставить универсальную функциональность, установить протокол или код повторного использования, класс разработан, что другие классы, как ожидают, наследуются от. Но можно также использовать наследование для изменения классов, не предназначающихся как суперклассы. Предположим, например, что существует объект, который работал бы хорошо в Вашей программе, но требуется изменить одну или две вещи, которые это делает. Можно внести изменения в подклассе.
-
Предварительный просмотр возможностей. Подклассы могут также использоваться для факторизации альтернатив для тестирования. Например, если класс должен быть закодирован с определенным пользовательским интерфейсом, альтернативные интерфейсы могут быть factored в подклассы в течение фазы проектирования проекта. Каждая альтернатива может тогда быть продемонстрирована потенциальным пользователям для наблюдения, который они предпочитают. Когда выбор сделан, выбранный подкласс может повторно интегрироваться в его суперкласс.
Динамизм
Когда-то в программировании истории, вопрос того, сколько могла бы использовать память программа, обычно разрешался, когда исходный код был скомпилирован и соединен. Вся память, в которой была бы когда-либо нужна программа, была обойдена для нее, поскольку она была запущена. Эта память была фиксирована; это не могло ни расти, ни уменьшение.
В непредусмотрительности очевидно, каково серьезное ограничение это было. Это ограничило не только, как были созданы программы, но и что Вы могли вообразить выполнением программы. Это ограничило проект, не просто программируя метод. Введение функций, динамично выделяющих память как программу, работает (такой как malloc) открытые возможности, не существовавшие прежде.
Время компиляции и разовые ссылкой ограничения ограничивают, потому что они вынуждают проблемы быть решенными от информации, найденной в исходном коде программиста, а не от информации, полученной от пользователя, когда работает программа.
Несмотря на то, что динамическое выделение удаляет одно такое ограничение, многие другие, одинаково ограничивая как выделение статического ЗУ, остаются. Например, элементы, составляющие приложение, должны быть соответствующими к типам данных во время компиляции. И во время ссылки обычно устанавливаются границы приложения. Каждая часть приложения должна быть объединена в единственном исполняемом файле. Новые модули и новые типы не могут быть представлены, когда работает программа.
Objective C стремится преодолеть эти ограничения и сделать программы максимально динамичными и жидкими. Это смещает большую часть нагрузки принятия решений со времени компиляции и времени ссылки ко времени выполнения. Цель состоит в том, чтобы позволить пользователям программы решить то, что произойдет, вместо того, чтобы ограничить их действия искусственно требованиями языка и потребностями компилятора и компоновщика.
Три вида динамизма особенно важны для объектно-ориентированного проекта:
-
Динамический контроль типов, ожидая до времени выполнения для определения класса объекта
-
Динамическое связывание, определяя во время выполнения, что метод вызвать
-
Динамическая загрузка, добавляя новые компоненты к программе, поскольку это работает
Динамический контроль типов
Компилятор обычно жалуется, присваивает ли код, который Вы пишете, значение типу, который не может разместить его. Вы могли бы видеть предупреждения как они:
incompatible types in assignment |
assignment of integer from pointer lacks a cast |
Проверка типа полезна, но существуют времена, когда она может вмешаться в преимущества, Вы добираетесь от полиморфизма, особенно если тип каждого объекта должен быть известен компилятору.
Предположим, например, что Вы хотите отправить объекту сообщение для выполнения start метод. Как другие элементы данных, объект представлен переменной. Если бы тип переменной (его класс) должен быть известен во время компиляции, было бы невозможно позволить факторам во время выполнения влиять на решение о том, какой объект должен быть присвоен переменной. Если класс переменной фиксируется в исходном коде, так версия start то, что сообщение вызывает.
Если с другой стороны возможно ожидать до времени выполнения для обнаружения класса переменной любой вид объекта мог быть присвоен ему. В зависимости от класса получателя, start сообщение могло бы вызвать различные версии метода и привести к совсем другим результатам.
Динамический контроль типов таким образом дает вещество динамическому связыванию (обсудил затем). Но это делает больше, чем это. Это разрешает ассоциациям между объектами быть определенными во время выполнения, вместо того, чтобы вынуждать их быть закодированными в статическом проекте. Например, сообщение могло передать объект в качестве параметра, не объявляя точно, какой объект это — т.е. не объявляя его класс. Получатель сообщения мог бы тогда отправить свои собственные сообщения в объект, снова никогда не заботясь о том, какой объект это. Поскольку получатель использует переданный объект выполнить часть его работы, это в некотором смысле настраивается объектом неопределенного типа (неопределенный в исходном коде, т.е. не во время выполнения).
Динамическое связывание
В стандарте C, можно объявить, что ряд альтернативных функций, таких как стандартное сравнение строк функционирует,
int strcmp(const char *, const char *); /* case sensitive */ |
int strcasecmp(const char *, const char *); /*case insensitive*/ |
и объявите указатель на функцию, имеющую тот же возврат и типы параметра:
int (* compare)(const char *, const char *); |
Можно тогда ожидать до времени выполнения для определения который функция присвоиться к указателю,
if ( **argv == 'i' ) |
compare = strcasecmp; |
else |
compare = strcmp; |
и вызовите функцию через указатель:
if ( compare(s1, s2) ) |
... |
Это сродни тому, что в объектно-ориентированном программировании вызывают динамическим связыванием, задерживая решение о точно, какой метод выполнить, пока программа не выполняет.
Несмотря на то, что не все объектно-ориентированные языки поддерживают его, динамическое связывание может обычно и прозрачно выполняться посредством обмена сообщениями. Вы не должны проходить через косвенность объявления указателя и присвоения значений к нему как показано в примере выше. Вы также не должны присваивать каждую альтернативную процедуру другое имя.
Сообщения вызывают методы косвенно. Каждое выражение сообщения должно найти, что реализация метода «вызывает». Найти, что метод, обменивающееся сообщениями машинное оборудование должно проверить класс получателя и определить местоположение его реализации метода, названного в сообщении. Когда это сделано во время выполнения, метод динамично связывается с сообщением. Когда это сделано компилятором, метод статически связывается.
Динамическое связывание возможно даже в отсутствие динамического контроля типов, но это не очень интересно. Когда класс получателя фиксируется и известен компилятору, существует мало преимущества в ожидании до времени выполнения для соответствия метода к сообщению. Компилятор мог точно также найти сам метод; результат во время выполнения не будет несколько отличаться.
Однако, если класс получателя с динамическим контролем типов, нет никакого пути к компилятору для определения который метод вызвать. Метод может быть найден только после того, как класс получателя разрешен во время выполнения. Динамический контроль типов таким образом влечет за собой динамическое связывание.
Динамический контроль типов также делает динамическое связывание интересным, поскольку это открывает возможность, что сообщение могло бы иметь совсем другие результаты в зависимости от класса получателя. Факторы во время выполнения могут влиять на выбор получателя и результат сообщения.
Динамический контроль типов и связывающий также открытый возможность, что код Вы пишете, может отправить сообщения в объекты, еще не изобретенные. Если типы объектов не должны быть решены до времени выполнения можно дать другим, которых свобода разработать их собственные классы и назвать их собственные типы данных и все еще иметь код отправляет сообщениям в их объекты. Все, с чем необходимо согласиться, является сообщениями, не типами данных.
Динамическая загрузка
Исторически, во многих общих средах, прежде чем программа может работать, все ее части должны были быть соединены в одном файле. Когда это было запущено, вся программа была загружена в память сразу.
Некоторые среды объектно-ориентированного программирования преодолевают это ограничение и позволяют различным частям исполняемой программы быть сохраненными в различных файлах. Программа может быть запущена по частям, поскольку они необходимы. Каждая часть динамично загружается и соединяется с остальной частью программы, поскольку это запускается. Пользовательские действия могут определить, какие части программы находятся в памяти и которые не являются.
Только ядро большой программы должно быть загружено в запуске. Другие модули могут быть добавлены, поскольку пользователь запрашивает их службы. Модули, которые не запрашивает пользователь, не предъявляют требований памяти к системе.
Динамическая загрузка повышает интересные возможности. Например, вся программа не должна быть разработана сразу. Можно поставить программное обеспечение в частях и обновить одну часть его за один раз. Можно разработать программу, которую хотят группы, несколько инструментов под единственным интерфейсом, и загружают просто инструменты пользователь. Программа может даже предложить наборы альтернативных инструментов для выполнения той же работы — пользователь тогда выбирает один инструмент из набора и только что был бы загружен инструмент.
Возможно, самое важное текущее преимущество динамической загрузки - то, что она подает расширяемые заявки. Можно позволить другим добавлять к и настраивать программу, которую Вы разработали. Вся Ваша программа должна сделать, служат основой, которую другие могут заполнить, и, во время выполнения, найти части, что они реализовали и загружают их динамично.
Основная проблема, с которой сталкивается динамическая загрузка, заставляет недавно загруженную часть программы работать с частями, уже работающими, особенно когда различные части были записаны различными людьми. Однако большая часть этой проблемы исчезает в объектно-ориентированной среде, потому что код организован в логические модули с ясным подразделением между реализацией и интерфейсом. Когда классы динамично загружаются, ничто в недавно загруженном коде уже не может столкнуться с кодом на месте. Каждый класс инкапсулирует свою реализацию и имеет независимое пространство имен.
Кроме того, динамический контроль типов и динамическое связывание позволяют классам, разработанным адаптацией других легко в программу, которую Вы разработали. Как только класс динамично загружается, он обработал не по-другому, чем какой-либо другой класс. Ваш код может отправить сообщения в их объекты и их к Вашим. Ни один из Вас не должен знать, какие классы другой реализовал. Вы должны только согласиться с коммуникационным протоколом.