Почему объектноориентированный подход победил процедурный?

Содержание

Слайд 2

Распространенность парадигм программирования во многом обуславливается их способностями поддерживать современные методологии разработки

Распространенность парадигм программирования во многом обуславливается их способностями поддерживать современные методологии разработки
программного обеспечения.
Основной показатель программных продуктов - сложность, а основными требованиями к методологиям разработки являются:
удобство сопровождения, возможность безболезненного наращивания уже существующей программы, способность разработанных программных объектов к повторному использованию.

Слайд 3

На второй план отступает такое требование, как быстрое проектирование первоначальной (полнофункциональной) версии

На второй план отступает такое требование, как быстрое проектирование первоначальной (полнофункциональной) версии
программы, потому что его воплощение обычно не позволяет соблюсти все остальные условия.
Процесс разработки программного обеспечения не заканчивается выпуском одного релиза. Он сводится к итеративному расширению предыдущих версий, что, в некоторой степени, и помогает решать проблему сложности.

Слайд 4

Техника эволюционного развития реализована в возвратном проектировании. Она же используется при экстремальном

Техника эволюционного развития реализована в возвратном проектировании. Она же используется при экстремальном
программировании (X-programming).
1. Буч Г. Объектно-ориентированный анализ и проектирование с примерами приложений на C++, 2-е изд./Пер. с англ. - М.: "Издательства Бином", СПб: "Невский диалект", 1998 г. - 560 с., ил.
2. Бек К. Экстремальное программирование./Пер. с англ. – СПб.: Питер,
3. Бек К. Экстремальное программирование: разработка через тестирование./Пер. с англ. – СПб.: Питер, 2003. – 224 с.

Слайд 5

Эволюционная разработка – один из основных факторов, определяющих создание современного программного обеспечения

Окончание

Эволюционная разработка – один из основных факторов, определяющих создание современного программного обеспечения
очередного витка эволюционного цикла разработки версии продукта, приводит к получению кода, написанного на некотором языке программирования. Новый виток спирали требует расширения и модификации этого кода, то есть его эволюции.

Слайд 6

Его нельзя переделать «мастером», его невозможно автоматически сгенерировать при повторном проектировании.
При

Его нельзя переделать «мастером», его невозможно автоматически сгенерировать при повторном проектировании. При
этом желательно как можно меньше изменять уже написанный код, используя методы, позволяющие его наращивать.
Лучше всего, если только добавлять новый код.
То есть (учитывая метаморфозы терминологии), эволюционное программирование можно ассоциировать с эволюционным кодированием.

Слайд 7

Основные виды отношений между программными объектами

Отличие парадигм программирования заключается в различных способах

Основные виды отношений между программными объектами Отличие парадигм программирования заключается в различных
реализации моделей состояния и поведения. Эти различия проявляются через отношения между алгоритмами и данными, способами описания алгоритмов и способами описания данных.

Слайд 8

Известная всем метафора:
Алгоритмы + Данные = Программы
Отражает только простейшие отношения, существующие между

Известная всем метафора: Алгоритмы + Данные = Программы Отражает только простейшие отношения,
данными и методами их обработки.

Слайд 9

Более точным отражением является текущего состояния является:
Программа =
Конструктив (Данные, Алгоритмы)
В борьбе

Более точным отражением является текущего состояния является: Программа = Конструктив (Данные, Алгоритмы)
конструктивов и проявляется формирование современных принципов эволюционного программирования.

Слайд 10

ООП предложило более гибкие конструктивы для построения эволюционно расширяемых программ

ООП предложило более гибкие конструктивы для построения эволюционно расширяемых программ

Слайд 11

Абстрагирование от конкретных экземпляров достигается за счет введения специальных понятий, определяющих как

Абстрагирование от конкретных экземпляров достигается за счет введения специальных понятий, определяющих как
семантические артефакты, например, "абстрактный тип данных" и "процедура" (понятие "функция" используется как синоним процедуры).
Также выделяются базовые понятия используются для конструирования составных программных объектов путем объединения в агрегаты и разделения по категориям. Категорию Г. Буч называет иерархией типа "is-a". Она также трактуется как обобщение программных объектов.
Цикритзис Д., Лоховски Ф. Модели данных. Пер. с англ. - М.: Финансы и статистика, 1985. - 344 с.

Слайд 12

Агрегаты и обобщения используются при конструировании композиций из данных и процедур. В

Агрегаты и обобщения используются при конструировании композиций из данных и процедур. В
каждой из существующих парадигм программирования вопросы такого конструирования композиций решаются по-своему, что и вносит определенные отличительные черты.
Артефакт (по Вирту) – создаваемый программистом программный объект с применением средств системы программирования.
Программный объект – любая конструкция допускаемая в системе (языке) программирования (никоим образом не должен ассоциироваться только с ООП).

Слайд 13

Конструирование агрегатов

Агрегация (агрегирование) - это абстрагирование, посредством которого один объект конструируется

Конструирование агрегатов Агрегация (агрегирование) - это абстрагирование, посредством которого один объект конструируется
из других [Цикритзис].
Агрегирование обеспечивает формирование программных объектов одним из способов:
непосредственным включением,
косвенным (ссылочным) связыванием,
с применением наследования (расширения),
образного восприятия.

Слайд 14

Наиболее типичным является восприятие агрегата как единой абстракции, сформированной непосредственным включением используемых

Наиболее типичным является восприятие агрегата как единой абстракции, сформированной непосредственным включением используемых
в нем программных объектов. В нашем сознании он видится как единый, монолитный ресурс, занимающий некоторое неразрывное пространство. Цельность и законченность данного объекта не требует выполнения дополнительных алгоритмов, связанных с формированием его структуры. Можно сразу приступать к алгоритмическому использованию объекта, например, его инициализации.

Слайд 16

Косвенное связывание позволяет формировать агрегаты из отдельных элементов, уже располагаемых в пространстве.

Косвенное связывание позволяет формировать агрегаты из отдельных элементов, уже располагаемых в пространстве.
Взаимосвязь осуществляется алгоритмическим заданием значений ссылок. Спецификой является выполнение кода обеспечивающего конструирование объекта из разрозненных экземпляров абстракций. Однако этот код выполняется только один раз, после чего работа с агрегатом осуществляется точно так же, как и при включении.
В ряде случаев, когда элементы и агрегат создаются статически, инициализация связей может быть проведена во время трансляции программы.

Слайд 18

Наследование позволяет создавать структуру объекта, эквивалентную той, которая формируется непосредственным включением. Но

Наследование позволяет создавать структуру объекта, эквивалентную той, которая формируется непосредственным включением. Но
поддерживает свойства конкатенации, в результате чего обращение к элементам подключаемых абстракций осуществляется напрямую. Сохраняется информация о базовых абстракциях, которая может использоваться для разрешения проблем неоднозначности доступа к элементам сформированного агрегата при совпадении их имен. В отличие от непосредственного включения, каждому наследуемому элементу "предоставляется" дополнительная информация о формируемом агрегате. Использование дополнительной информации позволяет правильно манипулировать агрегатом, используя лишь сведения об одном из его базовых (включаемых) элементов. Например, через базовый элемент можно определить тип агрегата, его размер, выполнить операцию удаления (реализация к конструктиве полиморфизма).

Слайд 20

Образное агрегирование связано с отсутствием специально созданной абстракции, соответствующей формируемому агрегату. Вместо

Образное агрегирование связано с отсутствием специально созданной абстракции, соответствующей формируемому агрегату. Вместо
этого агрегат воссоздается только в мысленном восприятии программиста, а на уровне программы имеются его разрозненные элементы, обрабатываемых как единое целое.
Например, точку на плоскости можно представить как две независимые целочисленные переменные x и y. Такое агрегирование уходит корнями в далекое прошлое (эпоху Фортрана и Алгола-60), но и сейчас встречаются программисты, которым "лень" вписать лишнюю абстракцию. Это приводит к определенным проблемам, связанным с мобильностью и повторным использованием кода (но иногда так хочется поскорее написать программу, что не остается времени на раздумья о стиле!).

Слайд 21

A

X

Z

Y

adrrZ

adrrX

Агрегат A из элементов X, Y, Z, построенный на основе образного восприятия

а)

A X Z Y adrrZ adrrX Агрегат A из элементов X, Y,
условное изображение на плоскости

б) отображение на одномерное адресное пространство

Z

X

Y

adrrY

Образное восприятие структуры агрегата

A

Слайд 22

Наряду с "чистыми" методами, агрегаты могут строиться по смешанному принципу, когда одновременно

Наряду с "чистыми" методами, агрегаты могут строиться по смешанному принципу, когда одновременно
применяются различные комбинации методов агрегирования.
В качестве примера представлен агрегат, выстроенный с применением включения, косвенного связывания и наследования.
Такой подход является наиболее популярным в настоящее время, так как позволяет рационально использовать различную технику в зависимости от организации и назначения агрегируемых элементов.

Слайд 24

К специфике различных парадигм программирования можно отнести способы построения агрегатов и их

К специфике различных парадигм программирования можно отнести способы построения агрегатов и их
использования. В частности, при процедурном программировании осуществляется разделение на агрегаты данных и процедур.
Объектно-ориентированный подход использует комбинированное агрегирование.
Ниже рассмотрены особенности построения и использования агрегатов для этих парадигм.

Слайд 25

Процедурное агрегирование

Агрегатам данных соответствуют абстрактные типы данных. Они состоят из объектов данных,

Процедурное агрегирование Агрегатам данных соответствуют абстрактные типы данных. Они состоят из объектов
подключаемых непосредственно или с использованием ссылок (указателей). Будем использовать понятие "запись". Процедурный подход предполагает независимость записей (R) от процедур (P). Запись R состоит из элементов данных: R=(D1, D2, ..., Dk), называемых полями записи.
Агрегатам процедур соответствуют вложения в тела процедур различных по иерархии объектов действия: операций, операторов, вызовов процедур, осуществляющих обработку отдельных элементов (полей) записи. Обозначим данную абстракцию понятием "независимая процедура". Доступ к различным экземплярам записи, определяющим агрегируемую абстракцию, осуществляется через один из элементов списка формальных параметров.

Слайд 26

//--------------------------------------------------------
// Простейший контейнер на основе одномерного массива
//--------------------------------------------------------
// Контейнер должен знать о фигуре
#include

//-------------------------------------------------------- // Простейший контейнер на основе одномерного массива //-------------------------------------------------------- // Контейнер должен
"shape_atd.h"
//--------------------------------------------------------
// Данные контейнера
struct container
{
enum {max_len = 100}; // максимальная длина
int len; // текущая длина
shape *cont[max_len];
};

Пример использования процедурного агрегирования

Слайд 27

//--------------------------------------------------
// Процедуры должны знать о контейнере и фигуре,
// доступной через модуль, описывающий

//-------------------------------------------------- // Процедуры должны знать о контейнере и фигуре, // доступной через
контейнер
#include "container_atd.h"
//--------------------------------------------------
// Инициализация контейнера
void Init(container &c)
{
c.len = 0;
}
//--------------------------------------------------
// Очистка контейнера от элементов (освобождение памяти)
void Clear(container &c)
{
for(int i = 0; i < c.len; i++)
{
delete c.cont[i];
}
c.len = 0;
}

Слайд 28

//---------------------------------------------------------
// Необходим прототип функции, формирующей фигуру при вводе
shape *In();
//---------------------------------------------------------
// Ввод содержимого контейнера
void

//--------------------------------------------------------- // Необходим прототип функции, формирующей фигуру при вводе shape *In(); //---------------------------------------------------------
In(container &c) {
cout
<< "Do you want to input next shape"
" (yes=\'y\', no=other character)? "
<< endl;
char k;
cin >> k;
while(k == 'y') {
cout << c.len << ": ";
if((c.cont[c.len] = In()) != 0){
c.len++;
}
cout
<< "Do you want to input next shape"
" (yes=\'y\', no=other character)? "
<< endl;
cin >> k;
}
}

Слайд 29

//---------------------------------------------------
// Необходим прототип функции вывода отдельной фигуры
void Out(shape &s);
//---------------------------------------------------
// Вывод содержимого

//--------------------------------------------------- // Необходим прототип функции вывода отдельной фигуры void Out(shape &s); //---------------------------------------------------
контейнера
void Out(container &c)
{
cout << "Container contents " << c.len
<< " elements." << endl;
for(int i = 0; i < c.len; i++)
{
cout << i << ": ";
Out(*(c.cont[i]));
}
}

Слайд 30

Объектно-ориентированное агрегирование

Объектно-ориентированное программирование предлагает следующие варианты композиции для создания агрегатов:
Основной

Объектно-ориентированное агрегирование Объектно-ориентированное программирование предлагает следующие варианты композиции для создания агрегатов: Основной
агрегирующей единицей является виртуальная или реальная оболочка (C), Реально существующая оболочка, вместе с размещенными в ней программными объектами, чаще всего называется классом. В нем могут быть размещены как данные, так и процедуры. Оболочка в явном виде может не присутствовать в языке и проявляться через связывание процедур с типами данных, как в языке программирования Оберон-2 [MoessenboeckWirth]. Но при этом подчеркивается объектно-ориентированная направленность механизма связывания процедур.

Слайд 31

Обычные процедуры, размещаемые в классе и используемые для изменения своего внутреннего состояния,

Обычные процедуры, размещаемые в классе и используемые для изменения своего внутреннего состояния,
часто называются методами, функциями-членами класса. Будем использовать термин "процедура класса". Виртуальные процедуры, переопределяемые в производных классах, будут рассмотрены ниже как составляющие обобщений.
Данные, определяющие внутреннее состояние класса, обычно называются переменными класса.
Термин "оболочка класса" (или просто "оболочка", если понятен контекст) будем использовать для обозначения класса в том случае, когда хотим исключить из рассмотрения его переменные и процедуры. Оболочка, при доступе извне, выступает в роли посредника к программным объектам, расположенным внутри класса.

Слайд 32

Пример объектно-ориентированного агрегирования

//------------------------------------------------------
// Простейший контейнер на основе одномерного массива
//------------------------------------------------------
// Контейнер должен знать

Пример объектно-ориентированного агрегирования //------------------------------------------------------ // Простейший контейнер на основе одномерного массива //------------------------------------------------------
о фигуре
#include "shape_atd.h"
//------------------------------------------------------
// Описание контейнера
class container
{
enum {max_len = 100}; // максимальная длина
int len; // текущая длина
shape *cont[max_len];
public:
void In(); // ввод фигур в контейнер
void Out(); // вывод фигур в выходного потока
double Area(); // подсчет суммарной площади
void Clear(); // очистка контейнера от фигур
container(); // инициализация контейнера
~container() {Clear();} // утилизация контейнера
};

Слайд 33

//-----------------------------------------------------------
// Необходимо знать описание контейнера и методов фигуры,
// доступных через container_atd.h
#include "container_atd.h"
//-----------------------------------------------------------
//

//----------------------------------------------------------- // Необходимо знать описание контейнера и методов фигуры, // доступных через
Прототип обычной внешней функции, формирующей фигуру
// при вводе
shape *In();
//-----------------------------------------------------------
// Инициализация контейнера
container::container(): len(0) { }
//-----------------------------------------------------------
// Очистка контейнера от элементов (освобождение памяти)
void container::Clear()
{
for(int i = 0; i < len; i++)
{
delete cont[i];
}
len = 0;
}

Слайд 34

//------------------------------------------------------
// Ввод содержимого контейнера
void container::In() {
cout
<< "Do you want

//------------------------------------------------------ // Ввод содержимого контейнера void container::In() { cout " (yes=\'y\', no=other
to input next shape"
" (yes=\'y\', no=other character)? "
<< endl;
char k;
cin >> k;
while(k == 'y')
{
cout << len << ": ";
if((cont[len] = simple_shapes::In()) != 0)
{
len++;
}
cout
<< "Do you want to input next shape"
" (yes=\'y\', no=other character)? "
<< endl;
cin >> k;
}
}

Слайд 35

//-------------------------------------------------------------
// Вывод содержимого контейнера
void container::Out() {
cout << "Container contents " <<

//------------------------------------------------------------- // Вывод содержимого контейнера void container::Out() { cout for(int i =
len
<< " elements." << endl;
for(int i = 0; i < len; i++) {
cout << i << ": ";
cont[i]->Out();
}
}
//-------------------------------------------------------------
// Вычисление суммарной площади для фигур, размещенных
// в контейнере
double container::Area() {
double a = 0;
for(int i = 0; i < len; i++) {
a += cont[i]->Area();
}
return a;
}

Слайд 36

Отличие методов агрегирования

ОО агрегирование не имеет никаких преимуществ перед процедурным агрегированием
ОО

Отличие методов агрегирования ОО агрегирование не имеет никаких преимуществ перед процедурным агрегированием
подход лучше описывает окружающий нас мир – заблуждение.
------------------------------------------------------- class simple {
int v;
public:
simple(int val);
void out();
};
-------------------------------------------------------struct simple {
int v;
};
simple *create_simple (int val);
void destroy_simple(simple* s);
void out (simple &s);

Слайд 37

Процедурный подход позволяет лучше скрывать данные [Meyers]

struct simple;
simple *create_simple (int val);
void

Процедурный подход позволяет лучше скрывать данные [Meyers] struct simple; simple *create_simple (int
destroy_simple(simple* s);
void out (simple &s);

Слайд 38

Резюме
ОО агрегирование обеспечивает более компактное написание программ за счет поддержки рациональных методов

Резюме ОО агрегирование обеспечивает более компактное написание программ за счет поддержки рациональных
создания артефактов.
Процедурное и ОО агрегирование практически ничем не отличаются между собой, если не считать за отличие размещение процедур.
Между тем, процедурное агрегирование обеспечивает более гибкую реализацию ассоциаций, связанных с конструированием эволюционирующих программ.
При процедурном проектировании агрегатов обеспечивается лучшее сокрытие данных

Слайд 39

Конструирование обобщений

Обобщение - это композиция альтернативных по своим свойствам программных объектов,

Конструирование обобщений Обобщение - это композиция альтернативных по своим свойствам программных объектов,
принадлежащих к единой категории в некоторой системе классификации.
Выделяются:
обобщение данных и обобщение процедур (процедурное обобщение). Обобщение данных состоит из основы обобщения, к которой присоединяются различные основы специализаций.

Слайд 40

Основа специализации – программный объект, который может использоваться в качестве специализации того

Основа специализации – программный объект, который может использоваться в качестве специализации того
или иного обобщения.
Для данных –любой тип данных.
Для процедур – любая процедура.
Специализации обобщения – отдельные альтернативные понятия, принадлежащие к единой категории (присоединенные основы специализаций).
Обработка обобщений данных осуществляется соответствующими процедурами, включаемыми в состав процедурного обобщения. Процедура, связанная с обработкой основы обобщения называется обобщающей процедурой. Процедура, осуществляющая обработку отдельной специализации обобщения, называется специализированной.

Слайд 41

Методы формирования обобщений

При обобщении на основе общего ресурса происходит размещение специализаций

Методы формирования обобщений При обобщении на основе общего ресурса происходит размещение специализаций
в едином адресном пространстве.
При этом обычно существует часть ресурса, одновременно перекрываемая всеми размещаемыми программными объектами.
Но чаще всего обобщения на основе общего ресурса строится таким образом, что начальный адрес для всех размещаемых объектов является одинаковым.

Слайд 44

Наличие общего ресурса для нескольких абстракций позволяет использовать такое обобщение в

Наличие общего ресурса для нескольких абстракций позволяет использовать такое обобщение в двух
двух различных целях:
Хранение одного из альтернативных объектов. Выделенное пространство предоставляется экземпляру абстракции только одного типа, который хранит свое значение, никоим образом по семантике не пересекающееся с семантикой альтернативных объектов.
Использование различных трактовок одного и того же пространства ресурсов. В частности, для математических операций целое число длиной два байта может интерпретироваться как единое слово, для операций сохранения-восстановления в двоичном формате, как младший и старший байты. Для операций сравнения и сдвига бывает необходимо отделять знаковый бит от значения. Для каждого из этих случаев возможна своя специализация. А вместе они могут формировать обобщение.

Слайд 45

Альтернативное связывание
Предполагает независимое размещение специализаций в рассматриваемом обобщении. Использование ссылки или

Альтернативное связывание Предполагает независимое размещение специализаций в рассматриваемом обобщении. Использование ссылки или
указателя обеспечивает доступ только к одному избранному объекту. Как и при агрегировании, альтернативное связывание обычно осуществляется алгоритмически. Выполнение алгоритма связывания, происходит для отдельного экземпляра обобщения только один раз.
Одновременно с выбором специализации осуществляется и установка ключа, указывающего признак специализации. Дальнейшая работа с обобщением осуществляется на основе предварительного анализа значения ключа. Это значение позволяет семантически правильно обработать специализацию через "обезличенный" указатель.

Слайд 48

Образное обобщение
Как и образное агрегирование, связано с мысленными ассоциациями программиста. Используя ключевой

Образное обобщение Как и образное агрегирование, связано с мысленными ассоциациями программиста. Используя
признак, можно связать с отдельными специализациями подмножества разрозненных объектов программы, семантическая связь между которыми поддерживается на уровне алгоритма. При этом одни и те же программные объекты могут использоваться в альтернативных специализациях, имея при этом различную семантическую трактовку.
Как и в случае с образным агрегированием, такой подход к написанию программ является "пережитком прошлого". Достаточно часто он использовался и для экономии памяти, когда одни и те же переменные интерпретировались различным образом.

Слайд 50

Вариантное обобщение

Вариант - основа обобщения данных в процедурном подходе.
Обобщение, применяемое в

Вариантное обобщение Вариант - основа обобщения данных в процедурном подходе. Обобщение, применяемое
процедурном подходе и построенное на основе варианта, назовем вариантным обобщением.
С каждым вариантом связан набор специализаций обобщения, построенный на основе уже существующих абстракций (основ специализаций). Определим их как вариантные специализации.

Слайд 51

Обработка вариантных обобщений осуществляется независимыми процедурами, организующими доступ к внутренним переменным через

Обработка вариантных обобщений осуществляется независимыми процедурами, организующими доступ к внутренним переменным через
экземпляр обобщения, получаемый, в качестве одного из аргументов списка параметров.
Процедуры, обрабатывающие специализации обобщений, могут создаваться независимо от обобщающей процедуры. Они могут использоваться различными обобщающими процедурами.
Каждая из таких процедур связана только со своими специализациями.
В дальнейшем специализированные процедуры, используемые в процедурном подходе, будут называться обработчиками вариантов.

Слайд 52

Процедуры, осуществляющие обработку всего вариантного обобщения, используют алгоритмический механизм анализа вариантов по

Процедуры, осуществляющие обработку всего вариантного обобщения, используют алгоритмический механизм анализа вариантов по
ключевому параметру, содержащему признак текущей вариантной специализации. Алгоритм анализа обычно строится с использованием условных оператором или переключателей. Анализ осуществляется всякий раз, когда запускается процедура, и заключается в проверке ключа, задающего признак специализации обобщения. После определения специализации запускается соответствующий обработчик варианта. Обобщающая процедура, осуществляющая обработку вариантного обобщения, называется вариантной процедурой.

Слайд 53

Использование независимых вариантных процедур для создания кода ведет к централизации процесса обработки

Использование независимых вариантных процедур для создания кода ведет к централизации процесса обработки
обобщений, выделя в нем отдельные задачи. Каждая из процедур обеспечивает решение одной из задач. Процедуры, решающие разные задачи, совершенно не связаны друг с другом. Декомпозиция работ внутри вариантной процедуры осуществляется в соответствии с вариантной специализацией. Каждая из подзадач выполняется отдельным обработчиком варианта.

Слайд 54

Построение вариантного обобщения на основе общего ресурса

Рассмотрим создание обобщенной геометрической фигуры,

Построение вариантного обобщения на основе общего ресурса Рассмотрим создание обобщенной геометрической фигуры,
используемой для представления прямоугольника или треугольника. Первоначально создаются абстракции данных, определяющие конкретные геометрические фигуры (основы специализаций).

Слайд 55

//------------------------------------
// прямоугольник
struct rectangle {
int x, y; // ширина, высота
};
//-------------------------------------
// треугольник
struct triangle

//------------------------------------ // прямоугольник struct rectangle { int x, y; // ширина, высота
{
int a, b, c; // стороны
};

С каждой из специализаций связывается набор обработчиков:

Слайд 56

Процедура динамического создания прямоугольника:
rectangle *Create_rectangle(int x, int y)
Процедура инициализации уже созданного

Процедура динамического создания прямоугольника: rectangle *Create_rectangle(int x, int y) Процедура инициализации уже
прямоугольника:
void Init(rectangle &r, int x, int y)
Процедура ввода параметров прямоугольника из входного потока:
void In(rectangle &r)
Процедура вывода параметров прямоугольника:
void Out(rectangle &r)
Процедура вычисления площади прямоугольника:
double Area(rectangle &r)

Слайд 57

Процедура динамического создания треугольника:
triangle *Create_triangle
(int a, int b, int c)
Процедура инициализации уже

Процедура динамического создания треугольника: triangle *Create_triangle (int a, int b, int c)
созданного треугольника:
void Init(triangle &t,
int a, int b, int c)
Процедура ввода параметров треугольника из входного потока:
void In(triangle &t)
Процедура вывода параметров треугольника:
void Out(triangle &t)
Процедура вычисления площади треугольника:
double Area(triangle &t)

Слайд 58

//------------------------------------------------------
// Процедуры, обеспечивающие работу с прямоугольником
//------------------------------------------------------ 
// Динамическое создание прямоугольника по двум сторонам
rectangle

//------------------------------------------------------ // Процедуры, обеспечивающие работу с прямоугольником //------------------------------------------------------ // Динамическое создание прямоугольника
*Create_rectangle(int x, int y)
{
rectangle *r = new rectangle;
r->x = x;
r->y = y;
return r;
}
// Инициализация уже созданного прямоугольника
// по двум сторонам
void Init(rectangle &r, int x, int y)
{
r.x = x;
r.y = y;

Слайд 59

// Ввод параметров прямоугольника
void In(rectangle &r)
{
cout << "Input Rectangle: x,

// Ввод параметров прямоугольника void In(rectangle &r) { cout cin >> r.x
y = ";
cin >> r.x >> r.y;
}
// Вывод параметров прямоугольника
void Out(rectangle &r)
{
cout << "It is Rectangle: x = "
<< r.x << ", y = "
<< r.y << endl;
}
// Вычисление площади прямоугольника
double Area(rectangle &r)
{
return r.x * r.y;
}

Слайд 60

//-----------------------------------------------------
// Процедуры, обеспечивающие работу с треугольником
// Динамическое создание треугольника по трем сторонам
triangle

//----------------------------------------------------- // Процедуры, обеспечивающие работу с треугольником // Динамическое создание треугольника по
*Create_triangle(int a, int b, int c)
{
triangle *t = new triangle;
t->a = a;
t->b = b;
t->c = c;
return t;
}
// Инициализация уже созданного треугольника
// по трем сторонам
void Init(triangle &t, int a, int b, int c)
{
t.a = a;
t.b = b;
t.c = c;
}

Слайд 61

// Ввод параметров треугольника
void In(triangle &t)
{
cout << "Input Triangle: a,

// Ввод параметров треугольника void In(triangle &t) { cout cin >> t.a
b, c = ";
cin >> t.a >> t.b >> t.c;
}
// Вывод параметров треугольника
void Out(triangle &t)
{
cout << "It is Triangle: a = "
<< t.a << ", b = " << t.b
<< ", c = " << t.c << endl;
}
// Вычисление площади треугольника
double Area(triangle &t)
{
// полупериметр
double p = (t.a + t.b + t.c) / 2.0;
return
sqrt(p * (p-t.a) * (p-t.b) * (p-t.c));
}

Слайд 62

Вариантное обобщение на основе прямого включения строится с применением объединения:

// Обобщение на

Вариантное обобщение на основе прямого включения строится с применением объединения: // Обобщение
основе
// разделяемого (общего) ресурса
union {
rectangle r;
triangle t;
};

Слайд 63

Объединение включается в агрегат, который также содержит признаки специализаций и ключевую переменную,

Объединение включается в агрегат, который также содержит признаки специализаций и ключевую переменную,
предназначенную для хранения текущего признака каждого экземпляра.

// структура, обобщающая все имеющиеся фигуры
struct shape {
// значения ключей для каждой из фигур
enum key {RECTANGLE, TRIANGLE};
key k; // ключ
// Обобщение на основе разделяемого
// (общего) ресурса
union { // используем простейшую реализацию
rectangle r;
triangle t;
};
};

Слайд 64

Вариантные процедуры
Динамическое создание обобщенной фигуры:
shape *Create_shape_rectangle(int x, int y)
shape *Create_shape_triangle
(int a,

Вариантные процедуры Динамическое создание обобщенной фигуры: shape *Create_shape_rectangle(int x, int y) shape
int b, int c)
Инициализация обобщенной фигуры:
void Init_rectangle(shape &s, int x, int y)
void Init_triangle
(shape &s, int a, int b, int c)
Создание обобщенной фигуры и ввод ее типа:
shape* In()
Вывод варианта, определяемого экземпляром обобщенной фигуры:
void Out(shape &s)
Вычисления площади заданного экземпляра вариантного обобщения:
double Area(shape &s)

Слайд 65

//------------------------------------------------------
// Процедуры, обеспечивающие работу с обобщенной фигурой
//------------------------------------------------------ 
// Сигнатуры, необходимые обработчикам вариантов.
void Init(rectangle

//------------------------------------------------------ // Процедуры, обеспечивающие работу с обобщенной фигурой //------------------------------------------------------ // Сигнатуры, необходимые
&r, int x, int y);
void Init(triangle &t, int a, int b, int c);
void In(rectangle &r);
void In(triangle &t);
void Out(rectangle &r);
void Out(triangle &t);
double Area(rectangle &r);
double Area(triangle &t);
// Динамическое создание обобщенного прямоугольника
shape *Create_shape_rectangle(int x, int y) {
shape *s = new shape;
s->k = shape::key::RECTANGLE;
Init(s->r, x, y);
return s;
}

Слайд 66

// Инициализация обобщенного прямоугольника
void Init_rectangle(shape &s, int x, int y) {
s.k

// Инициализация обобщенного прямоугольника void Init_rectangle(shape &s, int x, int y) {
= shape::key::RECTANGLE;
Init(s.r, x, y);
}
// Динамическое создание обобщенного треугольника
shape *Create_shape_triangle(int a, int b, int c) {
shape *s = new shape;
s->k = shape::key::TRIANGLE;
Init(s->t, a, b, c);
return s;
}
// Инициализация обобщенного треугольника
void Init_triangle(shape &s, int a, int b, int c)
{
s.k = shape::key::TRIANGLE;
Init(s.t, a, b, c);
}

Слайд 67

// Ввод параметров обобщенной фигуры из
// стандартного потока ввода
shape* In() {

// Ввод параметров обобщенной фигуры из // стандартного потока ввода shape* In()
shape *sp;
cout << "Input key: for Rectangle is 1, "
"for Triangle is 2, else break: ";
int k;
cin >> k;
switch(k) {
case 1:
sp = new shape;
sp->k = shape::key::RECTANGLE;
In(sp->r);
return sp;
case 2:
sp = new shape;
sp->k = shape::key::TRIANGLE;
In(sp->t);
return sp;
default:
return 0;
}
}

Слайд 68

// Вывод параметров текущей фигуры в стандартный
// поток вывода
void Out(shape &s)

// Вывод параметров текущей фигуры в стандартный // поток вывода void Out(shape
{
switch(s.k) {
case shape::key::RECTANGLE:
Out(s.r);
break;
case shape::key::TRIANGLE:
Out(s.t);
break;
default:
cout << "Incorrect figure!" << endl;
}
}
// Нахождение площади обобщенной фигуры
double Area(shape &s) {
switch(s.k) {
case shape::key::RECTANGLE:
return Area(s.r);
case shape::key::TRIANGLE:
return Area(s.t);
default:
return 0.0;
}
}

Слайд 69

Полная версия примера в pp_examp1.zip.
Резюме
Вариантные обобщения на основе общего ресурса формируются

Полная версия примера в pp_examp1.zip. Резюме Вариантные обобщения на основе общего ресурса
при написании программы и распознаются во время трансляции.
Это позволяет заранее распределить память и обеспечить быстрый и непосредственный доступ к отдельным экземплярам.
К недостаткам можно отнести неэффективное использование пространства памяти при различных размерах специализаций.

Слайд 70

Процесс построения языковой структуры вариантного обобщения совпадает по направленности с восходящим проектированием.

Процесс построения языковой структуры вариантного обобщения совпадает по направленности с восходящим проектированием.

Это никоим образом не говорит, что при процедурном подходе проблематично применять нисходящую разработку. Проектирование, при статических вариантных обобщениях, ведется таким образом, что в начале осуществляется полная проработка всей иерархии обобщения, а уж только потом можно приступать к его кодированию.
Возможно, что эта особенность кодирования вариантных обобщений в какой-то мере послужила популярности водопадной модели, оказавшейся малоэффективной при разработке сложных программ.

Слайд 71

Построение вариантного обобщения на основе альтернативного связывания

Позволяет обойтись без дополнительных обобщающих

Построение вариантного обобщения на основе альтернативного связывания Позволяет обойтись без дополнительных обобщающих
конструкций, что обеспечивает большую гибкость при эволюционном наращивании процедурной программы.
Можно посмотреть, как изменится метод решения задачи, если использование общего ресурса заменить вариантным связыванием. Сохраним существующие специализации без изменения, но заменим обобщение:

Слайд 72

// структура, обобщающая все имеющиеся фигуры
struct shape {
// значения ключей

// структура, обобщающая все имеющиеся фигуры struct shape { // значения ключей
для каждой из фигур
enum key {RECTANGLE, TRIANGLE};
key k; // ключ
// используемые альтернативные указатели
union { // используем простейшую реализацию
rectangle *pr;
triangle *pt;
};
};

Полная версия примера в pp_examp1b.zip.

Слайд 73

Построение вариантного обобщения на основе образного восприятия

Для иллюстрации образного восприятия вариантов,

Построение вариантного обобщения на основе образного восприятия Для иллюстрации образного восприятия вариантов,
введем в нашу программу следующие изменения:
Создадим контейнер, используя указатели на любой тип данных (void). Будем подразумевать, что каждый элемент контейнера указывает на одну из разработанных фигур.
Для идентификации специализаций, вместо перечислимого типа, используем целые числа, образно ассоциируя их с соответствующими фигурами (1 - прямоугольник, 2 - треугольник). Тогда, появление новой фигуры просто сведется к образной ассоциации с еще одним числом.

Слайд 74

struct container {
enum {max_len = 100}; // максимальная длина
int len;

struct container { enum {max_len = 100}; // максимальная длина int len;
// текущая длина
// Контейнер обазных указателей на специализации,
// построенные на основе распределенного ключа
void* cont[max_len];
};
//----------------------------------------------------- 
// Использование образного восприятия обобщения на основе
// ключа, разнесенного по отдельным дополнительным
// специализациям 
// шаблонная структура, обобщающая все имеющиеся фигуры
struct shape {
// значения ключей для каждой из фигур
int k;
// образый ключ устанавливается для вводимой фигуры.
// Можно легко ошибиться, но гибкость - прекрасная!
// А связь с фигурами отсутствует!
// Предполагается, что они пропишутся отдельно
// и будут связаны
// через образное наложение одинаковых структур памяти
};

Полная версия примера в pp_examp1c.zip.

Слайд 75

// структура, обобщающая прямоугольник
struct r_shape {
// значения ключей для каждой из

// структура, обобщающая прямоугольник struct r_shape { // значения ключей для каждой
фигур
int k; // образый ключ = 1
// А здесь будет храниться прямоугольник
rectangle r;
};
// структура, обобщающая треугольнк
struct t_shape {
// значения ключей для каждой из фигур
int k; // образый ключ = 2
// А здесь будет храниться треугольник
triangle t;
};

Слайд 76

void Out(rectangle &r);
void Out(triangle &t);
void Out(shape *ps)
{
switch(ps->k) {
case 1: //

void Out(rectangle &r); void Out(triangle &t); void Out(shape *ps) { switch(ps->k) {
прямоугольник
Out(((r_shape*)ps)->r);
break;
case 2: // треугольник
Out(((t_shape*)ps)->t);
break;
default:
cout << "Incorrect figure!" << endl;
}
}

Слайд 77

Пример программы на языке программирования Оберон
(процедурный стиль)

Язык содержит расширяемые типы данных, обеспечивает

Пример программы на языке программирования Оберон (процедурный стиль) Язык содержит расширяемые типы
проверку типов во время выполнения, в нем реализована автоматическая сборка мусора.
Ограничения на работу с указателями и использование только расширяемых типов (отсутствуют вариантные записи предшественников) позволили сократит число вариантов написания программы.

Слайд 78

(******************************************************
figure.o: содержит модуль figure с процедурами и типами,
осуществляющими обработку обобщенных геометрических фигур
********************************************************)
MODULE

(****************************************************** figure.o: содержит модуль figure с процедурами и типами, осуществляющими обработку обобщенных
figure;
TYPE
(* Структуры данных описывающие обобщенную
геометрическую фигуру *)
(* Указатель на обобщенную геометрическую фигуру *)
Pfig* = POINTER TO fig;
(* Запись, определяющая структуру обобщенной
геометрической фигуры *)
fig* = RECORD
(* запись является базовой для всех прочих *)
(* поля отсутствуют *)
END;
END figure.

Слайд 79

(******************************************************* rectangle.o: содержит модуль rectangle с процедурами и типами, осуществляющими обработку прямоугольников
********************************************************)
MODULE

(******************************************************* rectangle.o: содержит модуль rectangle с процедурами и типами, осуществляющими обработку прямоугольников
rectangle;
IMPORT In, Out, FileIO, figure;
TYPE
(* Структуры данных описывающие прямоугольник *)
(* Указатель на прямоугольник *)
Prect* = POINTER TO rect;
(* Запись, определяющая структуру прямоугольника *)
rect* = RECORD(figure.fig)
x*, y* : INTEGER (* стороны прямоугольника*)
END;
(* Процедура динамического создания и инициализации нового прямоугольника *)
PROCEDURE New*(x, y: INTEGER): Prect;
VAR tmp : Prect;
BEGIN
NEW(tmp);
tmp^.x := x;
tmp^.y := y;
RETURN tmp
END New;

Слайд 80

(* Процедура инициализации уже существующего прямоугольника *)
PROCEDURE Init*(VAR r: rect; x, y:

(* Процедура инициализации уже существующего прямоугольника *) PROCEDURE Init*(VAR r: rect; x,
INTEGER);
BEGIN
r.x := x;
r.y := y;
END Init;
(* Процедура вычисления периметра прямоугольника *)
PROCEDURE Perimeter*(VAR r: rect) : REAL;
VAR
BEGIN
RETURN (r.x + r.y) * 2
END Perimeter;
(* Процедура ввода прямоугольника из входного потока *)
PROCEDURE Input*(VAR r: rect);
BEGIN
Out.String("x = "); In.Int(r.x);
Out.String("y = "); In.Int(r.y);
END Input;

Слайд 81

(* Процедура вывода *)
PROCEDURE Output*(VAR r: rect);
BEGIN
Out.String("Rectangle: x = ");

(* Процедура вывода *) PROCEDURE Output*(VAR r: rect); BEGIN Out.String("Rectangle: x =
Out.Int(r.x, 0);
Out.String(", y = "); Out.Int(r.y, 0);
Out.String(", perimemter = "); Out.Real(Perimeter(r), 0);
Out.Ln;
END Output;
(* Процедура Заполнения прямоугольника из файла *)
PROCEDURE FileInput*
(VAR inFile : FileIO.TFile; VAR r: rect);
VAR
f : INTEGER;
BEGIN
f := inFile.ReadInt(r.x);
f := inFile.ReadInt(r.y);
END FileInput;

Слайд 82

(* Процедура вывода в файл, использующая передачу параметра-переменной *)
PROCEDURE FileOutput*
(VAR outFile

(* Процедура вывода в файл, использующая передачу параметра-переменной *) PROCEDURE FileOutput* (VAR
: FileIO.TFile; VAR r: rect);
VAR
flag : INTEGER;
BEGIN
flag := outFile.WriteString("Rectangle: x = ");
flag := outFile.WriteInt(r.x, 7);
flag := outFile.WriteString(", y = ");
flag := outFile.WriteInt(r.y, 7);
flag := outFile.WriteString(", perimemter = ");
flag := outFile.WriteReal(Perimeter(r), 7);
flag := outFile.Ln();
END FileOutput;
END rectangle.

Точно также устроен модуль triangle, обеспечивающий обработку треугольника…

Слайд 83

(******************************************************** figureproc.o: содержит модуль figureProc с процедурами, осуществляющими обработку специализаций, связанных с

(******************************************************** figureproc.o: содержит модуль figureProc с процедурами, осуществляющими обработку специализаций, связанных с
обобщенной геометрической фигурой. Эти процедуры лучше разместить после того, как будут созданы специализации, т.к. их структуру необходимо знать во время обработки.
*********************************************************)
MODULE figureProc;
IMPORT In, Out, FileIO, figure, rectangle, triangle;
(* Процедура, вычисляющая переметр обобщенной фигуры *)
PROCEDURE Perimeter*(VAR f: figure.fig) : REAL;
VAR
p : REAL;
BEGIN
WITH f: rectangle.rect DO p := rectangle.Perimeter(f)
| f: triangle.trian DO p := triangle.Perimeter(f)
ELSE p := 0
END;
RETURN p
END Perimeter;

Слайд 84

(* Процедура, осуществляющая создание и ввод одной из специализаций после предварительного анализа

(* Процедура, осуществляющая создание и ввод одной из специализаций после предварительного анализа
признака вводимой фигуры *)
PROCEDURE Input*() : figure.Pfig;
VAR
tag : INTEGER;
pf : figure.Pfig;
pr : rectangle.Prect;
pt : triangle.Ptrian;
BEGIN
Out.String("Input figure tag (1 - rectangle; 2 - triangle; else - NIL): ");
Out.Ln;
In.Int(tag);
IF tag = 1 THEN
NEW(pr); rectangle.Input(pr^); pf := pr;
ELSIF tag = 2 THEN
NEW(pt); triangle.Input(pt^); pf := pt;
ELSE
pf := NIL;
END;
RETURN pf
END Input;

Слайд 85

(* Процедура, осуществляющая вывод специализаций через параметр-переменную *)
PROCEDURE Output*(VAR f: figure.fig);
BEGIN

(* Процедура, осуществляющая вывод специализаций через параметр-переменную *) PROCEDURE Output*(VAR f: figure.fig);
WITH f: rectangle.rect DO rectangle.Output(f)
| f: triangle.trian DO triangle.Output(f)
ELSE Out.String("Invalid figure"); Out.Ln
END
END Output;

Слайд 86

(* Процедура Заполнения фигуры из файла *)
PROCEDURE FileInput*(VAR inFile : FileIO.TFile) :

(* Процедура Заполнения фигуры из файла *) PROCEDURE FileInput*(VAR inFile : FileIO.TFile)
figure.Pfig;
VAR
flag : INTEGER;
tag : INTEGER;
pf : figure.Pfig;
pr : rectangle.Prect;
pt : triangle.Ptrian;
BEGIN
flag := inFile.ReadInt(tag);
IF tag = 1 THEN
NEW(pr); rectangle.FileInput(inFile, pr^); pf := pr;
ELSIF tag = 2 THEN
NEW(pt); triangle.FileInput(inFile, pt^); pf := pt;
ELSE
pf := NIL;
END;
RETURN pf
END FileInput;

Слайд 87

(* Процедура вывода в файл, использующая передачу параметра-переменной *)
PROCEDURE FileOutput*(VAR outFile :

(* Процедура вывода в файл, использующая передачу параметра-переменной *) PROCEDURE FileOutput*(VAR outFile
FileIO.TFile; VAR f: figure.fig);
VAR
flag : INTEGER;
BEGIN
WITH f: rectangle.rect DO rectangle.FileOutput(outFile, f)
| f: triangle.trian DO triangle.FileOutput(outFile, f)
ELSE
flag := outFile.WriteString("Invalid figure");
flag := outFile.Ln();
END
END FileOutput;
END figureProc.

Слайд 88

(* test.o: *)
MODULE figureTest;
IMPORT In, Out, FileIO, Console,
figure, rectangle, triangle,

(* test.o: *) MODULE figureTest; IMPORT In, Out, FileIO, Console, figure, rectangle,
figureProc, container;
VAR
flag : INTEGER;
c: container.cont;
pf: figure.Pfig;
inFile, outFile : FileIO.TFile;
str : ARRAY 100 OF CHAR;
BEGIN
In.Open;
(* Открытие файла, используемого для чтения исходных
данных *)
Out.String("Input name of source file: ");
In.String(str);
inFile.Assign(str);
inFile.Reset;
(* Открытие файла для записи результатов *)
Out.String("Input name of output file: ");
In.String(str);
outFile.Assign(str);
outFile.Rewrite;

Слайд 89

(* Предварительная инициализаци контейнера *)
container.Init(c);
(* Тестовый вывод содержимого контейнера

(* Предварительная инициализаци контейнера *) container.Init(c); (* Тестовый вывод содержимого контейнера до
до работы *)
Out.String("Initialed Container is:");
container.Output(c);
Out.Ln;
(* Чтение из файла в контейнер *)
flag := container.FileInput(inFile, c);
Out.String("Result Container is:"); Out.Ln;
container.Output(c);
flag := outFile.WriteString("Result Container is:");
flag := outFile.Ln();
container.FileOutput(outFile, c);
(* Закрытие используемых файлов *)
inFile.Close;
outFile.Close;
(* Закрытие используемых файлов *)
inFile.Close;
outFile.Close;
END figureTest.

Слайд 90

Отличительные особенности объектно-ориентированной альтернативы

Основной спецификой ОО подхода является возможность группирования процедур вокруг

Отличительные особенности объектно-ориентированной альтернативы Основной спецификой ОО подхода является возможность группирования процедур
обрабатываемых ими данных. Она, в сочетании с динамическим связыванием объектов, обеспечивает возможность выбора альтернатив без дополнительного анализа признака специализации.
При процедурном же подходе анализ принадлежности артефактов к определенному типу обычно осуществляется внутри процедур, что ведет к использованию алгоритмических методов обработки альтернатив.

Слайд 91

При ООП методы классов напрямую привязываются к обрабатываемым данным. Эта привязка осуществляется

При ООП методы классов напрямую привязываются к обрабатываемым данным. Эта привязка осуществляется
во написания кода (в декларативной) манере посредством инструментальной поддержки механизма виртуализации.
В программе не требуется создание экземпляров конкретных объектов, но заготовки отношений между типами данных и процедурами уже хранятся в виде векторов отношений, в каждом из классов Сi ∈ С:

C1 = (t1, f11, f 11, f m1),
C2 = (t2, f 12, f 22, f m2),

Cn = (tn, f 1n, f 2n, f mn).

Слайд 92

Поэтому, когда в ходе выполнения программы формируется экземпляр объекта Di с данными

Поэтому, когда в ходе выполнения программы формируется экземпляр объекта Di с данными
типа ti, создается и экземпляр отношения Ci, которое содержит и процедуры f1n, f2n, fmn, обеспечивающие требуемую обработку созданного объекта.
Однозначная зависимость между данными и процедурами их обработки, сгруппированными в одном объекте, позволяет каждому экземпляру класса в любой момент обратиться к его же методу через существующее отношение без дополнительного анализа признака при выборе специализированной процедуры.
Достоинства ОО альтернатив проявляются в «физике» конструкции, а не в преимуществах «идеологии».

Слайд 93

Объектное обобщение

При ОО методе организации альтернатив, не требуется анализа признаков специализаций.

Объектное обобщение При ОО методе организации альтернатив, не требуется анализа признаков специализаций.
Используется зависимость двух статически связанных агрегатов, динамически подключаемых к указателю на альтернативу.
Первый агрегат, являясь экземпляром производного или базового класса, содержит данные и процедуры, непосредственно размещенные в его теле. Для различных производных классов состав этих данных и процедур может отличаться, так как не они определяют облик формируемого обобщения.

Слайд 96

Основная задача выбора альтернативы заключается в вызове соответствующей связанной процедуры.
Она решается

Основная задача выбора альтернативы заключается в вызове соответствующей связанной процедуры. Она решается
следующим образом. Через указатель на экземпляр базового класса, обеспечивающий динамическое связывание с любым экземпляром производного класса, осуществляется доступ к интерфейсу объекта.
Назовем такой указатель опорой объектного обобщения.

Слайд 98

Таким образом, в объектном обобщении не нужен специализированный ключ, обеспечивающий анализ существующей

Таким образом, в объектном обобщении не нужен специализированный ключ, обеспечивающий анализ существующей
альтернативы, так как решение проблемы безусловного выбора осуществляется через использование внутренней структуры объекта.
Вместе с тем, можно считать, что такой ключ у каждого класса существует. Им является совокупность всех специализированных процедур, подключаемых через статически связанный агрегат.

Слайд 99

Пример использования объектного обобщения

// Класс, обобщающий все имеющиеся фигуры.
// Является абстрактным,

Пример использования объектного обобщения // Класс, обобщающий все имеющиеся фигуры. // Является
обеспечивая, тем самым,
// проверку интерфейса
class shape {
public:
virtual void In() = 0; // ввод данных
virtual void Out() = 0; // вывод данных
virtual double Area() = 0; // площадь
};

Полная версия примера в oop_examp1.zip

Слайд 100

Опорные процедуры:
virtual void In()
virtual void Out()
virtual double Area()
Все эти виртуальные

Опорные процедуры: virtual void In() virtual void Out() virtual double Area() Все
абстракции начинают обрастать конкретным содержанием только при реализации конкретных специализаций. При этом, разработка специализаций может осуществляться независимо и поэтапно, так как между ними отсутствует какая-либо взаимосвязь. Поэтому, в начале разработаем прямоугольник и его методы.

Слайд 101

// прямоугольник
class rectangle: public shape {
int x, y; // ширина, высота
public:

// прямоугольник class rectangle: public shape { int x, y; // ширина,
// переопределяем интерфейс класса
void In(); // ввод данных из стандартного потока
void Out(); // вывод данных в стандартный поток
double Area(); // вычисление площади фигуры
rectangle(int _x, int _y);
rectangle() {} // создание без инициализации.
};

Слайд 102

// Динамическое создание прямоугольника по двум сторонам
rectangle::rectangle(int _x, int _y): x(_x), y(_y)

// Динамическое создание прямоугольника по двум сторонам rectangle::rectangle(int _x, int _y): x(_x),
{}
// Ввод параметров прямоугольника
void rectangle::In() {
cout << "Input Rectangle: x, y = ";
cin >> x >> y;
}
// Вывод параметров прямоугольника
void rectangle::Out() {
cout << "It is Rectangle: x = " << x
<< ", y = " << y << endl;
}
// Вычисление площади прямоугольника
double rectangle::Area() {
return x * y;
}

Слайд 103

class triangle: public shape {
int a, b, c; // стороны
public:
void

class triangle: public shape { int a, b, c; // стороны public:
In(); // ввод данных из стандартного потока
void Out(); // вывод данных в стандартный поток
double Area(); // вычисление площади фигуры
triangle(int _a, int _b, int _c); // создание
triangle() {} // создание без инициализации.
};

Слайд 104

// Инициализация по трем сторонам
triangle::triangle(int _a, int _b, int _c):
a(_a),

// Инициализация по трем сторонам triangle::triangle(int _a, int _b, int _c): a(_a),
b(_b), c(_c) {}
// Ввод параметров треугольника
void triangle::In() {
cout << "Input Triangle: a, b, c = ";
cin >> a >> b >> c;
}
// Вывод параметров треугольника
void triangle::Out() {
cout << "It is Triangle: a = " << a << ", b = "
<< b << ", c = " << c << endl;
}
// Вычисление площади треугольника
double triangle::Area() {
double p = (a + b + c) / 2.0; // полупериметр
return sqrt(p * (p-a) * (p-b) * (p-c));
}

Слайд 105

Пример программы на языке программирования Оберон 2
(ОО стиль)

Язык дополнительно содержит процедуры, связанные

Пример программы на языке программирования Оберон 2 (ОО стиль) Язык дополнительно содержит
с типом, что обеспечивает полиморфизм процедур, аналогичный полиморфизму методов ОО языков. Процедуры при этом находятся вне типов (но в одном с ним модуле).

Слайд 106

(******************************************************* figure.o: содержит модуль figure с процедурами и типами, осуществляющими обработку обобщенных

(******************************************************* figure.o: содержит модуль figure с процедурами и типами, осуществляющими обработку обобщенных
геометрических фигур
********************************************************)
MODULE figure;
IMPORT Out, FileIO;
TYPE
(* Указатель на обобщенную геометрическую фигуру *)
Pfig* = POINTER TO fig;
(* Запись, определяющая структуру обобщенной
геометрической фигуры *)
fig* = RECORD
(* запись является базовой для всех прочих *)
(* поля отсутствуют *)
END;

Слайд 107

(* Процедура, вычисляющая переметр обобщенной фигуры *)
PROCEDURE (VAR f: fig) Perimeter* ():

(* Процедура, вычисляющая переметр обобщенной фигуры *) PROCEDURE (VAR f: fig) Perimeter*
REAL;
BEGIN
RETURN 0
END Perimeter;
(* Процедура, осуществляющая вывод обобщенной фигуры *)
PROCEDURE (VAR f: fig) Output*;
BEGIN
Out.String("Invalid figure"); Out.Ln
END Output;
(* Процедура вывода обобщенной фигуры в файл *)
PROCEDURE (VAR f: fig) FileOutput*
(VAR outFile : FileIO.TFile);
VAR
flag : INTEGER;
BEGIN
flag := outFile.WriteString("Invalid figure");
flag := outFile.Ln();
END FileOutput;
END figure.

Слайд 108

(********************************************************rectangle.o: содержит модуль rectangle с процедурами и типами, осуществляющими обработку прямоугольников
********************************************************)
MODULE rectangle;

(********************************************************rectangle.o: содержит модуль rectangle с процедурами и типами, осуществляющими обработку прямоугольников ********************************************************)

IMPORT In, Out, FileIO, figure;
TYPE
(* Указатель на прямоугольник *)
Prect* = POINTER TO rect;
(* Запись, определяющая структуру прямоугольника *)
rect* = RECORD(figure.fig)
x*, y* : INTEGER (* стороны прямоугольника*)
END;
(* Процедура динамического создания и инициализации нового прямоугольника *)
PROCEDURE New*(x, y: INTEGER): Prect;
VAR tmp : Prect;
BEGIN
NEW(tmp);
tmp^.x := x;
tmp^.y := y;
RETURN tmp
END New;

Слайд 109

(* Процедура инициализации уже существующего прямоугольника *)
PROCEDURE (VAR r: rect) Init*(x, y:

(* Процедура инициализации уже существующего прямоугольника *) PROCEDURE (VAR r: rect) Init*(x,
INTEGER);
BEGIN
r.x := x;
r.y := y;
END Init;
(* Процедура вычисления периметра прямоугольника - наследника фигуры *)
PROCEDURE (VAR r: rect) Perimeter* () : REAL;
VAR
BEGIN
RETURN (r.x + r.y) * 2
END Perimeter;
(* Процедура ввода прямоугольника из входного потока *)
PROCEDURE (VAR r: rect) Input*;
BEGIN
Out.String("x = "); In.Int(r.x);
Out.String("y = "); In.Int(r.y);
END Input;

Слайд 110

(* Процедура вывода прямоугольника - наследника фигуры *)
PROCEDURE (VAR r: rect) Output*;
BEGIN

(* Процедура вывода прямоугольника - наследника фигуры *) PROCEDURE (VAR r: rect)

Out.String("Rectangle: x = "); Out.Int(r.x, 0);
Out.String(", y = "); Out.Int(r.y, 0);
Out.String(", perimemter = "); Out.Real(r.Perimeter(), 0);
Out.Ln;
END Output;
(* Процедура Заполнения прямоугольника из файла *)
PROCEDURE (VAR r: rect) FileInput*(VAR inFile : FileIO.TFile);
VAR
f : INTEGER;
BEGIN
f := inFile.ReadInt(r.x);
f := inFile.ReadInt(r.y);
END FileInput;

Слайд 111

(* Процедура вывода в файл прямоугольника - наследника фигуры *)
PROCEDURE (VAR r:

(* Процедура вывода в файл прямоугольника - наследника фигуры *) PROCEDURE (VAR
rect) FileOutput*(VAR outFile : FileIO.TFile);
VAR
flag : INTEGER;
BEGIN
flag := outFile.WriteString("Rectangle: x = ");
flag := outFile.WriteInt(r.x, 7);
flag := outFile.WriteString(", y = ");
flag := outFile.WriteInt(r.y, 7);
flag := outFile.WriteString(", perimemter = ");
flag := outFile.WriteReal(r.Perimeter(), 7);
flag := outFile.Ln();
END FileOutput;
END rectangle.

Точно также устроен модуль triangle, обеспечивающий обработку треугольника…

Слайд 112

(********************************************************
figureproc.o: ********************************************************)
MODULE figureProc;
IMPORT In, Out, FileIO, figure, rectangle, triangle;
(* Создание и

(******************************************************** figureproc.o: ********************************************************) MODULE figureProc; IMPORT In, Out, FileIO, figure, rectangle, triangle;
ввод одной из специализаций после предварительного анализа признака вводимой фигуры *)
PROCEDURE Input*() : figure.Pfig;
VAR
tag : INTEGER;
pf : figure.Pfig;
pr : rectangle.Prect;
pt : triangle.Ptrian;
BEGIN
Out.String("Input figure tag (1 - rectangle; 2 - triangle; else - NIL): ");
Out.Ln;
In.Int(tag);
IF tag = 1 THEN
NEW(pr); pr.Input; pf := pr;
ELSIF tag = 2 THEN
NEW(pt); pt.Input; pf := pt;
ELSE
pf := NIL;
END;
RETURN pf
END Input;

Слайд 113

(* Процедура заполнения обобщенной фигуры из файла *)
PROCEDURE FileInput*
(VAR inFile : FileIO.TFile)

(* Процедура заполнения обобщенной фигуры из файла *) PROCEDURE FileInput* (VAR inFile
: figure.Pfig;
VAR
flag : INTEGER;
tag : INTEGER;
pf : figure.Pfig;
pr : rectangle.Prect;
pt : triangle.Ptrian;
BEGIN
flag := inFile.ReadInt(tag);
IF tag = 1 THEN
NEW(pr); pr.FileInput(inFile); pf := pr;
ELSIF tag = 2 THEN
NEW(pt); pt.FileInput(inFile); pf := pt;
ELSE
pf := NIL;
END;
RETURN pf
END FileInput;
END figureProc.

Слайд 114

Отличие методов обобщения

Основным неоспоримым достоинством объектного обобщения является поддержка быстрого и

Отличие методов обобщения Основным неоспоримым достоинством объектного обобщения является поддержка быстрого и
безболезненного, для уже написанного кода, добавления новых альтернатив в существующие обобщающие процедуры. Там, где процедурный подход ведет к поиску и редактированию фрагментов программы, ООП довольствуется только созданием и легкой притиркой новых классов.
Рассмотрим добавление круга в уже написанную программу.

Слайд 115

//--------------------------------------------
// структура, обобщающая все имеющиеся фигуры
// Изменилась, в связи с добавлением круга
struct

//-------------------------------------------- // структура, обобщающая все имеющиеся фигуры // Изменилась, в связи с
shape {
// значения ключей для каждой из фигур
enum key {RECTANGLE, TRIANGLE, CIRCLE};
// добавился признак круга
key k; // ключ
// используемые альтернативы
union { // используем простейшую реализацию
rectangle r;
triangle t;
circle c; // добавился круг
};
};

Полная версия примера в pp_examp1_1.zip

Слайд 116

// Ввод параметров обобщенной фигуры
// Изменилась из-за добавления круга
shape* In() {
shape

// Ввод параметров обобщенной фигуры // Изменилась из-за добавления круга shape* In()
*sp;
// Изменилась подсказка из-за добавления круга
cout <<
"Input key: for Rectangle is 1, for Triangle is 2,"
" for Circle is 3 else break: ";
int k; cin >> k;
switch(k) {
case 1:
sp = new shape;
sp->k = shape::key::RECTANGLE;
In(sp->r); return sp;
case 2:
sp = new shape;
sp->k = shape::key::TRIANGLE;
In(sp->t); return sp;
case 3: // добавился ввод круга
sp = new shape;
sp->k = shape::key::CIRCLE;
In(sp->c);
return sp;
default: return 0;
}
}

Слайд 117

// Вывод параметров текущей фигуры
// Изменилась из-за добавления вывода круга
void Out(shape

// Вывод параметров текущей фигуры // Изменилась из-за добавления вывода круга void
&s){
switch(s.k) {
case shape::key::RECTANGLE:
Out(s.r); break;
case shape::key::TRIANGLE:
Out(s.t); break;
case shape::key::CIRCLE: // добавился вывод круга
Out(s.c); break;
default:
cout << "Incorrect figure!" << endl;
}
}
// Нахождение площади обобщенной фигуры
// Изменилась в связи с добавлением круга
double Area(shape &s){
switch(s.k) {
case shape::key::RECTANGLE:
return Area(s.r);
case shape::key::TRIANGLE:
return Area(s.t);
case shape::key::CIRCLE: // добавился круг
return Area(s.c);
default: return 0.0;
}
}

Слайд 118

При разработке же больших программных систем обработка обобщений может осуществляться не одной

При разработке же больших программных систем обработка обобщений может осуществляться не одной
сотней процедур, каждую из которых потребуется изменить.
А изменение написанного кода всегда связано с риском внести дополнительные ошибки. Кроме этого, изменения, связанные с добавлением сведений о новой специализаций могут затронуть различные единицы компиляции (без изменения располагаемых в них программных объектов), что тоже ведет к дополнительным затратам.

Слайд 119

ОО подход, в аналогичной ситуации, позволяет провести добавление круга практически без изменений

ОО подход, в аналогичной ситуации, позволяет провести добавление круга практически без изменений
уже написанного кода.
При этом, все добавления, связанные с новой фигурой могут осуществляться во вновь создаваемых единицах компиляции. Процесс добавления новой фигуры полностью аналогичен разработке уже созданных и не требует специальных комментариев. В рассматриваемом случае необходимо только изменить процедуру, обеспечивающую ввод новой фигуры.
Хотя, и здесь можно было бы поступить более тонко.

Слайд 120

// Ввод параметров обобщенной фигуры
// Изменяется в связи с добавлением круга
shape* In()

// Ввод параметров обобщенной фигуры // Изменяется в связи с добавлением круга
{
shape *sp;
// Изменяется подсказка
cout << "Input key: for Rectangle is 1,
" for Triangle is 2,"
" for Circle is 3, else break: ";
int k; cin >> k;
switch(k) {
case 1:
sp = new rectangle; sp->In(); return sp;
case 2:
sp = new triangle; sp->In(); return sp;
case 3: // добавлен ввод круга
sp = new circle; sp->In(); return sp;
default: return 0;
}
}

Полная версия примера в oop_examp1_1.zip

Слайд 121

ОО обобщение прекрасно поддерживает эволюционное расширение агрегатов, при котором используется только одна

ОО обобщение прекрасно поддерживает эволюционное расширение агрегатов, при котором используется только одна
производная альтернатива, заменяющая ту, которая эксплуатировалась до нее. Эта возможность вытекает из того, что обобщение - агрегат. А добавляемые или изменяемые в производных классах виртуальные методы можно трактовать как методы агрегата. Таким образом, применяя наследование, мы можем легко расширять внутреннюю структуру и функциональность объекта, предоставляемого клиентам. А сохранение интерфейса обеспечивает прозрачную для клиента подмену одного объекта другим. Методы любого производного класса могут использовать тот же интерфейс, что и методы ранее используемых базовых классов.
Главное для клиента - это отсутствие изменений в интерфейсе связываемого объекта.

Слайд 122

Ограничения техники ООП

Проблемы с расширением функциональности альтернатив

При добавлении нового обработчика специализации необходимо

Ограничения техники ООП Проблемы с расширением функциональности альтернатив При добавлении нового обработчика
включить в базовый класс новую виртуальную процедуру, расширяющую исходный интерфейс. Далее требуется вставить во все производные классы методы, осуществляющие непосредственное вычисление периметров.
При процедурном подходе таких проблем не возникает.
Следует отметить, что большая часть расходов ложится не на модификацию, а перекомпиляцию

Слайд 123

Пример. Добавление вычисления периметра (ПП)

// Вычисление периметра прямоугольника double Perimetr(rectangle &r) {

Пример. Добавление вычисления периметра (ПП) // Вычисление периметра прямоугольника double Perimetr(rectangle &r)
return (r.x + r.y) * 2.0; }
// Вычисление периметра треугольника double Perimetr(triangle &t) { return t.a + t.b + t.c; }
// Нахождение периметра обобщенной фигуры double Perimetr(shape &s) {
switch(s.k) {
case shape::key::RECTANGLE: return Perimetr(s.r);
case shape::key::TRIANGLE: return Perimetr(s.t);
default: return 0.0;
}
}
// Вычисление суммарного периметра для фигур
double Perimetr(container &c) {
double a = 0;
for(int i = 0; i < c.len; i++) {
a += Perimetr(*(c.cont[i]));
}
return a;
}

Слайд 124

Пример. Добавление вычисления периметра (ООП)

// Класс, обобщающает все имеющиеся фигуры.
// Изменился

Пример. Добавление вычисления периметра (ООП) // Класс, обобщающает все имеющиеся фигуры. //
в связи с добавлением вычисления периметра
class shape {
public:
virtual void In() = 0; // ввод данных
virtual void Out() = 0; // вывод данных
virtual double Area() = 0; // вычисление площади фигуры
// добавлено вычисление периметра фигуры
virtual double Perimetr() = 0;
};

Слайд 125

// Измененный прямоугольник (вычисляет периметр)
class rectangle: public shape {
int x,

// Измененный прямоугольник (вычисляет периметр) class rectangle: public shape { int x,
y; // ширина, высота
public:
void In(); // ввод данных из стандартного потока
void Out(); // вывод данных в стандартный поток
double Area(); // вычисление площади фигуры
double Perimetr(); // добавлено вычисление периметра
rectangle(int _x, int _y);
rectangle() {}
}
// Измененный треугольник
class triangle: public shape {
int a, b, c; // стороны
public:
void In(); // ввод данных из стандартного потока
void Out(); // вывод данных в стандартный поток
double Area(); // вычисление площади фигуры
double Perimetr();
// добавлено вычисление периметра фигуры
triangle(int _a, int _b, int _c);
triangle() {}
};

Слайд 126

Проблемы при добавлении специализированных действий

Пример. Надо выводить все прямоугольники, расположенные в

Проблемы при добавлении специализированных действий Пример. Надо выводить все прямоугольники, расположенные в
контейнере. Соответствующая процедура должна "выявлять" прямоугольник из множества фигур всех видов и запускать специализированную процедуру вывода.
Использование процедурного подхода позволяет добавить новый метод без каких-либо проблем, так как его отличие от других обработчиков альтернатив заключается только в анализе одного признака. Ее легко сформировать путем вырезания лишнего кода из уже существующей процедуры вывода произвольной альтернативы.

Слайд 127

// Вывод только обобщенного прямоугольника
// Процедура добавлена без изменений
// других

// Вывод только обобщенного прямоугольника // Процедура добавлена без изменений // других
объектов
bool Out_rectangle(shape &s) {
switch(s.k) {
case shape::key::RECTANGLE:
Out(s.r);
return true;
default: return false;
}
}

Добавление в процедурную программу

Слайд 128

Объектно-ориентированный подход не позволяет, в данном случае, получить элегантное решение.
Возможные пути:
Использование

Объектно-ориентированный подход не позволяет, в данном случае, получить элегантное решение. Возможные пути:
динамического анализа типа объекта. Но это – процедурный прием, так как используются внешние свойства объекта.
Перенос специализированного обработчика в базовый класс и закрепления за ним функций ничего неделания, используя для этого, например, пустое тело. Основным недостатком примененного технического решения является "разбухание" интерфейсов базового и производных классов. Возникают проблемы, связанные модификацией базового класса и полной перекомпиляции всех зависимостей при добавлении каждого нового специализированного метода.

Слайд 129

// Класс, обобщающает все имеющиеся фигуры.
class shape {
public:
virtual void

// Класс, обобщающает все имеющиеся фигуры. class shape { public: virtual void
In() = 0; // ввод данных
virtual void Out() = 0; // вывод данных
// Добавлен вывод только прямоугольника как заглушку
// Метод не является чистым и вызывается
// там где не переопределен
virtual bool Out_rectangle() { return false; };
virtual double Area() = 0; // вычисление площади фигуры
};

Добавление в ОО программу

Слайд 130

// Прямоугольник переопределяет вывод себя
class rectangle: public shape {
int x,

// Прямоугольник переопределяет вывод себя class rectangle: public shape { int x,
y; // ширина, высота
public:
void In(); // ввод данных из стандартного потока
void Out(); // вывод данных в стандартный поток
// Переопределен вывод только прямоугольника
bool Out_rectangle(); // вывод только прямоугольника
double Area(); // вычисление площади фигуры
rectangle(int _x, int _y);
rectangle() {}
}; /
// Вывод только прямоугольника
bool rectangle::Out_rectangle() {
Out(); return true;
};

Слайд 131

Выводы

Независимо от техники и парадигм программирования создаются программные объекты и отношения между

Выводы Независимо от техники и парадигм программирования создаются программные объекты и отношения
ними, с использованием таких понятий как агрегат и обобщение. К одному и тому же конечному результату можно прийти различными путями.
Процедурное агрегирование обладает большей гибкостью по сравнению с объектно-ориентированным, а метафора автономного полнофункционального объекта не всегда удобна, так как вступает в противоречие с ассоциациями, необходимыми при построении эволюционно наращиваемых программ.
Имя файла: Почему-объектноориентированный-подход-победил-процедурный?.pptx
Количество просмотров: 44
Количество скачиваний: 0