Принципы объектного программирования
Инкапсуляция
Под этим словом в программировании понимается языковая конструкция, способствующая объединению данных с методами, обрабатывающими эти данные, а также механизм ограничения доступа к определённым компонентам объекта. Инкапсуляция делает объекты похожими на маленькие программные модули, в которых скрыты внутренние данные и у которых имеется интерфейс использования в виде подпрограмм. Переход от понятий «структура данных» и «алгоритм» к понятию «объект» значительно повысил ясность и надежность программ. Для ограничения доступа используется разграничение видемости. Для этого предназначены специальные директивы, с помощью которых класс делиться на секции. Они указаны ниже.
Наследование
Этот простой принцип означает, что если вы хотите создать новый класс объектов, который расширяет возможности уже существующего класса, то нет необходимости в переписывании заново всех полей, методов и свойств. Вы объявляете, что новый класс является потомком (или дочерним классом) имеющегося класса объектов, называемого предком (или родительским классом), и добавляете к нему новые поля, методы и свойства. Процесс порождения новых классов на основе других классов называется наследованием. Новые классы объектов имеют как унаследованные признаки, так и, возможно, новые. Например, класс собаки унаследовал многие свойства своих предков — волков.
Класс не может быть унаследован от другого класса, если тот является закрытым для наследования. Это указывается с помощью ключевого слова final.
Полиморфизм
Он означает, что в производных классах вы можете изменять работу уже существующих в базовом классе методов. При этом весь программный код, управляющий объектами родительского класса, пригоден для управления объектами дочернего класса без всякой модификации.
Статический и динамический полиморфизм
Полиморфизм может пониматься как наличие точек кастомизации в коде, когда один и тот же написанный программистом фрагмент кода может означать разные операции в зависимости от чего-либо. В одном случае конкретный смысл фрагмента зависит от того, в каком окружении код был построен. Это т.н. статический полиморфизм. Шаблоны в Си++ реализуют именно статический полиморфизм. Если в коде шаблонного класса вызвана, например, std::sort, то реальный смысл вызова зависит от того, для каких именно типовых параметров будет развернут данный шаблон - вызовется одна из std::sort<T>. В другом случае конкретный смысл фрагмента определяется только на этапе исполнения и зависит от того, как именно и где именно был построен данный объект. Это обычный, динамический полиморфизм, реализуется через виртуальные методы.
Полиморфизм включения
Этот полиморфизм называют чистым полиморфизмом. Применяя такую форму полиморфизма, родственные объекты можно использовать обобщенно. С помощью замещения и полиморфизма включения можно написать один метод для работы со всеми типами объектов TPerson. Используя полиморфизм включения и замещения можно работать с любым объектом, который проходит тест «is-A». Полиморфизм включения упрощает работу по добавлению к программе новых подтипов, так как не нужно добавлять конкретный метод для каждого нового типа, можно использовать уже существующий, только изменив в нем поведение системы. С помощью полиморфизма можно повторно использовать базовый класс; использовать любого потомка или методы, которые использует базовый класс. Известен также как принцип подстановки Барбары Лисков. Пусть у нас есть базовый класс фигура. Мы выполняем наследование от него в два класса – прямоугольник и круг. Согласно этому принципу если мы создадим массив с типом данных базового класса, то мы сможем хранить в нём не только объекты базового класса, но и наследников и вызовать соответствующие методы, т.е. для каждого объекта будет вызоваться именного его метод.
Параметрический полиморфизм
Используя Параметрический полиморфизм можно создавать универсальные базовые типы. В случае параметрического полиморфизма, функция реализуется для всех типов одинаково и таким образом функция реализована для произвольного типа. В Параметрическом полиморфизме рассматриваются параметрические методы и типы.
Полиморфизм переопределения
Абстрактные (виртуальные) методы часто относятся к отложенным методам. Класс, в котором определен этот метод, может вызвать метод и полиморфизм обеспечивает вызов подходящей версии отложенного метода в дочерних классах. Специальный полиморфизм допускает специальную реализацию для данных каждого типа.
Полиморфизм-перегрузка
Это частный случай полиморфизма. С помощью перегрузки одно и то же имя может обозначать различные методы, причем методы могут различаться количеством или типом параметров, то есть зависят от своих аргументов. Метод может не ограничиваться специфическими типами параметров многих различных типов.
Абстракция
Абстра́кция — в объектно-ориентированном программировании это придание объекту характеристик, которые четко определяют его концептуальные границы, отличая от всех других объектов. Основная идея состоит в том, чтобы отделить способ использования составных объектов данных от деталей их реализации в виде более простых объектов, подобно тому, как функциональная абстракция разделяет способ использования функции и деталей её реализации в терминах более примитивных функций, таким образом, данные обрабатываются функцией высокого уровня с помощью вызова функций низкого уровня.
Такой подход является основой объектно-ориентированного программирования. Это позволяет работать с объектами, не вдаваясь в особенности их реализации.
Абстракция данных — популярная и в общем неверно определяемая техника программирования. Фундаментальная идея состоит в разделении несущественных деталей реализации подпрограммы и характеристик существенных для корректного ее использования. Такое разделение может быть выражено через специальный «интерфейс», сосредотачивающий описание всех возможных применений программы.
С точки зрения теории множеств, процесс представляет собой организацию для группы подмножеств своего множества.
Уровни видимости
Согласно принципу инкапсуляции происходит разделение на доступность членов класса в зависимости от того, каким из пяти уровней видимости они описаны. Эти уровни определяются специальными ключевыми словами (директивами), которые начинают разделы объявлений определения класса и разделяют описание класса на секции. По умолчанию видимость родительских членов класса наследуется в точности, однако видимость разрешено повышать. Понижение не допускается. Выделяют следующие разделы:
1) Public (общедоступный) – члены класса, находящиеся в данном разделе, доступны из любой точки программы. Public поля, свойства и методы - внешне видимая часть объекта, дающая доступ к внутренней организации класса. Предупреждение: избегайте создание полей публичными - всегда лучше вместо этого определить свойство для обращения к ним, это позволит вводить в поля только корректные значения;
2) Private (закрытый) – члены класса, которые доступны только внутри реализации класса, в наследники они не переносяться. Это - жизненно важная часть объектно-ориентированной концепции, в которой класс рассматривается как черный ящик - то, что используется внутри не уместно для внешнего использования;
3) Protected (защищённый) - члены класса доступны только в реализации текущего класса и его наследников. Вообще, большинство данных и методов, внутренних для класса должны быть определены в разделе Protected. Часто это дает подклассам полезный доступ к ним. Только используйте Private, когда вы уверены, что хотите сохранить материал полностью локальным для текущего класса/модуля. Это может быть верным, когда подкласс должен быть изолированным от сложностей родительского класса. Вы, возможно, захотите сделать защищенные методы виртуальными, чтобы позволить подклассам изменять их, чтобы удовлетворить их потребности;
4) Published (публикуемый) – Устанавливает правила видимости те же, что и директива public. Особенность состоит в том, что для элементов, помещенных в секцию published, компилятор генерирует информацию о типах этих элементов. Эта информация доступна во время выполнения программы, что позволяет превращать объекты в компоненты визуальной среды разработки. Секцию published разрешено использовать только тогда, когда для самого класса или его предка включена директива компилятора $TYPEINFO. Предупреждение: избегайте создание полей публичными - всегда лучше вместо этого определить свойство для обращения к ним, это позволит вводить в поля только корректные значения. Только один Constructor может быть объявлен как published, overload версии должны быть определены как public. Published свойства не могут возвращать массивы;
5) Automated (автоматический) – совпадает с разделом public, описание разрешается размещать только для наследников класса TAutoObject при создании серверов автоматизации COM.
Реализация методов
После того как в модуле описан класс, необходимо описать реализацию всех его методов (если они есть) исключая абстрактные. В языках программирования высокого уровня встречается реализация методов двух видов: inline и outline. Суть первого в том, что мы реализуем метод сразу же после его описания, т.е. прямо в теле класса. При втором методе реализация находиться после раздела var. Inline реализация в стандарте object pascal отсутствует (возможно, присутствие в .Net платформе).
Чтобы компилятор понял, к какому классу относится конкретная подпрограмма, перед её названием указывается имя соответствующего класса и точка, т.е. используется уточнение имени. Например, реализуем один метод из листинга 4.
Листинг 1.5
FunctionTSimpleClass.GetCount(): integer;
Begin
Result:=fv;
End;
Заголовок подпрограммы должен в точности соответствовать заголовку, указанному в описании класса. Реализация методов обычно находиться в разделе implementation.
Доступ к полям и методам объекта происходит с помощью уточненных имен через точку, например: MyClass.Draw();
Кроме того, как и при работе с записями, допустимо использование оператора with, например: with MyClass do Draw();
Обратите внимание, что внутри методов класса обращения к полям и другим методам выполняются как к обычным переменным и подпрограммам без уточнения экземпляра объекта. Такое упрощение достигается путем использования в пределах метода псевдопеременной Self (стандартный идентификатор). Физически Self представляет собой дополнительный неявный параметр, передаваемый в метод при вызове. Напоминаем, что методы класса хранятся в памяти отдельно от объектов и в единственном экземпляре, поэтому чтобы определить какой объект в данный момент должен обрабатывать метод и используется данная переменная. Чтобы пояснить сказанное, напишем отдельный метод SetActive, представив его в виде обычной процедуры:
Листинг 1.6
procedure TSimpleClass_SetActive(Self: TSimpleClass; AActive: Boolean);
Begin
if AActive then
Reset(Self.F) // Открытиефайла
else
CloseFile(Self.F); // Закрытиефайла
end;
Практика показывает, что псевдопеременная Self редко используется в явном виде. Ее необходимо применять только тогда, когда при написании метода может возникнуть какая-либо двусмысленность для компилятора, например, при использовании одинаковых имен для локальных переменных и полей объекта.
Вызовреализуетсятак:
TSimpleClass_SetActive(My, True);
На ООП это будет выглядить как MyClass.SetActive(True). Как мы видем при использовании ООП, требуется передать в подпрограмму на один параметр меньше, т.к. при этом параметр Self будет присутствовать там неявно.