Разработка алгоритмов, реализующих полученные операции
Каждой операции, определённой в уточнённой объектной модели, должен быть сопоставлен алгоритм, реализующий эту операцию. При выборе алгоритма можно руководствоваться следующими соображениями:
- вычислительная сложность алгоритма: для алгоритмов, применяемых к достаточно большим массивам данных, важно, чтобы оценка их вычислительной сложности была разумной; например, вряд ли имеет смысл избегать косвенности в ссылках, особенно когда введение косвенности существенно упрощает понимание программы; в то же время замена пузырьковой сортировки с оценкой сложности n2 на алгоритм сортировки с оценкой n´log n всегда резко ускоряет вычисления;
- понятность алгоритма и лёгкость его реализации: для достижения этого можно даже пойти на небольшое снижение эффективности; например, введение рекурсии всегда снижает скорость выполнения программы, но упрощает её понимание (рис. 3.7);
- гибкость: большая часть программ рано или поздно должна быть модифицирована; как правило, высокоэффективный алгоритм труден для понимания и модификации; одним из выходов является разработка двух алгоритмов выполнения операции: простого, но не очень эффективного, и эффективного, но сложного; при модификации в этом случае изменяется более простой алгоритм, что обеспечивает работоспособность системы на период разработки более эффективного модифицированного алгоритма.
Определение функции n!
0! = 1
n!= nx(n-1)
Нерекурсивный алгоритм Рекурсивный алгоритм
factorial (int n) fact (int n)
{ {
Int 1, f=1 Int f=1
for (1=n, 1 > 0: 1--) If (n > 0)
f = f^1: f = f^ fact (n – 1):
return (f): return (f):
} }
Рис. 3.7. Сравнение рекурсивного и нерекурсивного
алгоритмов вычисления n!
Выбор алгоритмов связан с выбором структур данных, обрабатываемых этими алгоритмами. Удачный выбор структур данных позволяет существенно оптимизировать алгоритм.
Ещё одним способом упрощения и оптимизации алгоритмов является введение внутренних (вспомогательных) классов. Эти классы не имеют соответствий в реальном мире; они связаны с реализацией, но могут существенно упростить её (примеры: класс стек, класс двусвязный список и т.п.).
Наконец, во многих случаях бывает полезным внести некоторые изменения в структуру объектной модели. Эти изменения сводятся к введению дополнительных классов и к перераспределению операций между классами.
При распределении операций по классам руководствуются следующими соображениями:
- если операция выполняется только над одним объектом, то она определяется в классе, экземпляром которого является этот объект;
- если аргументами операции являются объекты разных классов, то её следует поместить в класс, к которому принадлежит результат операции;
- если аргументами операции являются объекты разных классов, причём изменяется значение только одного объекта, а значения других объектов только читаются, то её следует поместить в класс, к которому принадлежит изменяемый объект;
- если классы вместе с их зависимостями образуют звезду с центром в одном из классов, то операцию, аргументами которой являются объекты этих классов, следует поместить в центральный класс.
Оптимизация разработки
Объектная модель, построенная на этапе анализа требований к программной системе, содержит информацию о логической структуре системы; на этапе разработки объектная модель уточняется и пополняется: в неё добавляются детали, связанные с необходимостью обеспечить более эффективный доступ к информационным структурам во время работы системы. Цель оптимизации разработки – заменить семантически корректную, но недостаточно эффективную модель, построенную на этапе анализа, более эффективной. В процессе оптимизации разработки выполняются следующие преобразования:
- добавляются избыточные зависимости, чтобы минимизировать потери, связанные с доступом к данным, и максимизировать удобство работы с ними;
- изменяется порядок вычислений для достижения большей эффективности;
- сохраняются производные атрибуты, чтобы устранить необходимость перевычисления сложных выражений.
На этапе анализа требований к программной системе избыточные зависимости нежелательны, так как они не вносят в модель новой информации. Однако на этапе разработки мы должны приспособить структуру объектной модели к требованиям эффективной реализации системы. Пример использования избыточной (производной) зависимости для повышения эффективности поиска представлен на рисунке 3.9: на рисунке 3.8(а) показаны зависимости из исходной объектной модели; добавление производной (и, следовательно, избыточной) зависимости (рис. 3.9(б)) позволяет резко ускорить поиск сотрудников, говорящих по-китайски.
|
|
Рис. 3.8. Ускорение поиска с помощью производной зависимости
Рис. 3.9. Использование производных атрибутов
для исключения повторных вычислений
Использование производных атрибутов для исключения повторных вычислений показано на рисунке 3.10: запоминание координат однажды найденных атрибутов и операций в специальных списках позволяет избежать повторного поиска.
Рис. 3.10. Использование производной зависимости
На рисунке 3.10 показано, как введение производной зависимости позволяет не перевычислять координаты перекрывающихся элементов окон в оконной системе для графического дисплея.
Производные атрибуты должны изменять свои значения, когда меняются их базовые значения. Для обеспечения этого пользуются одним из трёх методов:
- явное перевычисление: каждый производный атрибут определяется с помощью одного или нескольких базовых объектов; когда значения базовых объектов меняются, требуется изменить значения всех производных атрибутов, связанных с ними;
- периодическое перевычисление всех производных атрибутов (в мо-мент изменения базового значения производные атрибуты перевычисляются);
- использование активных значений: активным называется значение, с которым связано некоторое множество зависимых значений; все зависимые значения группируются вокруг определяющих их активных значений и перевычисляются синхронно с ними.
Реализация управления
Реализация управления связана с реализацией динамической модели объектов системы. Известны три подхода к реализации динамической модели:
- процедурное управление: состоянию соответствует определённый фрагмент программы;
- управление через события: явная реализация конечного автомата;
- использование параллельных независимых задач.
Процедурное управление является традиционным способом реализации динамической модели; в этом случае состояние объекта определяется текущим оператором программы, а каждому переходу соответствует оператор ввода: следующее состояние определяется по текущему состоянию и вводимому имени события.
Пример практического использования процедурного управления представлен на рисунке 3.11.
Рис. 3.11. Псевдокод, соответствующий динамической модели ATM