Лекции 30 -32 Тема № 6 Алгоритмизация и программирование
n Объектноориентированное программирование (objectorient programming, OOP) – это методология программирования, основанная на представлении программы в виде совокупности объектов, каждый из которых является экземпляром определенного класса, а классы образуют иерархию наследования.
n n n В данном определении можно выделить три части: 1) ООР использует в качестве базовых элементов объекты, а не алгоритмы; 2) Каждый объект является экземпляром какого-либо определенного класса; 3) Классы организованы иерархически. Программа будет объектноориентированной только при соблюдении всех требований.
n n Объект обладает состоянием, поведением и идентичностью; Структура и поведение схожих объектов определяет общий для них класс, термины «экземпляр класса» и «объект» взаимозаменяемы.
n Состояние объекта характеризуется перечнем (обычно статическим) всех свойств (атрибутов) данного объекта и текущими (обычно динамическими) значениями каждого из этих свойств.
n n Объекты не существуют изолированно, а подвергаются воздействию или сами воздействуют на другие объекты. Поведение – это то, как объект действует и реагирует; поведение выражается в терминах состояния объекта и передачи сообщения. Иными словами, поведение объекта – это его наблюдаемая и проверяемая извне деятельность.
n Операцией (сообщением) называется определенное воздействие одного объекта на другой с целью вызвать соответствующую реакцию. n Операции, выполняемые над данным объектом, называются методами и входят в описание класса объекта.
Поведение объекта определяется выполняемыми над ним операциями и его состоянием, причем некоторые операции имеют побочное действие: они изменяют состояние объекта. n Отсюда можно дать другое определение состояния объекта: состояние объекта представляет суммарный результат его поведения. n
n n n На практике над объектами совершаются операции пяти видов: 1) Модификатор - операция, которая изменяет состояние объекта. 2) Селектор- операция, считывающая состояние объекта, но не меняющая состояние. 3) Итератор- операция, позволяющая организовать доступ ко всем частям объекта в строго определенной последовательности. 4) Конструктор- операция создания и/или его инициализации. 5) Деструктор- операция, освобождающая состояние объекта и/или разрушающая сам объект. В чистых объектно-ориентированных языках операции могут быть только методами.
n Идентичность – это такое свойство объекта, которое отличает его от всех других объектов. n Класс – это некое множество объектов, имеющих общую структуру и общее поведение.
n n n Классы и объекты – это отдельные, но тесно связанные понятия. Каждый объект является экземпляром класса, класс может порождать любое количество объектов. Классы статичны, т. е. все их особенности и содержание определены в процессе компиляции программы. Любой объект относится к строго фиксированному классу. Сами объекты, напротив, в процессе выполнения программы создаются и уничтожаются.
n Событие –то, что может изменить состояние объекта. n Изменение состояние объекта это результат использования соответствующего метода объекта (обработчика события).
Отличия в объектной модели в Object Pascal. n В Object Pascal классами называются специальные типы, которые содержат поля, методы и свойства. Класс служит образцом для создания конкретных экземпляров, которые называются объектами.
n Важным отличием классов от других типов является то, что объекты класса всегда располагаются в динамической памяти (куче), поэтому объектпеременная представляет собой лишь указатель на динамическую область памяти. Однако при ссылке на содержимое объекта запрещается использовать символ «^» за именем объекта.
n В Object Pascal введена новая языковая конструкция — свойства, введены новые директивы управления областью доступа — published, protected, automated, введены специальные директивы для динамических (dynamic), перекрываемых (override) и абстрактных (abstract) методов.
В основе объктноориентированного программирования лежат три фундаментальных принципа: n инкапсуляция, n наследование, n полиморфизм.
Инкапсуляция n Класс представляет собой единство трех сущностей – полей, методов и свойств. Объединение этих сущностей в единое целое и называется инкапсуляцией. Инкапсуляция позволяет во многом изолировать класс от остальных частей программы, сделать его «самодостаточным» для решения конкретной задачи.
n Инкапсуляция полезна потому, что помогает перечислить связанные методы и данные под одной эгидой, а так же скрывать детали реализации, которые не требуют своей демонстрации или могут быть изменены в будущих версиях объекта.
n n n Хорошо сконструированные объекты должны состоять из двух частей: 1) Данных и разделов реализации, скрытых от программистов, использующих объект (с целью защиты данных от несанкционированных изменений) 2) Набора интерфейсных элементов, предоставляющих возможность программистам обращаться со скрытыми методами и данными.
n Для надежной защиты данных и разделов реализации в Delphi определены директивы объявлений разделов описаний в классе, позволяющие контролировать область видимости описанных компонентов.
n Инкапсуляция представляет собой мощное средство обмена готовыми к работе программными заготовками. Библиотека классов Delphi – это фактически набор «кирпичиков» , созданных программистами Borland для построения программ.
Наследование. n n Любой класс может быть порожден от другого класса. Для этого при его объявлении указывается имя класса родителя: Tchild. Class = class (TParent. Class)
n Дочерний класс автоматически наследует поля, методы и свойства своего родителя и может добавлять их новыми. Таким образом, принцип наследования обеспечивает поэтапное создание сложных классов и разработку собственных библиотек классов.
n Все классы Object Pascal порождены от единственного родителя – класса Tobject. Этот класс не имеет полей и свойств, но включает в себя методы самого общего назначения, обеспечивающие весь жизненный цикл любых объектов – от их создания до уничтожения.
Программист не может создать класс, который бы не был дочерним классом Tobject, т. к. следующие два объявления идентичны: n TMy. Class= class (Tobject) n TMy. Class= class n
В Турбо Паскале для создания объектов используются три зарезервированных слова: object, constructor, destructor и три стандартные директивы: private, public и virtual. Зарезервированное слово object используется для описания объекта. Описание объекта должно помещаться в разделе описания типов: type My. Object = object {Поля объекта} {Методы объекта} end; Если объект порождается от какого-либо родителя, имя родителя указывается в круглых скобках сразу за словом object: type My. Descendant. Object = object(My. Object) … end;
n Принцип наследования приводит к созданию ветвящегося дерева классов. Каждый потомок дополняет возможности своих родителей и передает их своим потомкам.
Полиморфизм. n Полиморфизм – это свойство классов решать схожие по смыслу проблемы различными способами. В рамках ООП поведение класса определяется набором входящих в него методов. Изменяя алгоритм того или иного метода в потомках класса, можно придавать этим потомкам отсутствующие у родителя специфические свойства.
n n Для изменения метода необходимо перекрыть его в потомке, т. е. объявить в потомке одноименный метод и реализовать в нем нужные действия. В результате в объекте- родителе и объекте-потомке будут действовать два одноименных метода, имеющих различные алгоритмы. В ООП полиморфизм достигается также виртуализацией методов, позволяющей родительским методам обращаться к методам своих потомков.
n n Общую структуру описания класса можно представить в таком виде: Type n Имя. Класса = class (Имя. Родит. Класса) Опубликованные поля; Опубликованные методы; Опубликованные свойства; n private n n n Приватные поля; Приватные методы; Приватные свойства;
n public n Общедоступные поля; Общедоступные методы; Общедоступные свойства; n protected n n n Защищенные поля; Защищенные методы; Защищенные свойства;
n published n Опубликованные поля; Опубликованные методы; Опубликованные свойства; n automated n n n Поля реализации ОLE-механизма; Методы реализации ОLE-механизма; Свойства реализации ОLE-механизма; n end; n n
n Директивы published, protected и automated являются нововведением Object Pascal. Причем директива automated появилась только в 32 -разрядной версии Delphi.
n n Директива public Поля, свойства и методы, описанные в разделе public, называются общедоступными или публичными. Поля, свойства и методы, расположенные сразу после заголовка класса по умолчанию принимаются общедоступными.
n n Директива published Поля, свойства и методы, описанные в разделе published, называются опубликованными. Их область видимости эквивалентна области видимости общедоступных описаний. Отличие состоит в том, что для опубликованных полей, свойств и методов генерируется дополнительная информация об их типе, которая доступна во время выполнения.
n Благодаря этой информации при создании и помещении компонент на палитру, их опубликованные свойства становятся доступными в Object Inspector. Однако такие свойства могут быть только порядковых типов, вещественных типов, типа String, типа "множество" с максимальным числом элементов не более 15, а также классового типа и типа "указатель на метод".
n n Директива automated Поля, свойства и методы, описанные в разделе automated, назовем по имени директивы автоматическими. Их область видимости эквивалентна области видимости общедоступных описаний. Отличие состоит в том, что для автоматических свойств и методов генерируется дополнительная информация, которая используется для реализации OLE-механизма.
n n Директива private Поля, свойства и методы, описанные в разделе private, называются приватными или личными. Их область видимости ограничивается пределами того модуля, в котором они описаны.
n n Директива protected Поля, свойства и методы, описанные в разделе protected, называются защищенными. Директива protected обеспечивает ограничение доступа. Но если private обеспечивает защиту на уровне исходного модуля, где сделаны описания, то protected ограничивает доступ на уровне исходного класса. То есть, если protected-описания принадлежат, например, классу TBase. Class в модуле Uprotec 1, то объекты класса TBase. Class, объявленные в "своем" модуле, могут работать с ними, а объекты того же класса, объявленные в других модулях, импортирующих модуль Uprotec 1, не могут.
n n n n Полями называются инкапсулированные в класса данные. Поля могут быть любого типа, в том числе классами. Принято имя поля начинать с буквы F (Field). Например: type TMy. Class = class(TForm) FInt: integer; FWidth: word; FPrim 1: Tobject; FFam: string; end;
n Каждый объект получает уникальный набор значений полей, но общий для всех объектов для данного класса набор методов и свойств. Понятие инкапсуляции и хороший стиль объектно-ориентированного программирования требуют, чтобы обращение к полям объектов выполнялось исключительно посредством методов. Однако в ОР разрешается обращаться к полям и напрямую. Для этого используются составные имена полей:
n n n n n Var My. Obj 1: Tmy. Class; … begin … My. Obj 1. ffam: =’Иванов’; My. Obj 1. fwidth: =500; … end; Класс-потомок получает все поля всех своих предков и может пополнить их своими, но он не может переопределить их или удалить.
n n n Инкапсулированные в классе процедуры и функции называются методами. Они объявляются, так же как и обычные подпрограммы: type TForm 1 = class(TForm) procedure Form. Click(Sender: TObject); function Key. Down : Word; end;
n n n n n Доступ к методам класса осуществляется с помощью составных имен: Var Form 1: TForm 1; Begin … Form 1. Form. Click(…) Key: = Form 1. Key. Down … End;
n Методы класса могут перекрываться в потомках. Потомок класса может иметь сходную по названию процедуру или функцию, которая будет выполнять другое действие. В ООП возможно статическое и динамическое замещение методов.
n n Ранним связыванием называется процесс статического связывания методов с объектами во время компиляции. По умолчанию все методы являются статическими. Вызов статических методов выполняется точно также, как и вызовы обычных процедур и функций. Определение конкретных адресов статических методов и их связывание выполняются во время компиляции. Поэтому такие методы не могут быть переопределены для реализации полиморфизма.
n n Поздним связыванием называется процесс динамического связывания методов с объектами во время выполнения. Раннее связывание реализовано для статических методов, а позднее — для виртуальных, динамических методов и методов обработки сообщений.
n Статическое связывание обладает существенным преимуществом над всеми остальными видами адресации, поскольку обеспечивает самую высокую скорость вызова. Недостатком же фиксированной адресации является то, что статические методы не подлежат изменениям в классах-потомках. При обращении к статическому методу компилятор точно знает класс, к которому данный метод принадлежит.
Динамическое замещение методов n n Основное различие между виртуальными и динамическими методами состоит в том, что для их реализации компилятор использует внутренние таблицы различной структуры: для виртуальных методов — ТВМ (таблицу виртуальных методов), а для динамических методов — ТДМ (таблицу динамических методов). В ТВМ и ТДМ находятся адреса точек входа динамических и виртуальных методов. При каждом обращении к замещаемому методу компилятор вставляет код, позволяющий извлечь адрес точки входа в подпрограмму из той или иной таблицы.
n n В классе потомке можно переопределить родительский метод с помощью директивы OVERRIDE. Получив это указание компилятор создаст код, который на этапе выполнения программы поместит в родительскую таблицу точку входа виртуального метода класса-потомка, что позволит родителю выполнить нужное действие с помощью нового метода.
n n Виртуальные методы Объявление виртуального метода в базовом классе выполняется с помощью ключевого слова virtual, а его перекрытие в производных классах - с помощью ключевого слова override. Перекрытый метод должен иметь точно такой же формат (список параметров, а для функций ещё и тип возвращаемого значения), что и перекрываемый:
n type TPeople = class Name: string; procedure Get. Name; virtual; //Виртуальный метод end; type TStudent = class(TPeople). . . procedure Get. Name; override; end;
n n Суть виртуальных методов в том, что они вызываются по фактическому типу экземпляра, а не по формальному типу, записанному в программе. Работа виртуальных методов основана на косвенном вызове подпрограмм. При косвенном вызове команда вызова подпрограммы оперирует не адресом подпрограммы, а адресом места в памяти, где хранится адрес подпрограммы.
n Для каждого виртуального метода создаётся процедурная переменная, но её наличие и использование скрыто от программиста. Все процедурные переменные с адресами виртуальных методов пронумерованы и хранятся в таблице, называемой таблицей виртуальных методов. Такая таблица создаётся одна для каждого класса объектов, и все объекты этого класса хранят на неё ссылку.
n Вызов виртуального метода происходит следующим образом: 1. Через объектную переменную выполняется обращение к занятому объектом блоку памяти. 2. Далее из этого блока извлекается адрес таблицы виртуальных методов (он записан в четырёх первых байтах). 3. На основании порядкового номера виртуального метода извлекается адрес соответствующей подпрограммы. 4. Вызывается код, находящийся по этому адресу.
n При построении иерархии классов часто возникает ситуация, когда работа виртуального метода в базовом классе не известна и наполняется содержанием только в наследниках. Директива abstract записывается после слова virtual и исключает необходимость написания кода виртуального метода для данного класса. Такой метод называется абстрактным. Свою реализацию эти методы получают в законченных наследниках.
Динамические методы. n n Динамические методы описываются с помощью директивы Dynamic. Переопределяются так же как виртуальные с помощью директивы OVERIDE. • В список динамических методов конкретного класса включены только адреса методов, описанных в данном классе. Поиск необходимого метода производится в обратном порядке дерева наследования. Если метод не найден в самом последнем дочернем классе, то поиск продолжается в его предке и так далее до TObject.
n Виртуальные методы вызываются максимально быстро, но платой за это является большой размер системных таблиц, с помощью которых определяются их адреса. Размер этих таблиц начинает сказываться с увеличением числа классов в иерархии. Динамические методы вызываются несколько дольше, но при этом таблицы с адресами методов имеют более компактный вид, что способствует экономии памяти.
n n Особой разновидностью методов являются конструкторы и деструкторы. Создание объекта включает выделение памяти под экземпляр и инициализацию его полей, а разрушение - очистку полей и освобождение памяти. Действия по инициализации и очистке полей специфичны для каждого конкретного класса объектов.
n По этой причине язык Delphi позволяет переопределить стандартный конструктор Create и стандартный деструктор Destroy для выполнения любых полезных действий. Можно даже определить несколько конструкторов и деструкторов (имена им назначает сам программист), чтобы обеспечить различные процедуры создания и разрушения объектов.
n n Объявление конструкторов и деструкторов похоже на объявление обычных методов с той лишь разницей, что вместо зарезервированных слов function и procedure используются слова constructor и destructor. Пример: type TPeople = class Name: string; Family: string; procedure Get. Name; procedure Get. Family; constructor Create; destructor Destroy; end;
n n Возможная реализация: Constructor TPeople. Create; begin TPeople. Name : = ' '; TPeople. Family : = ' '; end; Destructor TPeople. Destroy; begin //Пока ничего не делаем end;
n Если объект содержит встроенные объекты или другие динамические данные, то конструктор - это как раз то место, где их нужно создавать. Конструктор применяется к классу или к объекту. Конструктор создаёт новый объект только в том случае, если перед его именем указано имя класса. Если указать имя уже существующего объекта, он поведёт себя по-другому: не создаст новый объект, а только выполнит код, содержащийся в теле конструктора.
n n n Если он применяется к классу People : = TPeople. Create; В динамической памяти выделяется место для нового объекта. Выделенная память заполняется нулями. Затем выполняются заданные программистом действия конструктора. Ссылка на созданный объект возвращается в качестве значения конструктора. Тип возвращаемого значения совпадает с типом класса, использованного при вызове (в нашем примере это тип TPeople).
n Таким образом, хотя на первый взгляд синтаксис конструктора схож с вызовом процедуры (не определено возвращаемое значение), но на самом деле конструктор - это функция, возвращающая созданный и инициализированный объект.
n n Если конструктор применяется к объекту, People. Create; то конструктор выполняется как обычный метод. Другими словами, новый объект не создаётся, а происходит повторная инициализация полей существующего объекта.
n n n Деструктор уничтожает объект к которому применяется: People. Destroy; В результате выполняются: Заданный программистом код завершения. Освобождается занимаемая объектом динамическая память. В теле деструктора обычно должны уничтожаться встроенные объекты и динамические данные, как правило, созданные конструктором.
n Вызов деструктора для несуществующих объектов недопустим и при выполнении программы приведёт к ошибке. Чтобы избавить программистов от лишних ошибок, в объекты ввели предоставленный метод Free, который следует вызвать вместо деструктора. Метод Free сам вызывает деструктор Destroy, но только в том случае, если значение объектной переменной не равно nil.
Свойства n При работе с объектом свойства выглядят как поля: они принимают значения и участвуют в выражениях. Но в отличии от полей свойства не занимают место в памяти, а операции их чтения и записи ассоциируются с обычными полями и методами. Это позволяет создавать необходимые сопутствующие эффекты при обращении к свойствам.
n n Объявление свойства выполняется с помощью зарезервированного слова property, например: type TPeople = class FName: string; procedure Get. Name; property Name: string read FName write Get. Name; // Свойство end;
n n Ключевые слова read и write называются спецификаторами доступа. После слова read указывается поле или метод, к которому происходит обращение при чтении (получении) значения свойства, а после write - поле или метод, к которому происходит обращение при записи (установке) значения свойства. Чтобы имена свойств не совпадали с именами полей, последние принято писать с буквы F.
n n Обращение к свойствам выглядит в программе как обращение к полям: var People: TPeople; Get: string; . . . People. Name : = 'Сергей'; Get : = People. Name;
n Если один из спецификаторов доступа опущен, то значение свойства можно либо только читать (задан спецификатор read), либо только записывать (задан спецификатор write). Следующий пример объявлено свойство, значение которого можно только читать: type TPeople = class FName: array of string; function Get. Name: integer; property Name: integer read Get. Name; end; function TPeople. Get. Name: integer; begin Result : = Length(FName); end;
n n n В отличии от полей свойства не имеют адреса в памяти, поэтому к ним запрещено применять операцию @. Как следствие, их нельзя передавать в var- и out-параметрах процедур и функций. Технология объектноориентированного программирования в среде Delphi предписывает избегать прямого обращения к полям, создавая вместо этого соответствующие свойства.
n n n Методы получения (чтения) и установки (записи) значений свойств подчиняются определенным правилам. Метод чтения свойства - это всегда функция, возвращающая значение того же типа, что и тип свойства. Метод записи свойства - это обязательно процедура, принимающая параметр того же типа, что и тип свойства. В остальных отношениях это обычные методы объекта.
n n Пример методов чтения и записи: type TPeople = class FName: boolean; procedure Set. Name(const AName: boolean); function Get. Name: integer; property Name: boolean read FName write Set. Name; property Count: integer read Get. Name; end;
n Использование методов для получения и установки свойств позволяет проверить корректность значения свойства, сделать дополнительные вычисления, установить значения зависимых полей и так далее.
Достоинства ООП n n n использование при программировании понятий, близких к предметной области; возможность успешно управлять большими объемами исходного кода благодаря инкапсуляции, то есть скрытию деталей реализации объектов и упрощению структуры программы; возможность многократного использования кода за счет наследования; сравнительно простая возможность модификации программ; возможность создания и использования библиотек объектов. 78
Недостатки ООП n n некоторое снижение быстродействия программы, связанное с использованием виртуальных методов; идеи ООП не просты для понимания и в особенности для практического использования; для эффективного использования существующих объектно-ориентированных систем требуется большой объем первоначальных знаний; неграмотное применение ООП может привести к значительному ухудшению характеристик разрабатываемой программы. 79