ОООП2 - WordPress.com

Download Report

Transcript ОООП2 - WordPress.com

Основы объектноориентированного
программирования
Преподаватель: Шамшев Анатолий
Борисович
ICQ: 446849975
Блог: anshamshev.wordpress.com
Основы объектно - ориентированного
программирования
1
Список литературы
•
•
•
•
Бертран Мейер
Объектно-ориентированное конструирование программных
систем
Биллиг В.А.
Основы программирования на C#
Интернет-университет информационных технологий - ИНТУИТ.ру,
2006
Анисимов А.Е., Пупышев В.В.
Сборник заданий по основаниям программирования
БИНОМ. Лаборатория знаний, Интернет-университет
информационных технологий - ИНТУИТ.ру, 2006
Фридман А.Л.
Язык программирования Си++
Основы объектно - ориентированного
программирования
2
Методологии программирования
•
•
•
•
•
Машинно-ориентированное
Структурное
Модульное
Объектно-ориентированное
Компонентно-ориентированное,
функциональное (перспективное)
Основы объектно - ориентированного
программирования
3
Традиционные модульные
структуры
• Подпрограммы (процедуры, функции)
• Пакеты – развитие подпрограмм
• Классы (в ОО – языках - модули)
Понятие объекта и объектноориентированного подхода
• Объект – сущность предметной области,
отличающаяся от других сущностей своими
отличительными особенностями (свойствами)
и выполняемыми действиями (методами)
• Объектно-ориентированный подход
подразумевает декомпозицию предметной
области на множество взаимодействующих
между собой объектов
• Объект есть конкретный экземпляр класса
Основы объектно - ориентированного
программирования
5
Преимущества использования OOметода
•
•
•
•
•
Совместимость
Переносимость
Простота использования
Эффективность:
Своевременность, экономичность и
функциональность
Состав объекта
•
•
•
•
•
•
•
Поля (свойства)
Методы
Конструкторы, в т.ч. статические
Деструкторы
События
Константы
Операторы
Основы объектно - ориентированного
программирования
7
Декомпозиция
Пример – иерархия нисходящего проектирования
Иерархия нисходящего проектирования
Модульная композиция
Может применяться при наличии уже готовых или ранее созданных модулей.
Один из критериев – модульная понятность.
Модульная понятность
ОО - подход
Объектная декомпозиция + 3 важнейших понятия:
• Инкапсуляция – объединение в одном классе
данных и обрабатывающих их методов
• Наследование – Возможность расширения
возможностей класса путём создания
наследников
• Полиморфизм – возможность присвоения
экземпляру класса экземпляров классов наследников
Инкапсуляция и скрытие
информации
• Разработчик каждого модуля должен выбрать некоторое
подмножество свойств модуля в качестве официальной
информации о модуле, доступной авторам клиентских
модулей.
• Применение этого правила означает, что каждый модуль
известен всем остальным через некоторое официальное
описание, или так называемые общедоступные (public)
свойства.
• Описание должно включать лишь некоторые из свойств
модуля. Остальные свойства должны оставаться не
общедоступными, или закрытыми. Вместо терминов общедоступные и закрытые свойства - используются также
термины: экспортируемые и частные (скрытые) (private)
свойства. Общедоступные свойства модуля известны
также как интерфейс.
Скрытие информации
Наследование
Основы объектно - ориентированного
программирования
15
Полиморфизм
• Под полиморфизмом в ООП понимают
способность одного и того же
программного текста x.M выполняться поразному, в зависимости от того, с каким
объектом связана сущность x.
Полиморфизм гарантирует, что
вызываемый метод M будет принадлежать
классу объекта, связанному с сущностью x.
Основа полиморфизма
• одностороннее присваивание объектов внутри семейства
классов; сущность, базовым классом которой является
класс предка, можно связать с объектом любого из
потомков.
• переопределение потомком метода, наследованного от
родителя. Благодаря переопределению, в семействе
классов существует совокупность полиморфных методов
с одним именем и сигнатурой;
• динамическое связывание, позволяющее в момент
выполнения вызывать метод, который принадлежит
целевому объекту.
Пример класса – рациональное
число
type
TRational=class
public
private
end;
Основы объектно - ориентированного
программирования
18
Свойства класса
• Два целых числа - m и n представляют
рациональное число. Они и становятся
полями класса.
//Поля класса. Числитель и знаменатель
рационального числа.
private
m,n:integer;
Конструктор класса
constructor TRational.Create(a,b:integer);
var d:integer;
begin
if (b=0) then
begin
m := 0;
n := 1;
end
else
begin
//приведение знака
if (b < 0) then
begin
b = -b;
a = -a;
end;
//приведение к несократимой дроби
d := nod(a, b);
m := round(a/d);
n := round(b/d);
end;
Метод НОД
function TRational.nod(n: Integer; m: Integer);
var p:integer;
begin
p := 0;
m := Abs(m);
n := Abs(n);
if (n > m) then
begin
p := m;
m := n;
n := p;
end;
while (n<>0) do
begin
p := m mod n;
m := n;
n := p;
end;
result:=m;
end;
Печать рациональных чисел
procedure TRational.PrintRational(name:string);
var tempstr1,tempstr2:string;
begin
Str(m,tempstr1);Str(n,tempstr2);
WriteLn(name+' = '+tempstr1+'/'+tempstr2);
end;
Работа с рациональными
числами
r1:= TRational.Create(0,0); r2:=TRational.Create(1,1);
r3:= TRational.Create(10, 8); r4 := TRational.Create(2, 6);
r5 := TRational.Create(4, -12); r6 := TRational.Create(-12, -14);
r1.PrintRational('r1:(0,0)');
r2.PrintRational('r2:(1,1)');
r3.PrintRational('r3:(10,8)');
r4.PrintRational('r4:(2,6)');
r5.PrintRational('r5: (4,-12)');
r6.PrintRational('r6: (-12,-14)');
Основы объектно - ориентированного
программирования
23
Объектная технология
• Выполнить программную систему - значит использовать
некоторые процессоры для применения некоторых действий к
некоторым объектам.
• Процессоры - это вычислительные устройства (физические или
виртуальные), выполняющие команды.
• Действия - это операции, производящие вычисления.
• Объекты - это структуры данных, к которым применяются
действия.
Функциональная декомпозиция
• Обеспечение непрерывности - это главная
забота при рассмотрении реального
жизненного цикла программных систем,
включающего не только производство
приемлемой первоначальной версии, но и
эволюцию системы на протяжении долгого
времени.
• Чтобы оценить качество архитектуры, нужно
понять не только то, насколько просто было
изначально получить эту архитектуру, не
менее важно выяснить, насколько легко ее
можно изменить.
Проектирование «Сверху вниз»
•
•
•
Метод логичен, хорошо организует дисциплину мышления, поддается
эффективному изучению, поощряет систематическое проектирование систем,
помогает разработчику найти пути преодоления больших сложностей,
возникающих обычно на начальной стадии разработки систем.
Сомнительной является сама идея охарактеризовать всю систему
посредством только одной функции.
Используя в качестве основы декомпозиции системы на модули свойства,
которые склонны подвергаться наибольшим изменениям, этот метод не
способен учесть эволюционную природу программных систем.
Несколько главных функций
• При эволюции системы то, что вначале воспринималось
как ее главная функция, с течением времени может
стать менее важным.
• Процесс изменений происходит непрерывно. Новая
система все еще является во многих отношениях "той
же", что и старая, но исходная "главная функция",
которая вначале выглядела самой важной, часто
становится просто одной из функций системы, а иногда
и совсем исчезает, становясь ненужной.
• Выделение конкретной вершины в реальных задачах
очень затруднено
Функции и эволюция
•
•
•
•
•
Главная функция часто не только не является наилучшим критерием для
начального определения системы, но она может также в процессе эволюции
системы почти сразу оказаться среди изменяемых свойств.
Большие научные программы очень часто имеют две версии: одну, которая
"пусть работает всю ночь, выполняя большую порцию вычислений", и другую,
которая "позволяет мне сначала проверить некоторые вещи, посмотреть на
результаты, а затем вычислить еще что-нибудь".
Подход сверху вниз не способен учесть то обстоятельство, что
результирующие программы должны быть ничем иным как двумя версиями
одной и той же программной системы, независимо от того, как они
проектируются - одновременно или одна выводится из другой.
Два самых неприятных последствия подхода сверху вниз: во-первых, он
сосредотачивается на внешнем интерфейсе, во-вторых, он преждевременно
устанавливает временные отношения.
Архитектура системы должна основываться на содержании, а не на форме.
Упор на внешний интерфейс неизбежен для метода, ключевой вопрос
которого: "Что система будет делать для конечного пользователя?».
Декомпозиция, основанная на объектах
• Использование объектов как ключа для разбиения
системы на модули основано на содержательных
целях, в частности, на расширяемости, возможности
повторного использования и совместимости.
• Никакой подход к созданию ПО не может быть
полным, если он не учитывает обе стороны функции и объекты. Поэтому нам нужно и в ООметоде сохранить надлежащее место для функций,
даже если они в результирующей архитектуре
системы будут подчинены объектам. Понятие
абстрактного типа данных предоставляет
определение объектов, в котором для функций
зарезервировано подходящее место.
Объектно – ориентированное
конструирование ПО
• ОО-конструирование ПО - это метод
разработки ПО, который строит архитектуру
всякой программной системы на модулях,
выведенных из типов объектов, с которыми
система работает (а не на одной или
нескольких функциях, которые она должна
предоставлять).
• Не спрашивай вначале, что система делает.
Спроси, кто в системе это делает!
• Вместо поиска самой верхней функции системы будут
анализироваться типы входящих в нее объектов.
Проектирование системы будет продвигаться вперед
путем последовательного улучшения понимания
классов этих объектов. Это процесс построения снизу
вверх устойчивых и расширяемых решений для
отдельных частей задачи и сборки из них все более и
более мощных блоков до тех пор, пока не будет получен
окончательный блок, доставляющий решение
первоначальной задачи. При этом можно надеяться, что
полученное решение не является единственно
возможным: если правильно применять метод, то те же
компоненты, собранные по-другому и, возможно,
объединенные с другими, окажутся достаточно
общими, чтобы получить в качестве побочного продукта
также и решения каких-то новых задач.
• Эта простая идея - вначале рассматривать данные,
забыв о непосредственной цели системы, - может
послужить ключом к повторному использованию и
расширяемости.
Основные вопросы
• Как находить релевантные типы объектов?
• Как описывать типы объектов?
• Как описывать взаимоотношения типов
объектов и их близость?
• Как использовать типы объектов для
структурирования ПО?
Выявление типов объектов
• Многие объекты лежат на поверхности - они непосредственно
моделируют объекты физической реальности, к которой
применяется ПО. ОО-технология является мощным средством
моделирования, использующим типы программных объектов
(классы) для моделирования типов физических объектов и
отношения между типами объектов для моделирования
отношений между типами физических объектов таких, как
агрегирование и специализация.
• Одним из источников типов объектов является повторное
использование: классы, ранее определенные другими. Данный
метод часто является очень полезным
• Опыт и копирование. Ознакомившись с успешными ООразработками и образцами проектов, проектировщик может
вдохновиться этими более ранними усилиями.
Описания типов и объектов
• Нужно добиваться независимости описаний
от представлений, чтобы не потерять
главное преимущество проектирования
сверху вниз: абстрактность.
• Нужно найти для функций подходящее
место в архитектуре программ, чья
декомпозиция основана на анализе типов
объектов.
Описание отношений
• Отношение "быть клиентом" достаточно широкое и покрывает многие
виды зависимостей. Примерами таких зависимостей является
отношение, часто называемое агрегацией (присутствие в каждом
объекте типа B подобъекта типа A ), а также зависимость по ссылке и
родовая зависимость.
• Отношение наследования покрывает многочисленные формы
специализации.
• Многие зависимости можно выразить в общем виде другими
способами. Например, для описания зависимости "от 1-го до n"
(каждый объект типа B связан с не менее чем одним и не более чем с
n объектами типа A) указывается, что B является клиентом A, и
присоединяется инвариант класса, точно определяющий природу
отношения "быть клиентом". Так как инварианты классов выражаются
с помощью логического языка, они покрывают намного больше
различных отношений, чем может предложить подход сущность-связь
или другие аналогичные подходы.
Ключевые концепции
•
•
•
•
•
•
•
•
•
•
Вычисление включает три вида ингредиентов: процессоры (или потоки управления), действия
(или функции) и данные (или объекты).
Архитектуру системы можно получить исходя из функций или из типов объектов.
Описание, основанное на типах объектов, с течением времени обеспечивает лучшую
устойчивость и лучшие возможности для повторного использования, чем описание,
основанное на анализе функций системы.
Как правило, неестественно считать, что задача системы состоит в реализации только одной
функции. У реальной системы обычно имеется не одна "вершина" и ее лучше описывать как
систему, предоставляющую множество услуг.
На ранних стадиях проектирования и разработки системы не нужно уделять много внимания
ограничениям на порядок действий. Многие временные соотношения могут быть описаны
более абстрактно в виде логических ограничений.
Функциональное проектирование сверху вниз не подходит для программных систем с долгим
жизненным циклом, включающим их изменения и повторное использование.
При ОО-конструировании ПО структура системы основывается на типах объектов, с которыми
она работает.
При ОО-разработке первоначальный вопрос не в том, что система делает, а в том, с какими
типами объектов она это делает. Решение о том, какая функция является самой верхней
функцией системы (и имеется ли таковая), откладывается на последние этапы процесса
проектирования.
Чтобы проектируемое ПО было расширяемым и допускало повторное использование, ООконструирование должно выводить архитектуру из достаточно абстрактных описаний
объектов.
Между типами объектов могут существовать два вида отношений: "быть клиентом" и
наследование.
Абстрактные типы данных
• Чтобы получить надлежащие описания
объектов, метод должен удовлетворять трем
условиям:
• Описания должны быть точными и
недвусмысленными.
• Они должны быть полными - или, по крайней
мере, иметь в каждом конкретном случае
нужную полноту (некоторые детали можно
намеренно опускать).
• Они не должны быть излишне
специфицированы.
Различные реализации
• Удобным и хорошо изученным примером
является описание объектов типа стек. Объект
стек служит для того, чтобы накапливать и
доставать другие объекты в режиме
"последним пришел - первым ушел" ("LIFO"),
элемент, вставленный в стек последним, будет
извлечен из него первым. Стек повсеместно
используется в информатике и во многих
программных системах, в частности,
компиляторы и интерпретаторы усыпаны
разными видами стеков.
Различные представления
Опасность излишней
спецификации
• Результаты изучения Линцем (Lientz) и Свенсоном
(Swanson) стоимости сопровождения показали, что
более 17% стоимости ПО приходится на изменения в
форматах данных. Очевидно, что метод, который ставит
анализ и проектирование в зависимость от физического
представления структур данных, не обеспечит
разработку достаточно гибкого ПО.
• Поэтому при использовании объектов или типов
объектов в качестве основы для архитектуры системы
требуется найти лучший способ описания, чем
конкретное представление.
Абстрактный взгляд на стек
• Представления стека при всех их различиях объединяет то, что они
описывают структуру "хранения», к которой применяются
определенные операции, обладающие определенными свойствами.
Сосредоточившись не на выборе конкретного представления
структуры, а на этих операциях и свойствах, можно получить
достаточно абстрактное, но, тем не менее, полезное описание
понятия стек.
Обычно для стеков рассматриваются следующие операции:
• Команда вталкивания некоторого элемента на вершину стека - put.
• Команда удаления верхнего элемента стека - remove.
• Запрос элемента, находящегося на вершине стека (если стек не пуст) item.
• Запрос на проверку пустоты стека.
• Кроме того, понадобится операция-конструктор для создания пустого
стека - make.
Формализация спецификаций
•
•
•
•
•
Спецификация АТД предоставит эту информацию. Она состоит из четырех
разделов, разъясняемых в следующих разделах:
ТИПЫ - указываются специфицируемые типы - STACK[G] .
ФУНКЦИИ - перечисляются операции, применяемые к экземплярам данного
АТД.
– функция-конструктор моделирует операцию, создающую экземпляры T
из экземпляров других типов или вообще не использующую аргументов.
– функции-запросы моделируют операции, которые устанавливают
свойства T, выраженные в терминах экземпляров других типов.
– функции-команды моделируют операции, которые по существующим
экземплярам T и экземплярам других типов выдают новые экземпляры
типа T.
АКСИОМЫ – свойства объектов
ПРЕДУСЛОВИЯ – условия, необходимые для работы функций – запросов и
функций - команд
Полная спецификация стека
Построение классов на основе АТД
• Класс - это абстрактный тип данных, снабженный некоторой
(возможно частичной) реализацией
• Полностью реализованный класс называется эффективным
(effective). Класс, который реализован лишь частично или совсем не
реализован, называется отложенным (deferred). Всякий класс
является либо отложенным, либо эффективным.
• Компоненты эффективного класса:
– Спецификации АТД.
– Выбора представления.
– Реализация множества функций в при помощи средств представления в
виде множества механизмов (или компонентов (features)), каждый из
которых реализует одну из функций в терминах представления и при этом
удовлетворяет аксиомам и предусловиям.
Роль отложенных классов
• При ОО-проектировании многие аспекты реализации будут опущены,
проектирование должно сосредотачиваться на архитектурных
свойствах высокого уровня - на том, какую функциональность
обеспечивает каждый модуль системы, а не на том, как он это делает.
• При постепенном продвижении к полной реализации будут
добавляться все новые и новые ее свойства до тех пор, пока не будет
получен эффективный класс.
• Один из лучших способов обеспечить необходимую для
сопровождения системы информацию - это сохранить отложенные
классы в ее окончательной форме.
• У отложенных классов имеется также применение, полностью
связанное с реализацией. Они служат для классификации групп
связанных типов объектов, предоставляют некоторые наиболее
важные многократно используемые модули высокого уровня,
фиксируют общие свойства поведения многих вариантов и играют
ключевую роль (вместе с полиморфизмом и динамическим
связыванием) в обеспечении децентрализации и расширяемости
программной архитектуры.
АТД и сокрытие информации
Классы и объекты
•
•
•
•
•
Класс - это абстрактный тип данных, поставляемый с возможно частичной
реализацией.
Абстрактные типы данных (АТД) являются математическим понятием,
пригодным на этапе подготовки спецификации - в процессе анализа. Понятие
класса, предусматривая частичную или полную реализацию, обеспечивает
необходимую связь с разработкой ПО на этапах проектирования и
программирования. Напомним, класс называется эффективным, если его
реализация полна, и отложенным - при частичной реализации.
Аналогично АТД, класс - это тип, описывающий множество возможных
структур данных, называемых экземплярами (instances) класса. Экземпляры
АТД являются абстракциями - элементами математического множества.
Экземпляр класса конкретен - это структура данных, размещаемая в памяти
компьютера и обрабатываемая программой.
Термин "объект" появляется как побочный продукт определения "класса".
Объект это просто экземпляр некоторого класса.
Программные тексты, описывающие создаваемую систему, содержат
определения классов. Объекты создаются только в процессе выполнения
программ.
Роль классов
• Модули - это структурные единицы, из которых состоит программа.
Независимо от конкретного выбора той или иной модульной
структуры, модуль всегда рассматривается как синтаксическая
концепция.
• Тип является статическим описанием вполне определенных
динамических объектов - элементов данных, которые
обрабатываются во время выполнения программной системы. Набор
типов обычно содержит предопределенные типы, такие как INTEGER
или CHARACTER, а также пользовательские типы: записи (структуры),
указатели, множества (в Pascal), массивы и другие. Понятие типа
является семантической концепцией, и каждый тип непосредственно
влияет на выполнение программной системы, так как описывает
форму объектов, которые система создает и которыми она
манипулирует.
• Класс как модуль и как тип
• Мощь ОО-метода, во многом, следствие этого отождествления.
Наследование, в частности, может быть полностью понято только при
рассмотрении его, как модульного расширения, так и, одновременно,
уточнения специализации типа.
Унифицированная система типов
• Каждый объект является экземпляром некоторого
класса
• Преимущества:
– Всегда желательно иметь простую и универсальную схему, нежели
множество частных случаев. Предлагаемая система типов
полностью опирается на понятие класса.
– Описание базовых типов как абстрактных структур данных и далее
как классов является простым и естественным.
– Определение базовых типов как классов позволяет использовать
все возможности ОО, главным образом наследование и родовые
средства. Если базовые типы не будут классами, то придется
вводить ряд ограничений и рассматривать частные случаи.
Простой класс: точка на плоскости
/// <summary>
/// Абстрактное представление точки
/// </summary>
public abstract class AbstractPoint
{
public int x, y;//Координаты на плоскости
abstract public void Draw();//Рисование точки
public abstract double Distance(Point p1);//Расстояние до
другой точки
}
Просто точка
public class Point:AbstractPoint
{
public Point()
{
}
public Point(int x1,int y1)
{
x = x1;
y = y1;
}
override public void Draw()
{
Console.WriteLine("Рисование точки");
}
public override double Distance(Point p1)
{
return (Math.Sqrt((x - p1.x)*(x - p1.x) + (y - p1.y)*(y - p1.y)));
}
}
Наследник точки - линия
public class Line:Point
{
public Line()
{
}
public Line (int xstart, int ystart, int xend, int yend)
{
x = xstart; y = ystart; x1 = xend; y1 = yend;
}
public int x1, y1;
private double GetLength()
{
return Math.Sqrt((x - x1)*(x - x1) + (y - y1)*(y - y1));
}
public double Length
{
get { return GetLength();}
}
override public void Draw()
{
Console.WriteLine("Рисование линии");
}
public override double Distance(Point p1)
{
return base.Distance(p1);
}
}
Использование созданных типов
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
Point t = new Point();
t.Draw();
t = new Line();
t.Draw();
Console.ReadKey();
}
}
}
Результат выполнения программы
Управление памятью
• Создание нового экземпляра – оператор new
• Выделение памяти – область памяти, называемая
«кучей» (heap)
• Динамический режим выделения памяти
Динамический режим выделения
памяти
Преимущества:
• Простота, эффективность
• Возможность переназначения выделенной
памяти
Недостатки:
• Необходимость следить за освобождением
памяти
• Появление неиспользуемых объектов
• Появление недостижимых объектов
Манипулирование памятью
• Выделение памяти – оператор new
• Освобождение памяти – Pascal – оператор
Dispose; C# - автоматический сборщик
мусора – System.GC; в отдельных случаях
реализация в классе метода Dispose
Критерии ОО-подхода
• Бесшовность
• Понятие класса – основное понятие языка. Неформально,
класс - элемент ПО, описывающий абстрактный тип
данных и его частичную или полную реализацию.
• Утверждения - предусловия и постусловия программ
класса и инварианты классов - играют эту роль.
• Классы – единственный вид модулей. Каждый тип данных
основан на классе
• Основной механизм вычислений – вызов компонента
• Автор класса должен иметь возможность указать область
доступности класса.
Критерии ОО-подхода
• Язык должен обеспечивать механизм восстановления в
неожиданных аварийных ситуациях.
• Система типов гарантирует безопасность работы с
объектами во время выполнения программной системы.
• Существовать возможность создания классов с
формальными родовыми параметрами,
представляющими произвольные типы.
• Возможность объявить класс наследником другого класса
или нескольких классов.
• Механизм универсальности должен поддерживать форму
ограниченной универсальности.
Критерии ОО-подхода
• Должно быть возможным переопределить спецификацию, сигнатуру
и реализацию наследованного компонента.
• Полиморфизм - Должна иметься возможность в период выполнения
присоединять к сущности объекты различных возможных типов под
управлением наследования.
• Динамическое связывание – Вызов сущностью компонента всегда
должен запускать тот компонент, который соответствует типу
присоединенного объекта, а не типу сущности.
• Необходимо иметь возможность определять во время выполнения,
соответствует ли тип объекта статически заданному типу.
• Необходимо иметь возможность написания класса или компонента
как отложенного, то есть специфицированного, но не полностью
реализованного.
• Язык должен давать возможность надежного автоматического
управления памятью, а реализация должна обеспечить наличие
автоматического менеджера, управляющего памятью, в функцию
которого входит сборка мусора.
Свойства ОО-подхода
•
•
•
•
•
Декомпозиции (decomposability).
Композиции (composability).
Понятности (understandability).
Непрерывности (continuity).
Защищенности (protection).
Декомпозиция
Иерархия нисходящего
проектирования
Модульная композиция
Модульная понятность
Модульная непрерывность
• Метод удовлетворяет критерию Модульной Непрерывности, если
незначительное изменение спецификаций разработанной системы
приведет к изменению одного или небольшого числа модулей.
Модульная защищённость
• Метод удовлетворяет
критерию Модульной
Защищенности, если он
приводит к архитектуре
системы, в которой
аварийная ситуация,
возникшая во время
выполнения модуля,
ограничится только этим
модулем, или, в худшем
случае, распространится
лишь на несколько
соседних модулей.
Правила ОО-подхода
• Прямое отображение (Direct Mapping).
• Минимум интерфейсов (Few Interfaces).
• Слабая связность интерфейсов (Small
interfaces - weak coupling).
• Явные интерфейсы (Explicit Interfaces).
• Скрытие информации (инкапсуляция)
(Information Hiding).
Прямое отображение
• Модульная структура, создаваемая в
процессе конструирования ПО, должна
оставаться совместимой с модульной
структурой, создаваемой в процессе
моделирования проблемной области.
Минимум интерфейсов
Слабая связность интерфейсов
• Если два модуля общаются между собой, то они должны
обмениваться как можно меньшим объемом
информации.
Явные интерфейсы
• Всякое общение двух модулей A и B между собой
должно быть очевидным и отражаться в тексте A
и/или B.
Скрытие информации
• Разработчик каждого модуля должен выбрать некоторое
подмножество свойств модуля в качестве официальной
информации о модуле, доступной авторам клиентских
модулей.
• Применение этого правила означает, что каждый модуль
известен всем остальным через некоторое официальное
описание, или так называемые общедоступные (public)
свойства.
• Описание должно включать лишь некоторые из свойств
модуля. Остальные свойства должны оставаться не
общедоступными, или закрытыми. Вместо терминов общедоступные и закрытые свойства - используются также
термины: экспортируемые и частные (скрытые) (private)
свойства. Общедоступные свойства модуля известны
также как интерфейс.
Скрытие информации
Принципы ОО-подхода
• Принцип Лингвистических Модульных
Единиц (Linguistic Modular Units).
• Принцип Самодокументирования (SelfDocumentation).
• Принцип Унифицированного Доступа
(Uniform Access).
• Принцип Открыт-Закрыт (Open-Closed).
• Принцип Единственного выбора (Single
Choice).
Лингвистические модульные
единицы
•
•
•
•
•
•
•
•
Модули должны соответствовать синтаксическим единицам используемого языка.
Упомянутым выше языком может быть язык программирования, язык
проектирования, язык оформления технических требований и т. д. В случае языка
программирования модули должны независимо компилироваться.
Этот принцип на любом уровне (анализа, проектирования, реализации) не
допускает объединения метода, исходящего из концепции модульности, и языка,
не содержащего соответствующих модульных конструкций.
Непрерывность: если границы модуля в окончательном тексте программы не
соответствуют логической декомпозиции спецификации или проекта, то при
сопровождении системы и ее эволюции будет затруднительно или даже
невозможно поддерживать совместимость различных уровней.
Прямое отображение: необходимо поддерживать явное соответствие между
структурой модели и структурой решения.
Декомпозиция: для разбиения системы на отдельные задачи необходимо быть
уверенным, что результатом решения каждой из задач явится четко ограниченная
синтаксическая единица;
Композиция: что же, кроме модулей с однозначно определенными
синтаксическими границами, можно объединять между собой?
Защищенность: лишь в случае, если модули синтаксически разграничены, можно
надеяться на возможность контроля области действия ошибок.
Самодокументируемость
Разработчик модуля должен стремиться к
тому, чтобы вся информация о модуле
содержалась в самом модуле.
Унифицированный доступ
• Унифицированный Доступ направлен на решение
вопроса о том, какой должна быть нотация,
задающая применение метода f к объекту x, не
содержащая каких-либо преждевременных
обязательств по способу реализации метода f.
• Все службы, предоставляемые модулем, должны
быть доступны в унифицированной нотации,
которая не подведет вне зависимости от
реализации, использующей память или
вычисления.
Унифицированный доступ
Принцип открыт - закрыт
• Модуль называют открытым, если он еще доступен для расширения.
Например, имеется возможность расширить множество операций в
нем или добавить поля к его структурам данных.
• Модуль называют закрытым, если он доступен для использования
другими модулями. Это означает, что модуль уже имеет строго
определенное окончательное описание. На уровне реализации
закрытое состояние модуля означает, что модуль можно
компилировать, сохранять в библиотеке и делать его доступным для
использования другими модулями (его клиентами). На этапе
проектирования или спецификации закрытие модуля означает, что он
одобрен руководством, внесен в официальный репозиторий
утвержденных программных элементов проекта - базу проекта, и его
интерфейс опубликован в интересах авторов других модулей.
Принцип единственного выбора
• Всякий раз, когда система программного
обеспечения должна поддерживать
множество альтернатив, их полный список
должен быть известен только одному
модулю системы.
Принцип единственного выбора
type PUBLICATION =
record
author, title: STRING;
publication_year: INTEGER
case pubtype:(book, journal,
conference_proceedings) of
book:(publisher: STRING);
journal:(volume, issue: STRING);
proceedings:(editor, place: STRING) end
Повторное использование
Преимущества:
•
Своевременность. При использовании уже существующих компонентов нужно меньше
разрабатывать, а, следовательно, ПО создается быстрее.
•
Сокращение объема работ по сопровождению ПО. Если кто-то разработал ПО, то он же отвечает и
за его последующее развитие.
•
Надежность. Получая компоненты от поставщика с хорошей репутацией, вы имеете определенную
гарантию, что разработчики предприняли все нужные меры, включая всестороннее тестирование и
другие методы контроля качества. В большинстве случаев можно ожидать, что кто-то уже испытал
эти компоненты до вас и обнаружил все возможно остававшиеся ошибки.
•
Эффективность. Факторы, способствующие возможности повторного использования ПО, побуждают
разработчиков компонентов пользоваться наилучшими алгоритмами и структурами данных,
известными в их конкретной сфере деятельности. При разработке большого проекта невозможно
оптимизировать все его детали. Следует стремиться к достижению наилучших решений в своей
области знаний, а в остальном использовать профессиональные разработки.
•
Совместимость. Если использовать хорошую современную ОО-библиотеку, то ее стиль повлияет, за
счет естественного "процесса диффузии", на стиль разработки всего ПО.
•
Инвестирование. Создание повторно используемого ПО позволяет сберечь плоды знаний и
открытий лучших разработчиков, превращая временные ресурсы в постоянные.
Ресурсы, используемые повторно
•
•
•
•
•
Персонал
Проекты и спецификации
Образцы проектов (шаблоны)
Исходный текст
Абстрактные модули
Трудности повторного
использования
• Придумано не нами (Not Invented Here или
"NIH")
• Привычка препятствовать нововведениям
(Habit Inhibiting Novelty или "HIN")
• Повторно использовать или переделать?
Форматы распространения для
повторного использования
• Краткая форма – описание интерфейса +
исполняемый код
• Полная форма (суперпоставка) – полный
набор документации + исходный код +
исполняемый код
Требования к модульным структурам
• Изменчивость Типов (Type Variation) - Повторно используемый модуль
поиска должен быть применим ко многим различным типам
элементов без того чтобы пользователи вынуждены были
производить "вручную" изменения в тексте программы. .
• Группирование Подпрограмм (Routine Grouping) - . Самодостаточный,
повторно используемый модуль должен включать множество
подпрограмм, обеспечивающих каждую из необходимых операций.
• Изменчивость Реализаций (Implementation Variation) – Один модуль
не может охватить все возможные варианты реализации – поддержка
идеи семейства модулей.
• Независимость Представлений (Representation Independence) - Общая
структура повторно используемого модуля должна позволять
определять действия при отсутствии сведений о реализации модуля.
• Факторизация Общего Поведения (Factoring Out Common Behaviors) –
Общие действия выполняются одиним и тем же способом вне
зависимости от внутренней реализации.
Традиционные модульные
структуры
• Подпрограммы (процедуры, функции)
• Пакеты – развитие подпрограмм
• Классы (в ОО – языках - модули)
Перегрузка и универсальность
Два технических приема - перегрузка (overloading) и
универсальность (genericity) предлагают свои
решения, направленные на достижение большей
гибкости модулей.
• Перегрузка - это связывание с одним именем
более одного содержания: перегрузка методов,
перегрузка операторов.
• square (x: INTEGER): INTEGER
• square (x: REAL): REAL
• square (x: DOUBLE): DOUBLE
Универсальность
• Универсальность - это механизм определения
параметризованных шаблонов модулей (module
patterns), параметры которых представляют собой
типы.
• List<G>
• Имя G, представляющее произвольный тип, и
называется формальным родовым параметром
(formal generic parameter).
• List<int>, List<string>, List<double>
• Универсальность позволяет использовать тот же
текст, реализующий некоторое понятие, для
различных видов объектов
Процесс разработки программной
системы
Стек
/// <summary>
/// Абстрактный класс GenStack<T> задает
контейнер с
/// доступом LIFO:
/// Функции:
/// конструктор new: -> GenStack<T>
/// запросы:
/// item: GenStack -> T
/// empty: GenStack -> Boolean
/// процедуры:
/// put: GenStack*T -> GenStack
/// remove: GenStack -> GenStack
/// Аксиомы:
/// remove(put(s,x)) = s
/// item(put(s,x)) = x
/// empty(new)= true
/// empty(put(s,x)) = false
/// </summary>
abstract public class GenStack<T>
{
/// <summary>
/// require: not empty();
/// </summary>
/// <returns>элемент вершины(последний
пришедший)</returns>
abstract public T item();
/// <summary>
/// require: not empty();
/// ensure: удален элемент
вершины(последний пришедший)
/// </summary>
abstract public void remove();
/// <summary>
/// require: true; ensure: elem находится
в вершине стека
/// </summary>
/// <param name="elem"></param>
abstract public void put(T t);
/// <summary>
/// require: true;
/// </summary>
/// <returns>true если стек пуст, иначе
false </returns>
abstract public bool empty();
}// class GenStack
Односвязанный стек
/// <summary>
/// Стек, построенный на
односвязных элементах списка
GenLinkable<T>
/// </summary>
public class OneLinkStack<T> :
GenStack<T>
{
public OneLinkStack()
{
last = null;
}
private GenLinkable<T> last; //ссылка
на стек (вершину стека)
public override T item()
{
return (last.Item);
} //item
public override bool empty()
{
return (last == null);
} //empty
public override void put(T elem)
{
GenLinkable<T> newitem = new
GenLinkable<T>();
newitem.Item = elem;
newitem.Next = last;
last = newitem;
} //put
public override void remove()
{
last = last.Next;
} //remove
} //class OneLinkStack
Класс Linkable
public class GenLinkable<T>
{
public T Item;
public GenLinkable<T> Next;
public GenLinkable()
{
Item = default(T);
Next = null;
}
}
Стек на основе массива
public class ArrayUpStack<T> : GenStack<T>
{
private int SizeOfStack;
private T[] stack;
private int top;
/// <summary>
/// конструктор
/// </summary>
/// <param name="size">размер
стека</param>
public ArrayUpStack(int size)
{
SizeOfStack = size;
stack = new T[SizeOfStack];
top = 0;
}
/// <summary>
/// require: (top < SizeOfStack)
/// </summary>
/// <param name="x"> элемент,
помещаемый в стек</param>
public override void put(T x)
{
stack[top] = x;
top++;
}
public override void remove()
{
top--;
}
public override T item()
{
return stack[top - 1];
}
public override bool empty()
{
return (top == 0);
}
} //class ArrayUpStack
public void TestStacks()
{
OneLinkStack<int> stack1 = new OneLinkStack<int>();
OneLinkStack<string> stack2 = new OneLinkStack<string>();
ArrayUpStack<double> stack3 = new ArrayUpStack
<double>(10);
stack1.put(11);
stack1.put(22);
int x1 = stack1.item(), x2 = stack1.item();
if ((x1 == x2) && (x1 == 22)) Console.WriteLine("OK!");
stack1.remove();
x2 = stack1.item();
if ((x1 != x2) && (x2 == 11)) Console.WriteLine("OK!");
stack1.remove();
x2 = (stack1.empty()) ? 77 : stack1.item();
if ((x1 != x2) && (x2 == 77)) Console.WriteLine("OK!");
stack2.put("first");
stack2.put("second");
stack2.remove();
string s = stack2.item();
if (!stack2.empty()) Console.WriteLine(s);
stack3.put(3.33);
stack3.put(Math.Sqrt(Math.PI));
double res = stack3.item();
stack3.remove();
res += stack3.item();
Console.WriteLine("res= {0}", res);
}
public void TestPerson()
{
OneLinkStack<int> stack1 = new
OneLinkStack<int>();
OneLinkStack<string> stack2 = new
OneLinkStack<string>();
ArrayUpStack<double> stack3 = new
ArrayUpStack
<double>(10);
ArrayUpStack<Person> stack4 = new
ArrayUpStack<Person>(7);
stack2.put("Петров");
stack2.put("Васильев");
stack2.put("Шустов");
stack1.put(27);
stack1.put(45);
stack1.put(53);
stack3.put(21550.5);
stack3.put(12345.7);
stack3.put(32458.8);
stack4.put(new Person(stack2.item(),
stack1.item(),
stack3.item()));
stack1.remove();
stack2.remove();
stack3.remove();
stack4.put(new Person(stack2.item(),
stack1.item(),
stack3.item()));
stack1.remove();
stack2.remove();
stack3.remove();
stack4.put(new Person(stack2.item(),
stack1.item(),
stack3.item()));
Person pers = stack4.item();
pers.PrintPerson();
stack4.remove();
pers = stack4.item();
pers.PrintPerson();
stack4.remove();
pers = stack4.item();
pers.PrintPerson();
stack4.remove();
if (stack4.empty())
Console.WriteLine("OK!");
}
Базовые методы обеспечения
надёжности
• Ключевой концепцией является Проектирование
по контракту (Design by Contract) - установление
отношений между классом и его клиентами в
виде формального соглашения, недвусмысленно
устанавливающее права и обязанности сторон.
Только через точное определение для каждого
модуля требований и ответственности можно
надеяться на достижение существенной степени
доверия к большим программным системам.
Метод Флойда
• Одним из методов доказательства правильности
программ был метод Флойда, при котором программа
разбивалась на участки, окаймленные утверждениями булевскими выражениями (предикатами). Истинность
начального предиката должна была следовать из входных
данных программы. Затем для каждого участка
доказывалось, что из истинности предиката, стоящего в
начале участка, после завершения выполнения
соответствующего участка программы гарантируется
истинность следующего утверждения - предиката в конце
участка. Конечный предикат описывал постусловие
программы.
Утверждения Assert
• В C# эта схема поддерживается тем, что классы
Debug и Trace имеют метод Assert, аргументом
которого является утверждение. Если истинно
булево выражение в Assert, то вычисления
продолжаются, не оказывая никакого влияния на
нормальный ход вычислений. Если оно ложно, то
корректность вычислений под сомнением, их
выполнение приостанавливается и появляется
окно с уведомлением о произошедшем событии,
Схема Бертрана обработки
исключительных ситуаций
• Бертран Мейер предложил следующую
схему обработки исключительных
ситуаций. В основе ее лежит подход к
проектированию программной системы на
принципах Проектирования по
Контракту.
• У обработчика исключительной ситуации
только две возможности - Retry и Rescue.
Параллельная работа обработчиков
исключений
• Обработчику исключения - catch-блоку, захватившему исключение, передается текущее исключение. Анализируя свойства этого объекта,
обработчик может понять причину, приведшую к возникновению
исключительной ситуации, попытаться ее исправить и в случае
успеха продолжить вычисления. После завершения catch-блока
выполняются операторы текущего метода, следующие за
конструкцией try-catch-finally.
• Зачастую обработчик исключения не может исправить ситуацию или
может выполнить это лишь частично, предоставив решение
оставшейся части проблем вызвавшему методу - предшественнику в
цепочке вызовов. Как правило, в конце своей работы обработчик
исключения выбрасывает исключение, выполняя оператор throw.
Блок finally
• Освобождение ресурсов, занятых try-блоком, выполняет
finally-блок. Если он присутствует, он выполняется всегда,
сразу же после завершения работы try-блока, как бы
последний ни завершился. Во всех случаях, прежде чем
управление будет передано по предписанному
назначению ( в том числе, прежде чем произойдет захват
исключения), предварительно будет выполнен finally-блок,
который освобождает ресурсы, занятые try-блоком, а
параллельно будет происходить освобождение стека от
локальных переменных.
public void Pattern()
{
do
{
try
{
bool Danger = false;
Success = true;
MakeJob();
Danger = CheckDanger();
if (Danger)
throw (new MyException());
MakeLastJob();
}
catch (MyException me)
{
if (count > maxcount)
throw(new MyException("Три попытки были безуспешны"));
Success = false;
count++;
//корректировка ситуации
Console.WriteLine("Попытка исправить ситуацию!");
level += 1;
}
} while (!Success);
}
public class MyException : Exception
{
public MyException()
{
}
public MyException(string message)
: base(message)
{
}
public MyException(string message, Exception e)
:
base(message, e)
{
}
}
private Random rnd = new Random();
private int level = -10;
private bool Success; //true - нормальное
завершение
private int count = 1; // число попыток
выполнения
private const int maxcount = 3;
private void MakeJob()
{
Console.WriteLine("Подготовительные работы завершены");
}
private bool CheckDanger()
{
//проверка качества и возможности продолжения работ
int low = rnd.Next(level, 10);
if (low > 6) return (false);
return (true);
}
private void MakeLastJob()
{
Console.WriteLine("Все работы завершены успешно");
}
public void TestPattern()
{
Excepts ex1 = new Excepts();
try
{
ex1.Pattern();
}
catch (Exception e)
{
Console.WriteLine("исключительная ситуация при вызове Pattern");
Console.WriteLine(e.ToString());
}
}
Класс Exception
• Основными свойствами класса являются:
• Message;
• HelpLink;
• InnerException;
• Source;
• StackTrace;
• TargetSite
Из методов класса отметим метод GetBaseException.
throw(new MyException("Все попытки Pattern безуспешны", me));
static public void PrintProperties(Exception e)
{
Console.WriteLine("Свойства исключения:");
Console.WriteLine("TargetSite = {0}", e.TargetSite);
Console.WriteLine("Source = {0}", e.Source);
Console.WriteLine("Message = {0}", e.Message);
if (e.InnerException == null)
Console.WriteLine("InnerException = null");
else Console.WriteLine("InnerException = {0}",
e.InnerException.Message);
Console.WriteLine("StackTrace = {0}", e.StackTrace);
Console.WriteLine("GetBaseException = {0}",
e.GetBaseException());
}
Цель программирования
• Качество - это цель инженерной
деятельности; построение качественного
ПО (software) - цель программной
инженерии (software engineering).
Методологии программирования
•
•
•
•
•
Машинно-ориентированное
Структурное
Модульное
Объектно-ориентированное
Компонентно-ориентированное
(перспективное)
Факторы качества
• Внешние
– Надёжность, простота использования,
эффективность и т.д.
• Внутренние
– Модульность, читаемость и т.д.
Внешние факторы
Корректность (Correctness)
• Корректность - это способность ПО
выполнять точные задачи так, как они
определены их спецификацией.
Устойчивость (Robustness)
• Устойчивость - это способность ПО
соответствующим образом реагировать на
аварийные ситуации.
Внешние факторы
Расширяемость (Extendibility)
• Расширяемость - это легкость адаптации
ПО к изменениям спецификации.
Повторное использование (Reusability)
• Повторное использование есть
способность элементов ПО служить для
построения многих различных
приложений.
Внешние факторы
Совместимость (Compatibility)
• Совместимость - это легкость сочетания
одних элементов ПО с другими.
Эффективность (Efficiency)
• Эффективность - это способность ПО как
можно меньше зависеть от ресурсов
оборудования.
Внешние факторы
• Переносимость (Portability)
Переносимость - это легкость переноса ПО в
различные программные и аппаратные среды.
• Простота использования (Easy of Use)
Простота использования - это легкость, с которой
люди с различными знаниями и квалификацией
могут научиться использовать ПО и применять
его для решения задач.
Внешние факторы
Функциональность (Functionality)
• Функциональность - это степень возможностей,
обеспечиваемых системой.
Своевременность (Timeliness)
• Своевременность - это выпуск ПО в нужный
момент, то есть тогда или незадолго до того, как
у пользователей появилась соответствующая
потребность в подобной системе.
Другие факторы
• Верифицируемость (Verifiability) - это легкость подготовки процедур
приемки, особенно тестовых данных, процедур обнаружения
неполадок и трассировки ошибок на этапах заключительной проверки
и введения проекта в действие.
• Целостность (Integrity) - это способность ПО защищать свои различные
компоненты (программы, данные) от несанкционированного доступа
и модификации.
• Восстанавливаемость (Repairability) - это способность облегчать
устранение дефектов.
• Экономичность (Economy) сочетается c своевременностью - это
способность системы завершиться, не превысив выделенного
бюджета или даже не истратив его.
Два пути работы над проектом:
кривые Осмонда
Документация
• Внешнюю, дающую пользователям возможность
понять сильные стороны системы и удобство их
использования.
• Внутреннюю, дающую разработчикам ПО
возможность понять структуру и реализацию
системы
• Описывающую интерфейс модулей. Она дает
возможность разработчикам понять функции,
реализованные модулем, без изучения его
реализации.
Противоречия в требованиях
• Корректность и устойчивость
• Расширяемость и повторное использование
Преимущества использования OOметода
•
•
•
•
•
Совместимость
Переносимость
Простота использования
Эффективность:
Своевременность, экономичность и
функциональность
Сопровождение ПО
70% стоимости ПО приходится на его
сопровождение. Никакое изучение
качества ПО не может быть
удовлетворительным, если оно
игнорирует этот аспект.
Ссылки:
• Экзаменационные вопросы:
• http://rapidshare.com/files/437054826/Exam_2010.doc
• Презентация:
• http://rapidshare.com/files/437055078/OOOP2.ppt
Основы объектно - ориентированного
программирования
130