Файл: История и развитие методологии объектно-ориентированного программирования. Сферы применения.pdf

ВУЗ: Не указан

Категория: Курсовая работа

Дисциплина: Не указана

Добавлен: 14.05.2023

Просмотров: 416

Скачиваний: 2

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

СОДЕРЖАНИЕ

Введение

Часть I Объектно-ориентированное программирование

Глава 1 Эволюция методологий программирования

1.1 Поколения языков программирования

1.1.1 Начало начал, или первое поколение языков программирования

1.1.2 Развитие алгоритмических абстракций. Второе поколение языков программирования.

1.1.3 Модульность, как единица построения программных систем, или третье поколение языков программирования

1.2. ЗАРОЖДЕНИЕ ОБЪЕКТНОЙ МОДЕЛИ

1.2.1 Объектные языки программирования

1.2.2 Объектно-ориентированные языки

1.2.3 Объектно-ориентированный анализ, дизайн и проектирование

1.3 Парадигмы программирования

Глава 2 Составные части объектного подхода

2.1 Абстрагирование

2.2. ИНКАПСУЛЯЦИЯ

2.3 Модульность

2.4. ИЕРАРХИЯ

2.5 Типизация

2.5. ТИПИЗАЦИЯ

2.6. ПАРАЛЛЕЛИЗМ

Глава 3 Объекты

3.1 Что такое объект с точки зрения ООП

3.3. ПОВЕДЕНИЕ

3.3.1 Классификация методов объектов

3.3.2 Роли объектов

3.3.3 Связь объектов и автоматов, активные и пассивные объекты

3.4 Идентичность

3.5 Жизненный цикл объекта

3.6 Отношения между объектами

Заключение

Литература

/* file simple_stack.h */

#ifndef _SIMPLE_STACK_H

#define _SIMPLE_STACK_H

#define MAX_STACK_SIZE 200

typedef struct { int top; void* content[MAX_STACK_SIZE]; } SimpleStack;

/* returns -1 if overflow */

int push(SimpleStack *pstack, void *pobj);

/* returns NULL if empty */

void* pop(SimpleStack *pstack);

SimpleStack* allocateStack();

void freeStack(SimpleStack *pstack);

#endif /*_SIMPLE_STACK_H */

/* file simple_stack.c */

#include

#include

#include "simple_stack.h"

SimpleStack* allocateStack() { SimpleStack* pstack = (SimpleStack*)malloc(sizeof (SimpleStack)); pstack->top = 0; return pstack; }

void freeStack(SimpleStack *pstack) { free(pstack); } int push(SimpleStack *pstack, void *pobj) { if (pstack->top < MAX_STACK_SIZE) { pstack->content[(pstack->top)++] = pobj; return 0; } else { return -1; /* overflow */ } } void* pop(SimpleStack *pstack) { if(pstack->top > 0) { return pstack->content[–(pstack->top)]; } else { return NULL; /* underflow */ } }

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

• Данные экземпляров структуры не защищены от недобросовестного использования. Следующий код может непоправимо нару- шить работу программы: SimpleStack pStack = allocateStack(); pStack->top = -10;

• Стек реализован в виде массива, и нет простого способа заме- нить его реализацию в программах, уже использующих данную версию.

Можно существенно усовершенствовать наш стек, если воспользуемся средствами модульного программирования, имеющимися в языке С++.

// Файл advanced_stack.h

// Усовершенствованный вариант стека, использующий

// механизм модульности языка C++

#ifndef _ADVANCED_STACK_H

#define _ADVANCED_STACK_H

// Таким образом мы определяем функциональный

// контракт модульной реализации стека namespace advanced_stack { struct Stack_impl; typedef struct Stack_impl AdvancedStack;

// returns -1 if overflow int push(AdvancedStack *pstack, void *pobj);

// returns NULL if empty void* pop(AdvancedStack *pstack);

AdvancedStack* allocateStack();

void freeStack(AdvancedStack* pstack); }

#endif

// _ADVANCED_STACK_H

// Файл advanced_stack.cpp

// Реализация контракта модульной реализации стека

#include "advanced_stack.h"

namespace advanced_stack { const int MAX_STACK_SIZE = 200; struct Stack_impl { int top; void* content[MAX_STACK_SIZE]; };

Stack_impl* allocateStack() { Stack_impl *pstack = new Stack_impl(); pstack->top = 0; return pstack; } void freeStack(Stack_impl *pstack) { delete pstack; } int push(Stack_impl *pstack, void *pobj) { if (pstack->top < MAX_STACK_SIZE) { pstack->content[(pstack->top)++] = pobj; return 0; } else { return -1; // overflow } } void* pop(Stack_impl *pstack) { if(pstack->top > 0) { return pstack->content[–(pstack->top)]; } else { return 0; // underflow } } } В данной реализации решены обе упомянутые выше проблемы. Примером использования модульной организации стека может стать следующая программа: #include #include "advanced_stack.h" int main() { using namespace advanced_stack; AdvancedStack *pstack = allocateStack(); //pstack->top = 10; // Данная строка вызвала бы ошибку компиляции. char *letters [3] = {"one","two","three"}; int i; push(pstack, (void*) (letters[0])); push(pstack, (void*) (letters[1])); push(pstack, (void*) (letters[2])); for (i = 0; i < 3; i++) { printf("\n%s\n", (char*)pop(pstack)); } freeStack(pstack); } Если в будущем потребуется заменить реализацию стека, то это можно сделать изменив реализацию структуры Stack_impl и функции работы с ней, реализованные в файле advanced_stack.cpp. Также можно объявить новое пространство имен (namespace), например dynamic_stack, в котором будет реализован стек изменяемого размера. В прежней программе, использующей модульную реализацию стека, достаточно будет заменить строку using namespace advanced_stack; на строку using namespace dynamic_stack; Это можно сделать при условии, что новое пространство имен имеет контракт, совпадающий с предыдущим. Опытные программисты на C могут заявить, что реализацию модульного подхода можно осуществить без использования механизма namespace языка C++, используя исключительно возможности препроцессора. Но на самом деле, дело обстоит не так просто как кажется на первый взгляд, и в дальнейшем, при детальном рассмотрении C++, мы увидим, какие преимущества несет использование внутренней языковой поддержки модульности в этом языке средствами namespace. Таким образом, мы вплотную подошли к идее объединения данных и кода, предназначенного для обработки этих данных, в одну концептуально целостную единицу. Следующим шагом является объектная реализация стека. //Файл object_stack.h #ifndef _OBJECT_STACK_H #define _OBJECT_STACK_H // Объектная модель стека class ObjectStack { private: static const int MAX_STACK_SIZE; int top; void **content; public: //Вместо allocateStack(...), используется конструктор ObjectStack(); //Вместо freeStack() используется деструктор ~ObjectStack(); int push(void* pobj); void* pop(); }; #endif //_OBJECT_STACK_H // Файл object_stack.cpp #include "object_stack.h" // Реализация объектной модели стека const int ObjectStack::MAX_STACK_SIZE = 200; ObjectStack::ObjectStack() :top(0) { content = new void*[MAX_STACK_SIZE]; } ObjectStack::~ObjectStack() { delete[] content; } void* ObjectStack::pop() { if(top > 0) { return content[–top]; } else { return 0; // underflow } } int ObjectStack::push(void* pobj) { if (top < MAX_STACK_SIZE) { content[top++] = pobj; return 0; } else { return -1; // overflow } } Примером использования такого стека может служить следующая программа: int main() { ObjectStack *pstack = new ObjectStack(); char *letters [3]= {"one","two","three"}; int i; //Доступ к членам данным в принципе невозможен извне //функций-членов класса ObjectStack // pstack->top = -10; //Ошибка времени компиляции pstack->push ((void*) letters[0]); pstack->push ((void*) letters[1]); pstack->push ((void*) letters[2]); for (i= 0;ipop()); } delete pstack; } Пользоваться такой реализацией стека гораздо удобней, кроме то- го, она надежней предыдущих. Код лаконичен и прост для понимания. Важным шагом к объектно-ориентированному программированию стало появление языков, поддерживающих объектный взгляд на напи- сание программных систем. Основной идеей объектного подхода является объединение данных и производимых над этими данными операций в одно концептуально замкнутое понятие — класс. Данные класса не должны изменяться извне его, доступ к данным стоит осуществлять только через функции- члены (методы класса). Программа, написанная на объектном языке, представляет собой совокупность объектов, каждый из которых принадлежит к определен- ному абстрактному типу данных (классу) и имеет интерфейс в виде на- бора методов для взаимодействия друг с другом (посылки сообщений).


1.2.2 Объектно-ориентированные языки

Насколько появление объектных языков и объектной модели явилось прямым развитием модульного подхода при написании программных систем, настолько и появление объектно-ориентированных механиз- мов явилось следующим шагом в развитии объектной модели. Прежде, чем мы собственно обозначим, что же такое объектно ориентированный язык, давайте обратимся к известной всем со школьной скамьи системе классификации животного мира. Система эта иерархична. Рассмотрим такое существо как домашняя любимица Мурка. Прежде всего следует отметить, что Мурка является кошкой, причем, скорее всего, кошкой определенной породы, например сиамской. Таким образом, наша Мурка является объектом класса сиамских кошек. Если полностью выписать классификационную принадлежность Мурки, то мы получим, что она принадлежит к сиамской породе домашних кошек, роду кошек, семейству кошачьих, отряду хищных, классу млекопитающих, подтипу позвоночных, типу хордовых, царству животных. Каждая из ступеней определяет ряд характеристик и особенностей, которыми обладает объект Мурка. Тот факт, что данный объект принадлежит к классу сиамских кошек говорит нам о том, что он имеет определенный окрас, длину шерсти и особенности поведения. Кроме того, мы знаем, что при этом наша кошка Мурка является представителем вида домашних кошек, что определяет ее размер, сильно отличающийся от размера, к примеру, тигра. Как представитель семейства кошачьих, Мурка обладает определенными особенностями анатомического строения; будучи хищником, она способна употреблять в пищу других животных; от класса млекопитающих она унаследовала способность выкармливать детенышей молоком и теплокровное строение организма и так далее. В терминах объектно-ориентированного подхода, мы говорим, что перечисленные классы образуют иерархию наследования, построенную на основе отношения «обобщение/специализация». Классы не всегда образуют строго пирамидальную иерархию. Например, если мы определим класс домашних животных (представители которого могут жить в доме человека и взаимодействовать со своими хозяевами), то обнаружим, что домашние животные могут быть как теплокровными, так и хладнокровными. Свойство быть домашним животным не вписывается естественным образом в нашу иерархию живых организмов, а скорее играет роль признака (интерфейса), или «подмешивания». В дальнейшем это понятие будет рассмотрено более подробно. Сложность окружающей действительности и многообразие объектов реального мира побуждает человека вырабатывать классификационные схемы, для того чтобы мыслить и оперировать объектами. Поэтому, совершенно естественным стало внедрение поддержки объект- ного подхода в языках программирования. Наличие иерархических отношений наследования между абстракциями (классами и интерфейсами), экземплярами которых являются объекты в программе, является отличительной чертой объектно-ориентированного подхода в программировании. Программа написанная на объектно-ориентированном языке представляет собой совокупность объектов, каждый из которых принадлежит к определенному абстрактному типу данных (классу), а классы образуют иерархию наследования.


1.2.3 Объектно-ориентированный анализ, дизайн и проектирование

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

Можно дать современные пояснения роли и сути ООА в процессе разработке программного обеспечения основываясь на методологии RUP(Rational Unified Process). Основными объектами, с которыми оперирует объектно-ориентированный анализ являются прецеденты взаимодействия (Use-Cases) и актеры (Actors). Причем, прецеденты взаимодействий рассматриваются в сфере отношений, схожих с отношениями возникающими между классами (расширения (extention), включения(inclusion), генерализации(generalization)).


1.3 Парадигмы программирования

Подводя итог рассмотрению истории развития языков и методологий программирования, следует упомянуть парадигмы различных методов построения программных систем, сформулированные Страуструпом [2, §2]. Процедурное программирование: Реши какие требуются процедуры; используй наилучшие доступные алгоритмы. Модульное программирование: Реши, какие требуются модули; разбей программу так, чтобы скрыть данные в модулях.(Принцип сокрытия данных) Объектное программирование: Реши, какие требуются типы; обеспечь полный набор операция для каждого типа. Объектно-ориентированное программирование: Реши, какие требуются классы; обеспечь полный набор операций для каждого класса; явно вырази общность через наследование. Обобщенное программирование: Реши, какие требуются алгоритмы; параметризуй их так, чтобы они могли работать со множеством подходящих типов и структур данных. На этом заканчивается наш краткий экскурс в историю развития программных технологий. Теперь, после того как мы проследили весь путь становления объектно-ориентированного подхода, приступим к непосредственному изучению его отличительных особенностей и более детальному рассмотрению основополагающих принципов.

Глава 2 Составные части объектного подхода

Можно встретить различные понятия стиля программирования, здесь мы остановимся на следующем определении по Боборову и Стефику, которое приводит Буч[1, §2]. Стиль программирования — «это способ построения программ, основанный на определенных принципах программирования, и выбор подходящего языка, который делает понятными программы, написанные в этом стиле». Они выделили пять основных разновидностей стилей программирования, которые приведены ниже вместе с используемыми в них видами абстракций:

• процедурно-ориентированный алгоритмы

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

• логико-ориентированный цели, исчисление предикатов

• ориентированный на правила правила «если-то»

• ориентированный на ограничения инвариантные соотношения

Таким образом, для решения тех или иных задач могут подходить разные стили, и, соответственно, языки их поддерживающие, в зависимости от класса поставленной задачи. Для проектирования баз знаний более пригоден стиль ориентированный на правила (язык PROLOG), а для решения вычислительных задач — ориентированный на алгоритмы (например язык FORTRAN). Тем не менее, объектно-ориентированный стиль является наиболее широко применимым стилем программирования для основных классов задач в силу его близости к образу мыслей человека. Концептуальной базой объектно-ориентированного стиля программирования является объектная модель, основывающаяся на четырех главных принципах:


• абстрагирование

• инкапсуляция

• модульность

• иерархия

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

• типизация

• парллелизм

• сохраняемость

Однако, принимая во внимание то, что в задачи курса входит изучение объектно-ориентированного программирования на языках C++ и Java, а Smalltalk в настоящее время выходит из употребления, данное классическое разделение требует корректировки, а именно, типизацию следует отнести к главным принципам. Типизация имеет очень большое значение как в языке Java (сильная типизация), так и в C++, (типизация также является сильной, но существуют правила неявного преобразования типов, определяемых пользователем). Следует также отметить, что в отличие от С++, в Java присутству- ет непосредственная языковая поддержка принципов параллелизма и сохраняемости. Без следования этим принципам вы можете по прежнему програм- мировать на объектно-ориентированных языках C++, Java, Smalltalk, Object Pascal, но под внешней красотой программы будет угадываться стиль Pascal, C или FORTRAN. Выразительные способности объектно-ориентированных языков будут при этом потеряны или искажены, и их использование не по назначению приведет к усложнению, а не облегчению решения поставленной задачи. Рассмотрим в подробностях каждый из принципов объектной модели.

2.1 Абстрагирование

Принцип абстрагирования реализуется в ряде методов решения задач с использованием объектной модели. В литературе можно встретить разные определения и расшифровки того, что понимается под термином абстрагирование. Хоар дает следующее определение: «абстрагирование проявляется в нахождении сходств между определенными объектами, ситуациями или процессами реального мира, и в принятии решений на основе этих сходств, отвлекаясь на время от имеющихся различий». Абстракция по Шоу — это «упрощенное описание или изложение системы, при котором одни свойства и детали выделяются, а другие опускаются. Хорошей является такая абстракция, которая подчеркивает детали, существенные для рассмотрения и использования, и опускает те, которые на данный момент несущественны». Берзинс, Грей и Науман полагают: «идея квалифицируется как абстракция только, если она может быть изложена, понята и проанализирована независимо от механизма, который будет в дальнейшем принят для ее реализации». Если объединить эти точки зрения, получим определение абстракции по Бучу: Абстракция выделяет существенные характеристики некоторого объекта, отличающие его от всех других видов объектов и, таким образом, четко определяет его концептуальные границы с точки зрения наблюдателя. Абстрагирование концентрирует внимание на внешних характеристиках объекта и позволяет отделить наиболее существенные особенности его поведения от менее существенных. Граница между существенными и несущественными с точки зрения разрабатываемой программной системы особенностями поведения объекта называется барьером абстракции. Последний определяется исходя из принципа минимизации связей, согласно которому интерфейс объекта должен описывать только существенные аспекты поведения. Так же следует соблюдать принцип наименьшего удивления. Следуя ему абстракция должна охватывать только поведение описываемого ей объекта, и, соответственно, не привносит сюрпризов и побочных эффектов, лежащих вне сферы ее применимости. Выделение полного и достаточного набора абстракций при решении задачи с применением объектного подхода представляет собой главную задачу объектно-ориентированного проектирования. Во время разработки программной системы могут появляться абстракции разных категорий, начиная с объектов, которые почти точно соответствуют реалиям предметной области, и кончая объектами, целесообразность использования которых сомнительна. Седвиц и Старк выделили следующий спектр абстракций: