Объектная модель Лекция 16. 12. 13 г. 1
Объектная модель – это концептуальная основа объектноориентированного программирования. Изучение основных элементов объектной модели и их взаимодействия позволяет лучше разобраться в принципах организации объектно-ориентированных программ, а также понять, в чем сходства и различия поддержки объектно-ориентированной парадигмы различными языками. Парадигма – набор теорий, стандартов и методов, которые совместно представляют собой способ организации научного знания, способ видения мира. Парадигма в программировании – способ концептуализации, который определяет, как следует проводить вычисления, и как работа, выполняемая компьютером, должна быть структурирована и организована. Лекция 16. 12. 13 г. 2
Объектно-ориентированное программирование (ООП) – это самая распространенная в современной проектной практике парадигма программирования, которая поддерживается большинством современных языков программирования: C++, C#, Java, Objective-C, Eiffel, VB. Net, Perl, Python, Ruby, Action. Script, Java. Script, PHP и др. Изучение ООП – это не столько изучение какого-либо конкретного языка программирования, сколько лежащих в его основе фундаментальных концепций и механизмов, которые могут отличаться в разных языках. Язык программирования – это инструмент, вытекающий из концепций проектирования и облекающий в синтаксическую форму основное содержание концепций, их суть. Поэтому изучение языка самого по себе вне связи с моделями проектирования, заложенными в основу языка, не приводит к улучшению качества проектирования и не обязательно способствует повышению квалификации разработчика. Лекция 16. 12. 13 г. 3
Языки программирования — это всегда некоторые модели (виртуальные вычислительные машины), позволяющие наиболее эффективно использовать возможности вычислительных средств, существенные для конкретных областей применения. Фактически, при написании программ ориентируются не на вычислительную машину как таковую, а на некоторую абстрактную модель вычислительного устройства. Качество абстрактной модели, ее соответствие сути решаемых задач во многом определяет качество и эффективность проектирования с использованием этой модели. Сложность задач, которые возможно решить с применением тех или иных концепций проектирования и выражающих их языков, непосредственно связана с уровнем абстракции как при постановке задачи, так и в ходе ее решения. Абстракция – это самое мощное интеллектуальное средство познания, имеющееся в распоряжении человека. Со способностью к абстракции связана и способность человека к творчеству. Обнаружение общих абстракций и механизмов значительно облегчает понимание сложных систем. Лекция 16. 12. 13 г. 4
Языки ассемблера позволяют избежать необходимости программирования в машинном коде, поэтому ассемблеры являются абстракцией вычислительной машины, для которой были спроектированы. Язык ассемблера (англ. assembly language) — машинно-ориентированный язык низкого уровня с командами, обычно соответствующими командам машины, который может обеспечить дополнительные возможности вроде макрокоманд. Лекция 16. 12. 13 г. 5
Языки, основанные на парадигме процедурного программирования, можно считать абстракциями ассемблеров. Примеры процедурных языков: Фортран, Алгол, КОБОЛ, PL/1, Паскаль, Си и др. Процедурный язык программирования предоставляет возможность программисту определять каждый шаг в процессе решения задачи. Особенность таких языков программирования состоит в том, что задачи разбиваются на шаги и решаются шаг за шагом. Используя процедурный язык, программист определяет языковые конструкции для выполнения последовательности алгоритмических шагов и представляет программу в виде иерархии процедур и функций. Лекция 16. 12. 13 г. 6
Основной недостаток процедурных языков заключается в том, достигаемый ими уровень абстракции все еще требует от программиста мышления в большей мере в терминах вычислительной машины, чем в терминах задачи, которую ему приходится решать. Качество проектирования определяется в итоге тем, насколько удачно программисту удалось установить соответствие между множеством понятий, характерных для решаемой задачи, и набором изобразительных средств языка, не всегда позволяющих адекватно отобразить эти понятия в рамках машинной модели. Развитие абстрактных моделей не всегда означало переход на более высокие уровни абстракции представления вычислительной машины как исполнительного устройства. Специфические особенности некоторых классов задач способствовали появлению альтернативных моделей. Примерами альтернатив моделированию вычислительной машины могут служить концепции, реализованные в языках программирования. функционального и логического Лекция 16. 12. 13 г. 7
Примеры языков функционального программирования: Lisp, Haskell, F#. ФП – это программирование, основанное на функциях (в математическом понимании этого термина), без использования переменных (т. е. изменяемых объектов). Лекция 16. 12. 13 г. 8
Примеры языков логического программирования –Prolog, Oz, Mercury. Основаны на принципе: "все задачи могут быть сведены к цепочке логических рассуждений". Эти языки нашли применение в задачах искусственного интеллекта и прикладной математики. Лекция 16. 12. 13 г. 9
Для решения задач логического управления в настоящее время активно развивается подход, основанный на использовании теории автоматов — автоматное программирование, реализуемое с помощью т. н. Switch-технологии. Лекция 16. 12. 13 г. 10
Объектно-ориентированный подход ØОО-подход предоставляет разработчику инструмент, позволяющий описать задачу и существенную часть реализации проекта в терминах предметной области, а не компьютерной модели. ОО-анализ начинается с исследования предметов реального мира, являющихся частью решаемой задачи. Применение ОО-парадигмы не гарантирует автоматически повышения качества проектирования, но способствует ясному представлению данных и присущих им свойств и действий (поведения) на уровне программного кода. При этом программные структуры на формальном ОО-языке соответствуют модели описания задачи и способов ее решения на языке, естественном для данной предметной области. Эта особенность отличает ОО-языки от языков процедурного программирования, нацеленных на моделирование действий, выполняемых вычислительной машиной. ØОО-проектирование предоставляет разработчикам гибкий мощный универсальный инструмент, не связанный с каким-то определенным классом задач. Объектный подход зарекомендовал себя как унифицирующая идея всей компьютерной науки, применимая не только в программировании, но также в проектировании интерфейса пользователя, баз данных и архитектуры компьютеров. Лекция 16. 12. 13 г. 11
Объектно-ориентированный подход Гради Буч (род. 27. 02. 1955 г. , США) — американский инженер, руководитель исследований в IBM Research с 2003 года. Г. Буч наиболее известен как создатель унифицированного языка моделирования UML, который он разработал совместно с Иваром Якобсоном и Джеймсом Рамбо. Известность Буч получил в 1980 -х гг. благодаря созданию метода разработки программного обеспечения, названного методом Буча. Он был изложен в книге «Объектно-ориентированный анализ и Лекция 16. 12. 13 г. проектирование» . 12
Главные элементы объектной модели Ø Ø абстрагирование (abstraction) инкапсуляция (encapsulation) иерархия (hierarchy) модульность (modularity) Абстрагирование позволяет выделить существенные характеристики некоторого объекта, отличающие его от всех других видов объектов. Абстракция четко определяет концептуальные границы объекта с точки зрения наблюдателя. Инкапсуляция — это процесс отделения друг от друга элементов объекта, определяющих его устройство и поведение; инкапсуляция также служит для того, чтобы изолировать внешнее поведение объекта от его внутреннего устройства. Иерархия — это упорядочение абстракций, средство классификации объектов, систематизация связей между объектами. Модульность — это представление системы в виде совокупности обособленных сегментов, связь между которыми обеспечивается посредством связей между классами, определяемыми в этих сегментах. Лекция 16. 12. 13 г. 13
Абстрагирование В ООП программа строится как совокупность взаимодействующих объектов. Объект - это сущность, обладающее значением (состоянием), типом (поведением) и индивидуальностью. Когда программист выделяет объекты в предметной области, он обычно абстрагируется (отвлекается) от большинства их свойств, концентрируясь на существенных для задачи свойствах. Взаимодействие объектов заключается в вызове методов одних объектов другими. Иногда говорят, что объекты посылают другу сообщения. Сообщения - это запросы к объекту выполнить некоторые действия. Объекты описываются не индивидуально, а с помощью классов. Класс - объект, являющийся шаблоном объекта. Объект, созданный на основе некоторого класса, называется экземпляром класса. Фактически класс – это тип данных. Лекция 16. 12. 13 г. 14
Абстрагирование Простейший пример абстракции – электрическая лампочка, для которой определены, как минимум, два присущих ей состояния: включена (светит) и выключена (не светит): Turn. On() – включить Turn. Off() – выключить Is. Turned. On() – узнать состояние Все лампочки обладают примерно одинаковым поведением, хотя их реализации могут отличаться. Принцип работы вольфрамовых, галогенных, светодиодных ламп различен, однако любая из них распознается нами как осветительное устройство, которое можно включить и выключить. Какие физические и химические процессы участвуют в ходе перехода лампы из выключенного состояния во включенное, конкретного потребителя может не интересовать. Конструируя абстрактную модель, программист в первую очередь пытается выразить наиболее общие свойства моделируемых объектов. Лекция 16. 12. 13 г. 15
Абстрагирование Объявление на языке C++, которое соответствует данной модели: class 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 *plight 1; plight 1 = new Light; // Объект размещается в динамической памяти plight 1 ->Turn. On(); plight 1 ->Turn. Off(); Лекция 16. 12. 13 г. 16
Абстрагирование Итак, абстрагирование отвечает за внешнее поведение объекта ("интерфейс") — так ведут себя все объекты данного класса. Абстрагирование направлено на наблюдаемое поведение объекта, а не на его внутреннюю реализацию. Лекция 16. 12. 13 г. 17
Инкапсуляция тесно связана с абстракцией: абстрагирование направлено на наблюдаемое поведение объекта, а инкапсуляция занимается внутренним устройством и поэтому определяет четкие границы между различными абстракциями. Другое важное свойство инкапсуляции — сокрытие реализации, что даёт возможность внесения изменений в реализацию класса без изменения других классов. Для простейшей абстракции электрической лампы уровень реализации связан с представлением состояния лампы. Например, это можно сделать с использованием булевской переменной turned. On. Знак "минус" у атрибута turned. On на диаграмме показывает, что доступ к нему извне закрыт: изменить значение этой переменной можно только используя функции, составляющие внешний интерфейс: Turn. On() и Turn. Off (). Лекция 16. 12. 13 г. 18
Инкапсуляция Инкапсулированный объект не имеет видимых для пользователя компонентов, независимо от того, насколько сложным является его реальное представление (т. е. его внутренняя кодировка). Объект является инкапсулированным, если и только если он представляет собой скаляр, т. е. термины «инкапсулированный» и «скалярный» означают в точности одно и то же. Лекция 16. 12. 13 г. 19
Инкапсуляция Уточненное определение класса Light на C++: class Light { // Объявление класса Light private: bool turned. On; public: void Turn. On(); void Turn. Off(); bool Is. Turned. On(); }; Реализация функций-членов класса Light: void Light: : Turn. On() { turned. On = true; } void Light: : Turn. Off() { turned. On = false; } bool Light: : Is. Turned. On() { returned. On; } Лекция 16. 12. 13 г. 20
Инкапсуляция В C++ управление доступом и видимостью реализовано достаточно гибко. Члены класса могут быть объявлены: Ø открытыми для всех внешних объектов (public), Ø полностью закрытыми для доступа извне (private) и Ø защищенными, т. е. видимыми для всех экземпляров данного класса и его подклассов (protected). Лекция 16. 12. 13 г. 21
Отношения между классами Существует четыре базовых вида отношений между объектно-ориентированными абстракциями: üотношение обобщения (generalization) описывает отношение между общей сущностью и ее конкретным воплощением (например, один класс является специализацией другого класса) üотношение ассоциации (association) описывает структурное отношение, показывающее, что объекты одного типа некоторым образом связаны с объектами другого типа (например, находятся в отношении типа "часть/целое") üотношение зависимости (dependency) описывает отношение использования, при котором изменение в спецификации одного класса может повлиять на класс, его использующий (например, объекты некоторого класса передаются в качестве аргументов функциям-членам другого класса) üотношение реализации (realization) описывает семантическое отношение, при котором класс гарантирует выполнение контракта, определяемого некоторым интерфейсом. Обычно определяется применительно к интерфейсам и классам, которые реализуют (implement) интерфейсы Лекция 16. 12. 13 г. 22
Отношение обобщения Основной и самой распространенной формой отношения обобщения является наследование. Базовый (родительский) класс – более общий Ещё одно название - суперкласс Производный (дочерний) класс – более специализированный Другое название – подкласс Лекция 16. 12. 13 г. 23
Наследование представляет отношение «общее/частное» ( «родитель/потомок» или «is a – один из, является» ). Наследование – это такое отношение между классами, при котором один класс (производный) повторяет ( «заимствует» ) структуру и поведение другого класса (базового). При наследовании базовый класс реализует наиболее общую модель сущности, а производный класс уточняет и развивает определение базового класса. При этом производный класс может расширять, изменять или ограничивать структуру и поведение своего базового класса. Лекция 16. 12. 13 г. 24
Наследование Определение производного класса Colored. Light: const int Last. Color = 4; enum Color. Enum {WHITE, RED, GREEN, YELLOW, BLUE}; class Colored. Light : public Light { private: int color; public: void Set. Color(int); int Get. Color(); const char *To. String(); }; void Colored. Light: : Set. Color (int custom. Color) { if(custom. Color < 0 || custom. Color > Last. Color) { cout<<"Incorrect color" <<endl; return; } color = custom. Color; } int Colored. Light: : Get. Color(){ return color; } const char* Colored. Light: : To. String() { switch(color) { case WHITE : return "white"; case RED : return "red"; case GREEN : return "green"; case YELLOW: return "yellow"; case BLUE : return "blue"; default : return "white"; } } Лекция 16. 12. 13 г. 25
Наследование Использование объектов производного класса Colored. Light: Colored. Light c 1; c 1. Set. Color(0); // Вызов «собственной» функции c 1. Turn. On(); // Вызов функции базового класса Colored. Light *p. C 1 = new Colored. Light; p. C 1 -> Turn. On(); Объект c 1 класса Colored. Light одновременно является одним из ( «is a» ) объектов класса Light, т. е. с ним можно обращаться как с объектом класса Light. В рассмотренном примере производный класс Colored. Light расширяет структуру и поведение базового класса Light. Лекция 16. 12. 13 г. 26
Отношение ассоциации Данное отношение иллюстрирует абстракция гирлянды (Garland) — набора цветных лампочек. Объект класса Garland должен предоставлять возможность работать с множеством объектов класса Colored. Light: Класс Garland находится в отношении ассоциации к классу Colored. Light. Лекция 16. 12. 13 г. 27
Отношение ассоциации Объявление класса Garland: typedef Colored. Light* PColored. Light; class Garland { private: PColored. Light *cl; int size; public: void Create(int); void Destroy(); void Turn. On(); void Turn. Off(); PColored. Light Get. Light(int); private: void Random. Light(PColored. Light&); }; Лекция 16. 12. 13 г. 28
Отношение ассоциации void Garland: : Create(int custom. Size) { cl = new PColored. Light[custom. Size]; assert(cl != 0); for(int i=0; i < custom. Size; i++) Random. Light(cl[i]); size = custom. Size; } void Garland: : Destroy() { if(cl != 0 ) { for(int i=0; i < size; i++) delete cl[i]; delete[] cl; } } PColored. Light Garland: : Get. Light(int number) { return cl[number]; } void Garland: : Turn. On() { for(int i=0; i < size; i++) cl[i]->Turn. On(); } void Garland: : Turn. Off() { for(int i=0; i < size; i++) cl[i]->Turn. Off(); } void Garland: : Random. Light(PColored. Light& ref. PLight) { int random. Color = rand() % (Last. Color+1); ref. PLight = new Colored. Light; ref. PLight->Set. Color((Color. Enum)random. Color); } Лекция 16. 12. 13 г. 29
Функция (макрос) assert Функция assert выводит диагностическое сообщение и завершает вызванный процесс, если expression ложно (= 0); в противном случае никакого действия не выполняется. Функция assert обычно используется для обнаружения логических ошибок в программе. Выражение expression должно быть задано таким образом, чтобы оно было истинным, если программа выполняется как намечено. Лекция 16. 12. 13 г. 30
Отношение ассоциации Основная программа, иллюстрирующая создание объекта класса Garland и его использование //Ex 209. cpp - гирлянда void main () { srand((unsigned)time(NULL)); Garland garland; garland. Create(10); for(int i=0; i < 10; i++ ) { Colored. Light *p. Cl = garland. Get. Light(i); cout << p. Cl->To. String() << endl; } garland. Destroy(); } Лекция 16. 12. 13 г. 31
Виды ассоциаций Отношение ассоциации соответствует наличию произвольного отношения или взаимосвязи между классами. Данное отношение обозначается сплошной линией с возможными дополнительными символами, которые характеризуют специальные свойства ассоциации. Студент 1. . * Предмет Разновидностями ассоциации являются отношения агрегирования и композиции. Агрегация (aggregation) – специальная форма ассоциации, которая служит для представления отношения типа "часть-целое" между агрегатом (целое) и его составной частью. Графически отношение агрегации изображается сплошной линией, оканчивающейся не закрашенным внутри ромбом. Ромб указывает на класс-контейнер (агрегат). Системный блок MB CPU RAM Лекция 16. 12. 13 г. HDD DVD ROM 32
Виды ассоциаций Композиция (composition) – разновидность отношения агрегации, при которой составные части не могут выступать в отрыве от целого, т. е. с уничтожением целого уничтожаются и все его составные части. Целое является «владельцем» своих частей, которые ему полностью подконтрольны: агрегат создает части и он же их уничтожает. Практический пример – окно графического интерфейса программы, которое может состоять из строки заголовка, полос прокрутки, строки состояния, рабочей области и главного меню. Лекция 16. 12. 13 г. 33
Отношение зависимости – это такое отношение между классами, когда изменение в спецификации или реализации одного класса влияет на спецификацию или реализацию другого класса. Пример данного отношения представлен ниже. В данном случае два метода класса Garland. App, определяющего функциональность приложения, принимают аргументы, тип которых зависит от типа Garland. Таким образом, на реализацию методов Create. Garland() и Test. Garland() могут влиять изменения в определении класса Garland. App -cl[] : Colored. Light* -size : int +Create() : void +Destroy() : void +Turn. On() : void +Turn. Off() : void +Get. Light(): Colored. Light* -Random. Light() : void +main() -Create. Garland(Garland**, int) -Test. Garland(Garland*) Класс-клиент (пользуется услугами сервера) Класс-сервер (предоставляет услуги клиентам) Лекция 16. 12. 13 г. 34
Отношение зависимости Определение класса Garland. App: class Garland. App { static void Create. Garland(Garland**, int); static void Test. Garland(Garland*); public: static void main() { Garland *g; srand( (unsigned) time( NULL ) ); Create. Garland( &g, 1 2 ); Test. Garland( g ); delete [] g; } }; void Garland. App: : Create. Garland(Garland **g, int size) { cout << "Creating the garland. . . "; (*g) = new Garland; assert( *g != 0 ); (*g) -> Create(size); cout << "OK. " << endl; } void Garland. App: : Test. Garland(Garland *g) { PColored. Light next. Light; cout << "Turning the garland on. . . "; g -> Turn. On(); cout << "OK. " << endl; cout << "Current garland state: " << endl; for( int i = 0; i < g -> Get. Size(); i++ ) { next. Light = g -> Get. Light( i ); cout << next. Light -> To. String() << endl; } cout << endl << "Turning the garland off. . . "; g -> Turn. Off(); cout << "OK. " << endl; } Лекция 16. 12. 13 г. 35
Отношение зависимости Тестирование Garland. App: //Ex 210. cpp - гирлянда #include <iostream> #include <string> #include <cstdlib> #include <ctime> #include <cassert> using namespace std; . . . int main(void) { Garland. App: : main(); system("PAUSE"); return 0; } Лекция 16. 12. 13 г. 36
Отношение реализации Обычно отношение реализации используется для определения отношения между классом-интерфейсом и классом, реализующим интерфейс. Интерфейс определяет набор действий (методов), характерных для объектов, обладающих определенными свойствами. Интерфейс определяет так называемый контракт, который должен реализовать класс-наследник данного интерфейса. Лекция 16. 12. 13 г. 37
Отношение реализации Определение интерфейсного класса: class IColorable { public: virtual void Set. Color( Color. Enum ) = 0; virtual Color. Enum Get. Color() = 0; }; class Colorable. Object : public IColorable { protected: Color. Enum color; public: void Set. Color( Color. Enum color ) {this->color = color; } Color. Enum Get. Color() { return color; } const char * To. String() { switch( Get. Color() ) { case WHITE : return "white"; case RED : return "red"; case GREEN : return "green"; case YELLOW: return "yellow"; case BLUE : return "blue"; default : return "white"; } } }; class Colored. Light : public Light, public Colorable. Object { }; Лекция 16. 12. 13 г. 38
Диаграмма классов Garland. App Лекция 16. 12. 13 г. 39
Виды структурных иерархий Иерархическая декомпозиция и иерархическая организация программного обеспечения образуют один из основных систематических методов преодоления сложности ПО. Отношение ассоциации Агрегирование Простое агрегирование Композитное агрегирование is-part-of is-a is-like-a виды структурных иерархий Лекция 16. 12. 13 г. 40
Модульность Различают два уровня представления модульности – физический и логический. Физический уровень имеет дело со структурой файлов и каталогов, в которых хранятся отдельные модули, из которых составлен проект. В C/C++ физическим модулем является отдельный сегмент компиляции, т. е. объектный файл. Физическому модулю соответствует файл с исходным текстом C/C++, к которому подключены необходимые заголовочные файлы, образующие интерфейс между модулями. Язык С и первые реализации языка C++ не предоставляли других механизмов реализации модульности, поэтому физический модуль являлся одновременно и логическим модулем. Модули взаимодействуют друг с другом посредством: Øвызовов функций, Øчтения-записи глобальных данных, Øпредоставления доступа к определениям типов и констант. Лекция 16. 12. 13 г. 41
Лекция 16. 12. 13 г. 42
Модульная организация проекта Garland Application Схема иллюстрирует модульность на логическом уровне. Часто используется модель организации проекта по схеме: один физический модуль — один класс, при этом исходные тексты размещаются в двух файлах: заголовочном *. h с определением класса и файле *. cpp, содержащем реализацию методов класса. colors “interface” Colorable “enumeration” Color. Enum “class” Colorable. Object lights “class” Light app “class” Colored. Light garlands “class” Garland. App “class” Garland Лекция 16. 12. 13 г. 43
Модульность Работа над проектом, организованным на основе физической модульности, затрудняется с ростом сложности задач и числа программистов, участвующих в разработке. Объектно-ориентированная парадигма с концепцией класса как фрагмента кода, ориентированного на его повторное использование, также предъявляет дополнительные требования к обеспечению модульности средствами языка. Возникает необходимость организации проекта на основе логической взаимосвязи его компонентов, позволяющей обеспечить: üкорректное разрешение конфликтов глобальных имен (переменных, констант, типов, функций), üразделение реализации и интерфейсной части, üобособление библиотечного и клиентского кода. Основной механизм логического группирования программных объектов, поддерживаемый современными языками программирования — это механизм пространств имен. Лекция 16. 12. 13 г. 44