Объектная модель.
Объектно-ориентированное программирование
Будем называть клиентом любой объект, использующий ресурсы другого объекта, называемого сервером. Мы будем характеризовать поведение объекта услугами, которые он оказывает другим объектам, и операциями, которые он выполняет над другими объектами. Этот подход концентрирует внимание на внешних проявлениях объекта и реализует так называемую контрактную модель программирования. Эта модель заключается… Читать ещё >
Объектная модель. Объектно-ориентированное программирование (реферат, курсовая, диплом, контрольная)
Объектно-ориентированный подход основывается на совокупности ряда принципов, называемой объектной моделью. Главными принципами являются.
- — абстрагирование;
- — инкапсуляция;
- — модульность;
- — иерархичность.
Эти принципы являются главными в том смысле, что без них модель не будет объектно-ориентированной. Кроме главных, назовем еще три дополнительных принципа:
- — типизация;
- — параллелизм;
- — сохраняемость.
Называя их дополнительными, мы имеем в виду, что они полезны в объектной модели, но не обязательны.
Абстрагирование
Люди развили чрезвычайно эффективную технологию преодоления сложности. Мы абстрагируемся от нее. Если мы не в состоянии полностью воссоздать сложный объект, то приходится игнорировать не слишком важные детали. В результате мы имеем дело с обобщенной, идеализированной моделью объекта.
Например, изучая процесс фотосинтеза у растений, мы концентрируем внимание на химических реакциях в определенных клетках листа и не обращаем внимание на остальные части — черенки, жилки и т. д.
Абстракция выделяет существенные характеристики некоторого объекта, отличающие его от всех других видов объектов, и, таким образом, четко определяет его концептуальные границы с точки зрения наблюдателя.
Абстрагирование концентрирует внимание на внешних особенностях объекта и позволяет отделить самые существенные особенности поведения от несущественных. Такое разделение смысла и реализации называют барьером абстракции. Установление того или иного барьера абстракции порождает множество различных абстракций для одного и того же предмета или явления реального мира. Абстрагируясь в большей или меньшей степени от различных аспектов проявления реальности, мы находимся на разных уровнях абстракции.
Для примера рассмотрим системный блок компьютера. Пользователю, использующему компьютер для набора текста, не важно, из каких частей состоит этот блок. Для него это — коробка белого цвета с кнопками и емкостью для дискеты. Он абстрагируется от таких понятий, как «процессор» или «оперативная память». С другой стороны, у программиста, пишущего программы в машинных кодах, барьер абстракции лежит намного ниже. Ему необходимо знать устройство процессора и команды, понимаемые им.
Является полезным еще один дополнительный принцип, называемый принципом наименьшего удивления. Согласно ему абстракция должна охватывать все поведение объекта, но не больше и не меньше, и не привносить сюрпризов или побочных эффектов, лежащих вне ее сферы применимости.
Например, нам необходимо использовать структуру данных, аналогичную стеку (с доступом, осуществляемым по правилу «первым вошел, последним вышел»), однако требуется проверять наличие в «стеке» некоторого элемента. Если мы назовем эту структуру данных стеком и предложим постороннему программисту, он очень удивится, заметив «лишнюю» операцию.
Все абстракции обладают как статическими, так и динамическими свойствами. Например, файл как объект требует определенного объема памяти на конкретном устройстве, имеет имя и содержимое. Эти атрибуты являются статическими свойствами. Конкретные же значения каждого из перечисленных свойств динамичны и изменяются в процессе использования объекта: файл можно увеличить или уменьшить, изменить его имя и содержимое.
Будем называть клиентом любой объект, использующий ресурсы другого объекта, называемого сервером. Мы будем характеризовать поведение объекта услугами, которые он оказывает другим объектам, и операциями, которые он выполняет над другими объектами. Этот подход концентрирует внимание на внешних проявлениях объекта и реализует так называемую контрактную модель программирования. Эта модель заключается в следующем: внешнее проявление объекта рассматривается с точки зрения его контракта с другими объектами, в соответствии с этим должно быть выполнено и его внутреннее устройство (часто — во взаимодействии с другими объектами). Контракт фиксирует все обязательства, которые объект-сервер имеет перед объектом-клиентом. Другими словами, этот контракт определяет ответственность объекта — то поведение, за которое он отвечает.
Каждая операция, предусмотренная контрактом, однозначно определяется ее сигнатурой — списком типов формальных параметров и типом возвращаемого значения. Полный набор операций, которые клиент может осуществлять над другим объектом, вместе с правильным порядком, в котором эти операции вызываются, называется протоколом. Протокол отражает все возможные способы, которыми объект может действовать или подвергаться воздействию. Тем самым протокол полностью определяет внешнее поведение абстракции.
Пример. В тепличном хозяйстве, использующем гидропонику, растения выращиваются на питательном растворе без песка, гравия и другой почвы. Управление режимом работы парниковой установки — очень ответственное дело. Оно зависит как от вида выращиваемых культур, так и от стадии выращивания. Нужно контролировать целый ряд факторов: температуру, влажность, освещение, кислотность и концентрацию питательных веществ. В больших хозяйствах для решения этой задачи часто используют автоматические системы, которые контролируют и регулируют указанные факторы. Цель автоматизации состоит здесь в том, чтобы при минимальном вмешательстве человека добиться соблюдения режима выращивания.
Одна из ключевых абстракций в данной задаче — датчик. Известно несколько разновидностей датчиков. Все, что влияет на урожай, должно быть измерено. Таким образом, нужны датчики температуры воды, температуры воздуха, влажности, кислотности, освещения и концентрации питательных веществ.
С внешней точки зрения датчик температуры — это объект, который способен измерять температуру там, где он расположен. Температура — это числовой параметр, имеющий ограниченный диапазон значений и определенную точность и означающий число градусов по Цельсию.
Местоположение датчика — это некоторое однозначно определенное место в теплице, температуру в котором необходимо знать. Таких мест, вероятно, немного. Для датчика температуры при этом существенно не само местоположение, а только то, что данный датчик расположен именно в данном месте.
Рассмотрим элементы реализации нашей абстракции на языке С++.
typedef float Temperature; // Температура по Цельсию.
typedef unsigned int Location; // Число, однозначно определяющее.
// положение датчика Здесь два оператора определения типов Temperature и Location вводят удобные псевдонимы для простейших типов, и это позволяет нам выражать свои абстракции на языке предметной области. Temperature — это числовой тип данных в формате с плавающей точкой для записи температур. Значения типа Location обозначают места, где могут располагаться температурные датчики.
Рассмотрим обязанности датчика температуры. Датчик должен знать значение температуры в своем местонахождении и сообщать ее по запросу. Клиент по отношению к датчику может выполнить такие действия: калибровать датчик и получать от него значение текущей температуры. Таким образом, объект «Датчик температуры» имеет две операции: «Калибровать» и «Текущая температура» .
struct TemperatureSensor {.
Temperature curTemperature; // текущая температура в.
// местонахождении датчика.
Location loc; // местонахождение датчика.
void calibrate (Temperature actualTemperature); // калибровать.
Temperature currentTemperature (); // текущая температура.
};
Данным описанием вводится новый тип TemperatureSensor. Важным здесь является то, что, во-первых, данные и функции, изменяющие их, объединены вместе в одном описании и, во-вторых, мы не работаем непосредственно с данными, а изменяем их посредством соответствующих функций.
Объекты данного типа вводятся так же, как и переменные стандартных типов:
TemperatureSensor TSensors[100]; // массив из ста объектов типа
// TemperatureSensor.
Функции, объявленные внутри описания, называются функциями-членами. Их можно вызывать только для переменной соответствующего типа. Например, калибровать датчик можно так:
TSensors [3]. calibrate (0.); // калибруется датчик номер 3.
Поскольку имя объекта, для которого вызывается функция-член, неявно ей передается, в списках аргументов функций отсутствует аргумент типа TemperatureSensor, задающий конкретный датчик, над которым производятся действия. К этому объекту внутри функции можно явно обратиться по указателю this. Например, в теле функции calibrate можно написать один из двух эквивалентных операторов.
curTemperature = actualTemperature;
this -> curTemperature = actualTemperature;
Центральной идеей абстракции является понятие инварианта. Инвариант — это некоторое логическое условие, значение которого (истина или ложь) должно сохраняться. Для каждой операции объекта можно задать предусловия (т.е. инварианты, предполагаемые операцией) и постусловия (т.е. инварианты, которым удовлетворяет операция).
Рассмотрим инварианты, связанные с операцией currentTemperature. Предусловие включает предположение, что датчик установлен в правильном месте в теплице, а постусловие — что датчик возвращает значение температуры в градусах Цельсия.
Изменение инварианта нарушает контракт, связанный с абстракцией. Если нарушено предусловие, то клиент не соблюдает свои обязательства и сервер не может выполнить задачу правильно. Если нарушено постусловие, то свои обязательства нарушил сервер, и клиент не может ему больше доверять.
Для проверки условий язык С++ предоставляет специальные средства в библиотеке assert.h.
В случае нарушения какого-либо условия возбуждается исключительная ситуация. Объекты могут возбуждать исключения, чтобы запретить дальнейшее выполнение операции и предупредить о проблеме другие объекты, которые в свою очередь могут принять на себя перехват исключения и справиться с проблемой.
С++ имеет специальный механизм обработки исключений, чувствительный к контексту. Контекстом для возбуждения исключения является блок try (пробный блок). Если при выполнении операторов, находящихся внутри блока try, происходит исключительная ситуация, то управление передается обработчикам исключений, которые задаются ключевым словом catch и находятся ниже блока try. Синтаксически обработчик catch выглядит подобно описанию функции с одним аргументом без указания типа возвращаемого значения. Для одного блока try может быть задано несколько обработчиков, отличающихся типом аргумента.
Исключение возбуждается посредством указания ключевого слова throw с необязательным аргументом-выражением. Исключение будет обработано посредством вызова того обработчика catch, тип параметра которого будет соответствовать типу аргумента throw. При наличии вложенных блоков try (например, из-за вложенности вызовов функций) будет использован обработчик самого глубокого блока. Если обработчика, соответствующего типу аргумента throw, на данном уровне не будет найдено, будет осуществлен выход из текущей функции и поиск в блоке try с меньшей глубиной вложенности и т. д. После обработки исключения управление передается на оператор, следующий за описаниями обработчиков catch.
Пример. Рассмотрим стек, реализованный с использованием массива фиксированной длины.
int stack[100]; // не более ста элементов в стеке.
int top=-1; // номер доступного элемента.
void push (int el) {.
if (top == 99) throw (1); // проверить на переполнение.
else stack[++top] = el; // поместить элемент в стек.
}.
int pop () {.
if (top == -1) throw (0); // проверить на пустоту.
else return stack[top—]; // извлечь элемент из стека.
}.
main () {.
int i = 0, k;
.. .
try{ // пробный блок.
push (i);
.. .
k = pop ();
.. .
}.
catch (int error){.. .} // если error = 0, то стек пуст;
// если error = 1, то стек полон.
}.