Лекция_1_Основание_ООП.ppt
- Количество слайдов: 84
Основание объектно-ориентированного программирования Лекция 1 Исаева Ирина Николаевна
§ 1. Сложность, присущая программному обеспечению Сложность вызывается четырьмя основными причинами: • сложностью реальной предметной области, из которой исходит заказ на разработку; • трудностью управления процессом разработки; • необходимостью обеспечить достаточную гибкость программы; • неудовлетворительное описание поведения больших дискретных систем.
Задача разработчиков программной системы - создать иллюзию простоты
Примеры сложных систем Структура персонального компьютера. Персональный компьютер (ПК) - прибор умеренной сложности. Большинство ПК состоит из одних и тех же основных элементов. Мы можем любую из этих частей разложить на составляющие. Каждую из этих частей можно также разложить на составляющие. Это пример сложной иерархической системы.
Дело не только в том, что сложная система ПК иерархична, но в том, что уровни этой иерархии представляют различные уровни абстракции, причем один надстроен над другим и каждый может быть рассмотрен (понят) отдельно.
Структура растений. Растение состоит из трех основных частей: корни, стебли и листья. Каждая из них имеет свою особую структуру. Каждая из этих структур, в свою очередь, представляет собой набор клеток. Внутри каждой клетки можно выделить следующий уровень, который включает хлоропласт, ядро и т. д. Так же, как у компьютера, части растения образуют иерархию, каждый уровень которой обладает собственной независимой сложностью.
Для каждого уровня абстракции всегда четко разграничено "внешнее" и "внутреннее". Например, можно установить, что части листа совместно обеспечивают функционирование листа в целом и очень слабо взаимодействуют или вообще прямо не взаимодействуют с элементами корней. Проще говоря, существует четкое разделение функций различных уровней абстракции.
Пять признаков сложной системы 1. Сложные системы часто являются иерархическими и состоят из взаимозависимых подсистем, которые в свою очередь также могут быть разделены на подсистемы, и т. д. , вплоть до самого низкого уровням. 2. Выбор, какие компоненты в данной системе считаются элементарными, относительно произволен и в большой степени оставляется на усмотрение исследователя.
3. Внутрикомпонентная связь обычно сильнее, чем связь между компонентами. 4. Разные сложные системы содержат одинаковые структурные части. 5. Любая работающая сложная система является результатом развития работавшей более простой системы. . . Сложная система, спроектированная "с нуля", никогда не заработает.
Наш опыт показывает, что наиболее успешны те программные системы, в которых заложены хорошо продуманные структуры классов и объектов и которые обладают пятью признаками сложных систем, описанными выше. Структуры классов и объектов системы вместе называтся архитектурой системы.
Человеческие возможности и сложные системы. Если мы знаем, как должны быть спроектированы сложные программные системы, то почему при создании таких систем мы сталкиваемся с серьезными проблемами? Главная причина: физическая ограниченность возможностей человека при работе со сложными системами.
Эксперименты психологов, например Миллера, показывают, что максимальное количество структурных единиц информации, за которыми человеческий мозг может одновременно следить, приблизительно равно семи плюс-минус два. Сложность программных систем возрастает, но способность нашего мозга справиться с этой сложностью ограничена.
Роль декомпозиции Процесс разбиения задачи на отдельные, структурно связанные, части, называется декомпозицией. При процедурной декомпозиции в задаче выделяются алгоритмы и обрабатываемые ими структуры данных, при логической – правила, связывающие отдельные понятия. При ОО-декомпозиции в задаче выделяются классы и способы взаимодействия объектов этих классов друг с другом.
Декомпозиция: алгоритмическая или объектно-ориентированная? Правильный ответ на этот вопрос: важны оба аспекта. Разделение по алгоритмам концентрирует внимание на порядке происходящих событий, а разделение по объектам придает особое значение либо объектам, либо субъектам действия.
Опыт показывает, что полезнее начинать с объектной декомпозиции. Такое начало поможет нам лучше справиться с приданием организованности сложности программных систем.
§ 2. Появление ОО языков программирования • Первые объектно-ориентированные языки программирования появились в конце 60 -х гг. (Симула-67, Smalltalk ). • Быстрое развитие технологии ООП началось лишь в середине 80 -х гг. Побуждающим мотивом для развития стала постоянно возрастающая сложность программного обеспечения.
Язык С++ очень быстро развивался, начиная с середины 80 -х гг. (первая версия была разработана Бьярном Страуструпом в 1979 г. ). В 1994 г. комитетом по стандартизации ANSI / ISO был принят стандарт языка С++. В начале 90 -х гг. на основе языка Си++ был разработан язык Java, предназначенный для написания программ для Интернета.
Язык Object Pascal был особенно популярен, пока фирма Apple применяла его в качестве основного языка программирования для компьютеров Macintosh. Object Pascal продолжает применяться на IBM-совместимых ПК в составе среды быстрой разработки программ Inprise Delphi.
Причины популярности ООП • надежда, что ООП приведет к быстрому росту продуктивности программистов и повышению надежности программ; • желание перейти от существующих языков к новой технологии; • сходство с методами проектирования, применяющимися в других инженерных областях (сборка изделия из готовых блоков).
Парадигма программирования – способ концептуализации, который определяет, как проводить вычисления и как работа, выполняемая компьютером, должна быть структурирована и организована. Известны следующие парадигмы программирования: • процедурное (языки Паскаль, Си), • ООП , • логическое (Пролог), • функциональное (Лисп).
Взаимодействие агентов на бытовом примере Допустим, что требуется приобрести компьютер. Простейшим вариантом будет пойти в ближайший компьютерный магазин, найти продавца, сформировать с ним конфигурацию компьютера, оплатить заказ и прийти через определенное время, чтобы забрать собранный компьютер. В решении описанной задачи явно заметны два агента: покупатель и продавец.
В данной естественной трактовке заметно основное свойство ООП: агент-источник посылает сообщение агенту-приемнику, чтобы он выполнил некоторое действие. В терминологии ООП агенты, обменивающиеся сообщениями, называются объектами. Агент-источник для посылки сообщения выполняет два необходимых действия: 1) поиск подходящего агента; 2) передача агенту сообщения, содержащего запрос.
Для удовлетворения запроса у агентаприёмника есть некоторый метод – алгоритм, или последовательность операций, которая используется агентом для выполнения запроса.
§ 3. Основные принципы ООП Сообщения Первый принцип ООП: действия задаются в форме сообщений, посылаемых одними объектами другим. Действие в ООП инициируется посредством передачи сообщения объекту, ответственному за выполнение действия. Сообщение содержит запрос на осуществление действия и сопровождается дополнительной информацией (параметрами), необходимой для выполнения действия (например, при покупке компьютера параметрами является описание конфигурации и деньги).
Получатель (receiver) – это объект, которому посылается сообщение от объекта-клиента (client или sender). Если получатель принимает сообщение, то на него автоматически возлагается ответственность за выполнение указанного действия. В качестве реакции на сообщение получатель запустит некоторый метод, чтобы удовлетворить принятый запрос. Понятие обязанности или ответственности за выполнение действия является фундаментальной концепцией ООП. Полный набор обязанностей, связанных с определенным объектом, часто определяется с помощью термина "протокол".
Сокрытие информации (инкапсуляция) При пересылке сообщений действует важный второй принцип ООП: сокрытие информации - клиенту, посылающему запрос, ничего не требуется знать о способе его выполнения. Если уже существует объект, который может выполнить запрос, то получатель может переадресовать запрос ему.
Пересылка сообщений отличается от вызова процедуры следующим: • у сообщения имеется вполне конкретный получатель – объект, которому послано сообщение ; • интерпретация сообщения (вызываемый метод) зависит от получателя и является различной для различных получателей (например, в разных компьютерных магазинах сборка компьютера может выполняться по-разному).
Часто конкретный получатель неизвестен вплоть до выполнения программы. В таком случае говорят, что имеет место позднее связывание между сообщением (именем функции) и фрагментом кода (методом), используемым в ответ на сообщение. Эта ситуация противопоставляется раннему связыванию (на этапе компиляции и компоновки программы) имени с фрагментом кода, что происходит при традиционных вызовах процедур.
Наследование Все объекты являются представителями, или экземплярами, классов. Понятие "класс" обозначает категорию объектов, имеющих общие черты. Класс включает в себя (инкапсулирует) набор свойств (переменных), определяющих состояние объекта данного класса, и набор действий (методов), определяющих поведение объектов данного класса.
Организация знаний о классах представляется в виде иерархии. Классы представляются в виде иерархической древовидной структуры, в которой более абстрактные (т. е. более общие) классы располагаются в корне дерева, а более специализированные классы располагаются на его концах, в ветвях.
Иерархическое дерево классов для биологических объектов
Третий принцип ООП: наследование состоит в том, что классы могут быть организованы в иерархическую структуру с наследованием свойств. Дочерний класс (или подкласс) наследует свойства родительского класса (или надкласса), расположенного выше в иерархическом дереве.
Полиморфизм Четвёртый принцип ООП: информация, содержащаяся в подклассе, может переопределять информацию, наследуемую из родительского класса. Очень часто при реализации такого подхода метод, соответствующий подклассу, имеет то же имя, что и соответствующий метод в родительском классе. Применение различных методов разными объектами для обработки одного сообщения является примером полиморфизма.
Формулировка характеристик ООП Фундаментальные характеристики ООП (в формулировке Алана Кея, одного из основоположников ООП и разработчика языка Смоллток): 1) Все является объектом. 2) Объекты взаимодействуют, посылая и получая сообщения. Сообщение – это запрос на выполнение действия, дополненный набором параметров, которые могут понадобиться для выполнения действия.
3) Каждый объект имеет независимую память, которая состоит из других объектов. 4) Каждый объект является представителем класса, который выражает общие свойства объектов. 5) В классе задается поведение (функциональность) объекта. Тем самым все объекты, которые являются экземплярами одного класса, могут выполнять одни и те же действия. 6) Классы организованы в единую древовидную структуру с общим корнем, называемую иерархией наследования. Память и поведение, связанное с экземплярами определенного класса, автоматически доступны любому классу, расположенному ниже в иерархическом дереве.
§ 4. Развитие средств абстрагирования в программировании Важность ООП-подхода можно понять, рассмотрев разнообразные механизмы, которые использовались программистами для контроля над сложностью. Главный способ борьбы со сложностью ПО – абстрагирование, т. е. способность отделить логический смысл фрагмента программы от проблемы его реализации. В некотором смысле ОО–подход не является революционным и может рассматриваться как естественный результат эволюции от процедур к модулям, далее к абстрактным типам данных и, наконец, к объектам.
Подпрограммы (процедуры и функции являются разновидностями подпрограмм) – это первые механизмы абстрагирования в языках программирования. Они позволяют сконцентрировать в одном месте работу, выполняемую многократно и затем многократно использовать этот код, вместо того чтобы писать его снова и снова. Они впервые обеспечили возможность скрытия информации – пользователи процедур могли не знать деталей реализованного алгоритма, а только интерфейс программы. Недостатки подпрограмм: нет эффективного механизма скрытия данных, проблема использования одинаковых имен полностью не снимается.
В качестве примера применения подпрограмм можно привести стек, реализованный с помощью глобальных переменных: int datastack[100]; int datatop = 0; void push( int val ) { if ( datatop < 100 ) datastack[datatop++] = val; } int top() { if ( datatop > 0 ) return datastack[datatop — 1]; return 0; } int pop() { if ( datatop > 0 ) return datastack[--datatop]; return 0; }
Модули – улучшенный метод создания и управления совокупностями имен и связанными с ними значениями. Пример со стеком: есть информация (интерфейсные процедуры), которая должна быть широко доступной, и есть некоторые данные (собственно данные стека), доступ к которым должен быть ограничен. Суть модуля состоит в разбиении пространства имен на две части: открытая (public) часть является доступной извне модуля, закрытая (private) часть доступна только внутри модуля. Достоинства модулей: эффективный механизм скрытия данных. Недостатки: нет способа размножения экземпляров областей данных.
Абстрактные типы данных АТД создаются программистом, но с этими типами можно работать так же, как и со встроенными типами данных. Каждому АТД соответствует набор допустимых значений (м. б. бесконечный) и ряд элементарных операций, которые могут быть выполнены над данными.
Объекты. Сообщения, наследование и полиморфизм. Объекты являются АТД, но понятия ООП добавляют к ним важные новшества по части разделения и совместного использования программного кода. Главная идея: пересылка сообщений. Действие инициируется по запросу, обращенному к конкретному объекту, а не через вызов функции. интерпретация сообщения может меняться для различных объектов
Механизм наследования: позволяет различным типам данных совместно использовать один и тот же код, приводя к уменьшению его размера и повышению функциональности. Полиморфизм: перекраивает этот общий код так, чтобы удовлетворить конкретным особенностям отдельных типов данных. Упор на независимость индивидуальных компонент позволяет использовать процесс пошаговой сборки, когда отдельные блоки ПО разрабатываются, программируются и отлаживаются до того, как они объединяются в большую систему.
Определение ООП Объектно-ориентированное программирование - это методология программирования, основанная на представлении программы в виде совокупности объектов, каждый из которых является экземпляром определенного класса, а классы образуют иерархию наследования.
Смысл проектирования В любой инженерной дисциплине под проектированием обычно понимается некий унифицированный подход, с помощью которого мы ищем пути решения определенной проблемы, обеспечивая выполнение поставленной задачи.
В контексте инженерного проектирования цель проектирования - создание системы, которая: • удовлетворяет заданным функциональным спецификациям; • согласована с ограничениями, накладываемыми оборудованием; • удовлетворяет требованиям по эксплуатационным качествам и ресурсопотреблению; • удовлетворяет критериям дизайна продукта; • удовлетворяет требованиям к самому процессу разработки (продолжительность и стоимость, привлечение дополнительных инструментальных средств).
По предположению Страуструпа: "Цель проектирования - выявление ясной и относительно простой внутренней структуры, иногда называемой архитектурой”. Подукты проектирования - модели, позволяющие понять структуру будущей системы, сбалансировать требования и наметить схему реализации.
Элементы программного проектирования Программисты разработали десятки различных методов, которые объединяет следующее: • условные обозначения - язык для описания каждой модели; • процесс - правила проектирования модели; • инструменты - средства, которые ускоряют процесс создания моделей, и в которых уже воплощены законы функционирования моделей.
Объектно-ориентированные модели Объектно-ориентированный анализ и проектирование - это метод, логически приводящий к объектноориентированной декомпозиции. Применяя объектно-ориентированное проектирование, мы создаем гибкие программы, написанные экономными средствами.
Так как построение моделей крайне важно при проектировании сложных систем, объектно-ориентированное проектирование предлагает богатый выбор моделей, которые представлены на рис.
• • • Выводы Программам присуща сложность, превосходящая возможности человеческого разума. Задача разработчиков программ - создать у пользователя системы иллюзию простоты. Сложные структуры часто принимают форму иерархий; полезны обе иерархии: и классов, и объектов. Познавательные способности человека ограничены; мы можем раздвинуть их рамки, используя декомпозицию, выделение абстракций и создание иерархий. Сложные системы можно исследовать, концентрируя основное внимание либо на объектах, либо на процессах; имеются веские основания использовать объектноориентированную декомпозицию, при которой мир рассматривается как упорядоченная совокупность объектов, которые в процессе взаимодействия друг с другом определяют поведение системы. Объектно-ориентированный подход имеет свою систему условных обозначений и предлагает богатый набор логических и физических моделей, с помощью которых мы можем получить представление о различных аспектах
§ 2. Элементы объектной модели Объектная модель имеет четыре главных элемента: • абстрагирование; • инкапсуляция; • модульность; • иерархия.
Абстрагирование выделяет существенные характеристики некоторого объекта, отличающие его от всех других видов объектов и, таким образом, четко определяет его концептуальные границы с точки зрения наблюдателя.
Инкапсуляция - это процесс отделения друг от друга элементов объекта, определяющих его устройство и поведение; инкапсуляция изолирует внешнее поведение объекта от его внутреннего устройства.
Иерархия – это упорядочение абстракций, средство классификации объектов, систематизация связей между объектами. Модульность – это представление системы в виде совокупности обособленных сегментов, связь между которыми обеспечивается посредством связей между классами, определяемыми в этих сегментах.
Смысл абстрагирования ООП характеризуется наличием 2 -х основных видов абстракций: • Тип данных объектной природы (класс) – определяемое программистом расширение исходных типов языка. • Экземпляр класса (объект) – переменная класса. Объект обладает состоянием, поведением и идентичностью.
Состояние объекта характеризуется набором его свойств (атрибутов) и текущими значениями каждого из этих свойств. Состояние объекта – результат его поведения, т. е. выполнения характерных для него действий. Идентичность – это такое свойство (набор свойств) объекта, которое позволяет отличить его от всех прочих объектов того же типа.
Простейший пример абстракции – электрическая лампочка, для которой определены 2 состояния: включена и выключена: Light +Turn. On(): void +Turn. Off(): void +Is. Turned. On(): bool Знак + означает, что функции составляют открытый интерфейс класса.
На языке С++: сlass Light { // Класс Light public: void Turn. On(); void Turn. Off(); bool Is. Turned. On(); }; Класс определяет общие свойства объектов.
В программе необходимо создать объект данного класса: // Объект размещается в стеке приложения Light light 1; // Объект класса Light 1. Turn. On(); // включить Light 1. Turn. Off(); // выключить
Можно разместить объект и в динамической памяти: // Объект размещается в динам. памяти Light *p 1; // Указатель на объект класса Light p 1=new Light; // Объект в динам. памяти p 1 ->Turn. On(); // включить p 1 ->Turn. Off(); // выключить
Итак, абстрагирование отвечает за внешнее поведение объекта ( «интерфейс» ), а не за внутренюю реализацию.
Смысл инкапсуляции Инкапсуляция занимается внутренним устройством и поэтому определяет чёткие границы между различными абстракциями. Другое важное свойство инкапсуляции – возможность скрытия реализации (ограничение доступа к данным класса).
Состояние лампы представляется булевой переменной turned. On: сlass Light { private: bool turned. On; // недоступно извне public: void Turn. On(); void Turn. Off(); bool Is. Turned. On(); };
Light - turned. On : bool=false +Turn. On(): void +Turn. Off(): void +Is. Turned. On(): bool Отделение интерфейса от реализации (диаграмма классов)
Методы (или функции-члены) класса: void Light: : Turn. On() { turned. On = true; } void Light: : Turn. Off() { turned. On = false; } void Light: : Is. Turned. On() { returned. On; } Закрытое поле (или член) класса доступен только из методов класса.
Программа, содержащая определение класса Light и использование объектов класса: #include <iostream> using namespace std; class Light { // Определение класса private: // Указывает на недоступность bool turned. On; public: // Указывает на открытость void Turn. On() { turned. On=false; } void Turn. Off() { turned. On=false; } bool Is. Turned. On() { returned. On; } };
// Создание и использование объектов класса void main() { Light L 1; // Создаём объект L 1. Turn. On(); if(L 1. Is. Turned. On()) cout<<“L 1: On”<<endl; else cout<<“L 1: Off”<<endl; L 1. Turn. Off(); if(L 1. Is. Turned. On()) cout<<“L 1: On”<<endl; else cout<<“L 1: Off”<<endl; // Указатель на объект класса Light *p. L 2=new Light; p. L 2 ->Turn. On();
if(p. L 2 ->Is. Turned. On())cout<<“L 2: On”<<endl; else cout<<“L 2: Off”<<endl; p. L 2 ->Turn. Off(); if(p. L 2 ->Is. Turned. On())cout<<“L 2: On”<<endl; else cout<<“L 2: Off”<<endl; }
Заметим, что инкапсуляция защищает от ошибок, а не от жульничества (она не обеспечивает полной закрытости класса). Существуют различия в реализации управления доступом в разных языках программирования: • В Smalltalk все данные закрыты, а все методы открыты; • В Object Pascal существует доступ к закрытым данным из всех классов, расположенных в том же модуле; • В С++ управление доступом и видимостью достаточно гибко (public, private, protected).
Вывод: Внутренняя реализация описывает, каким образом достигается желаемое поведение объекта. Инкапсуляция изолирует интерфейс от реализации. Инкапсуляция связана с управлением доступом к данным класса.
Типы структурных иерархий I. Структурная иерархия “is-part-of” (является частью - композитное агрегирование). Применительно к ООП: созданный класс должен представлять собой фрагмент полезного кода, доступного для повторного использования.
II. Структурная иерархия “is-a”. Применительно к ООП: наследование. Из существующего (базового) класса создаём новый класс (производный). В качестве примера иерархии классов приведём пример с геометрическими фигурами
Shape +Draw() +Erase() Circle - radius - center Triangle - angle. Points[3] + Rotate 90() Square - side - corner
#include <iostream> using namespace std; typedef struct { double x; double y; } Point; class Shape { public: void Draw() { cout<<“Shape: : Draw()”<<endl; } void Erase() { cout<<“Shape: : Erase()”<<endl; } };
class Circle: public Shape { double radius; Point center; }; class Square: public Shape { double side; Point corner; }; class Triangle: public Shape { Point angle. Points[3]; public: void Rotate 90() { cout<<“Triangle: : Rotate 90()”<<endl; };
// Пример использования объектов void main() { shape sh. Obj; sh. Obj. Draw(); sh. Obj. Erase(); Circle cir. Obj; cir. Obj. Draw(); cir. Obj. Erase(); Square sq. Obj; sq. Obj. Draw();
Triangle tr. Obj; tr. Obj. Draw(); tr. Obj. Erase(); tr. Obj. Rotate 90(); }
III. Структурная иерархия «is-like-a» Дочерний класс предоставляет операции, замещающие операции родительского класса, т. е. изменяющие функциональность базового класса. Поведение объектов, на которых вызываются измененные методы, называют полиморфным. Механизм, обеспечивающий такое поведение, называют полиморфизмом.
#include <iostream> using namespace std; typedef struct { double x; double y; } Point; class Shape { public: // virtual – метод может быть замещён virtual void Draw() { cout<<“Shape: : Draw()”<<endl; } virtual void Erase() { cout<<“Shape: : Erase()”<<endl; } };
class Circle: public Shape { double radius; Point center; public: void Draw() { // Замещающий метод // Circle: : Draw замещает Shape: : Draw cout<<“Circle: : Draw()”<<endl; void Erase() { // Замещающий метод // Circle: : Erase замещает Shape: : Erase cout<<“Circle: : Erase()”<<endl; } }; class Square: public Shape {. . . }; class Triangle: public Shape {. . . };
// Пример использования объектов void main() { Shape *p. Sh=new Shape(); p. Sh->Draw(); //печатается “Shape: : Draw” p. Sh->Erase(); //печатается “Shape: : Erase” Circle *p. Cir = new Circle; p. Cir->Draw(); //печатается “Circle: : Draw” p. Cir->Erase(); //печатается “Circle: : Erase” }
Теперь, работая с фигурами разных типов, мы можем использовать методы, имеющие одинаковый интерфейс. Это упрощает реализацию программы и её восприятие.
Модульность В разных языках программирования модульность поддерживается по-разному. Например, в C++ модулями являются раздельно компилируемые файлы. Для C/C++ традиционным является помещение интерфейсной части модулей в отдельные файлы с расширением. h (так называемые файлы-заголовки). Реализация, то есть текст модуля, хранится в файлах с расширением. с, . ср или. срр). Связь между файлами объявляется директивой макропроцессора #include.
Модули выполняют роль физических контейнеров, в которые помещаются определения классов и объектов при логическом проектировании системы. Модульность - это свойство системы, которая была разложена на внутренне связные, но слабо связанные между собой модули.
Лекция_1_Основание_ООП.ppt