- •Лекция 1. Качество ПО
- •Внешние и внутренние факторы
- •Обзор внешних факторов
- •Корректность (Correctness)
- •Устойчивость (Robustness)
- •Расширяемость (Extendibility)
- •Повторное использование (Reusability)
- •Совместимость (Compatibility)
- •Эффективность (Efficiency)
- •Переносимость (Portability)
- •Простота использования (Easy of Use)
- •Функциональность (Functionality)
- •Своевременность (Timeliness)
- •Другие качества
- •Компромиссы
- •Ключевые вопросы
- •О программном сопровождении
- •Ключевые концепции
- •Лекция 2. Критерии объектной ориентации
- •О критериях
- •До какой степени мы должны быть догматичными?
- •Категории
- •Метод и язык
- •Бесшовность (seamlessness)
- •Классы
- •Утверждения (Assertions)
- •Классы как модули
- •Классы как типы
- •Вычисления, основанные на компонентах
- •Скрытие информации (information hiding)
- •Обработка исключений (Exception handling)
- •Статическая типизация (static typing)
- •Универсальность (genericity)
- •Единичное наследование (single inheritance)
- •Множественное наследование (Multiple inheritance)
- •Дублируемое наследование (Repeated inheritance)
- •Ограниченная универсальность (Constrained genericity)
- •Переопределение (redefinition)
- •Полиморфизм
- •Динамическое связывание
- •Выяснение типа объекта в период выполнения
- •Отложенные (deferred) свойства и классы
- •Управление памятью (memory management) и сборка мусора (garbage collection)
- •Реализация и среда
- •Автоматическое обновление (automatic update)
- •Быстрое обновление (fast update)
- •Живучесть (persistence)
- •Документация
- •Быстрый просмотр (browsing)
- •Библиотеки
- •Базовые библиотеки
- •Графика и пользовательские интерфейсы
- •Механизмы эволюции библиотек
- •Механизмы индексации в библиотеках
- •Продолжение просмотра
- •Библиографические ссылки и объектные ресурсы
- •Лекция 3. Модульность
- •Пять критериев
- •Декомпозиция
- •Модульная Композиция
- •Модульная Понятность
- •Модульная Непрерывность
- •Модульная Защищенность
- •Пять правил
- •Прямое отображение
- •Минимум интерфейсов
- •Слабая связность интерфейсов
- •Явные интерфейсы
- •Скрытие информации
- •Пять принципов
- •Лингвистические Модульные Единицы
- •Самодокументирование
- •Унифицированный Доступ
- •Открыт-Закрыт
- •Единственный Выбор
- •Ключевые концепции
- •Библиографические замечания
- •Упражнения
- •У3.1 Модульность в языках программирования
- •У3.2 Принцип Открыт-Закрыт (для программистов Lisp)
- •У3.3 Ограничения на скрытие информации
- •У3.4 Метрики для модульности (отчетная исследовательская работа)
- •У3.5 Модульность существующих систем
- •У3.6 Управление конфигурацией и наследование
- •Лекция 4. Подходы к повторному использованию
- •Цели повторного использования
- •Ожидаемые преимущества
- •Потребители и производители повторно используемых программ
- •Что следует повторно использовать?
- •Повторное использование персонала
- •Повторное использование проектов и спецификаций
- •Образцы проектов (design patterns)
- •Повторное использование исходного текста
- •Повторное использование абстрактных модулей
- •Повторяемость при разработке ПО
- •Нетехнические препятствия
- •Синдром NIH
- •Фирмы по разработке ПО и их стратегии
- •Организация доступа к компонентам
- •Несколько слов об индексировании компонентов
- •Форматы для распространения повторно используемых компонентов
- •Оценка
- •Техническая проблема
- •Изменения и постоянство
- •Повторно использовать или переделать? (The reuse-redo dilemma)
- •Пять требований к модульным структурам
- •Изменчивость Типов (Type Variation)
- •Группирование Подпрограмм (Routine Grouping)
- •Изменчивость Реализаций (Implementation Variation)
- •Независимость Представлений
- •Факторизация Общего Поведения
- •Традиционные модульные структуры
- •Подпрограммы
- •Пакеты
- •Пакеты: оценка
- •Перегрузка и универсальность
- •Синтаксическая перегрузка
- •Семантическая перегрузка (предварительное представление)
- •Универсальность (genericity)
- •Основные методы модульности: оценка
- •Ключевые концепции
- •Библиографические замечания
- •Ингредиенты вычисления
- •Функциональная декомпозиция
- •Декомпозиция, основанная на объектах
- •Объектно-ориентированное конструирование ПО
- •Вопросы
- •Ключевые концепции
- •Библиографические замечания
- •Лекция 5. К объектной технологии
- •Лекция 6. Абстрактные типы данных (АТД)
- •Критерии
- •Различные реализации
- •Представления стеков
- •Опасность излишней спецификации
- •Какова длина второго имени?
- •К абстрактному взгляду на объекты
- •Использование операций
- •Политика невмешательства в обществе модулей
- •Согласованность имен
- •Можно ли обойтись без абстракций?
- •Формализация спецификаций
- •Специфицирование типов
- •Универсализация (Genericity)
- •Перечисление функций
- •Категории функций
- •Раздел АКСИОМЫ
- •Две или три вещи, которые мы знаем о стеках
- •Частичные функции
- •Предусловия
- •Полная спецификация
- •Ничего кроме правды
- •От абстрактных типов данных к классам
- •Классы
- •Как создавать эффективный класс
- •Роль отложенных классов
- •Абстрактные типы данных и скрытие информации
- •Переход к более императивной точке зрения
- •Назад к тому, с чего начали?
- •Конструирование объектно-ориентированного ПО
- •За пределами программ
- •Дополнительные темы
- •Еще раз о неявности
- •Соотношение спецификации и проектирования
- •Соотношение классов и записей
- •Альтернативы частичным функциям
- •Полна ли моя спецификация?
- •Доказательство достаточной полноты
- •Ключевые концепции
- •Библиографические замечания
- •Упражнения
- •У6.1 Точки
- •У6.2 Боксеры
- •У6.3 Банковские счета
- •У6.4 Сообщения
- •У6.5 Имена
- •У6.6 Текст
- •У6.7 Покупка дома
- •У6.8 Дополнительные операции для стеков
- •У6.9 Ограниченные стеки
- •У6.10 Очереди
- •У6.11 Распределители
- •У6.12 Булевский -- BOOLEAN
- •У6.13 Достаточная полнота
- •У6.14 Непротиворечивость
- •Лекция 7. Статические структуры: классы
- •Классы, а не объекты - предмет обсуждения
- •Устранение традиционной путаницы
- •Роль классов
- •Модули и типы
- •Класс как модуль и как тип
- •Унифицированная система типов
- •Простой класс
- •Компоненты
- •Атрибуты и подпрограммы
- •Унифицированный доступ
- •Класс POINT
- •Основные соглашения
- •Распознавание вида компонент
- •Тело подпрограммы и комментарии к заголовку
- •Предложение indexing
- •Обозначение результата функции
- •Правила стиля
- •Наследование функциональных возможностей общего характера
- •Объектно-ориентированный стиль вычислений
- •Текущий экземпляр
- •Клиенты и поставщики
- •Вызов компонента
- •Принцип единственности цели
- •Слияние понятий модуль и тип
- •Роль объекта Current
- •Квалифицированные и неквалифицированные вызовы
- •Компоненты-операции
- •Селективный экспорт и скрытие информации
- •Неограниченный доступ
- •Ограничение доступа клиентам
- •Стиль объявления скрытых компонент
- •"Внутренний" экспорт
- •Собираем все вместе
- •Общая относительность
- •Большой Взрыв
- •Системы
- •Программа main отсутствует
- •Компоновка системы
- •Классическое "Hello"
- •Структура и порядок: программист в роли поджигателя
- •Обсуждение
- •Форма объявлений
- •Атрибуты или функции?
- •Экспорт атрибутов
- •Доступ клиентов к атрибутам
- •Оптимизация вызовов
- •Архитектурная роль селективного экспорта
- •Импорт листингов
- •Присваивание функции результата
- •Дополнение: точное определение сущности
- •Ключевые концепции
- •Библиографические замечания
- •Упражнения
- •У7.1 POINT как абстрактный тип данных
- •У7.2 Завершение реализации POINT
- •У7.3 Полярные координаты
- •Лекция 8. Динамические структуры: объекты
- •Объекты
- •Что такое объект?
- •Базовая форма
- •Простые поля
- •Простое представление книги - класс BOOK
- •Писатели
- •Ссылки
- •Идентичность объектов
- •Объявление ссылок
- •Ссылка на себя
- •Взгляд на структуру объектов периода выполнения
- •Объекты как средство моделирования
- •Четыре мира программной разработки
- •Реальность: "седьмая вода на киселе"
- •Работа с объектами и ссылками
- •Динамическое создание и повторное связывание
- •Инструкция создания
- •Общая картина
- •Для чего необходимо явное создание объектов?
- •Процедуры создания
- •Перекрытие инициализации по умолчанию
- •Статус экспорта процедур создания
- •Правила, применимые к процедурам создания
- •Процедуры создания и перегрузка
- •Еще о ссылках
- •Состояния ссылок
- •Вызовы и пустые ссылки
- •Операции над ссылками
- •Присоединение ссылки к объекту
- •Сравнение ссылок
- •Значение void
- •Клонирование и сравнение объектов
- •Копирование объектов
- •Глубокое клонирование и сравнение
- •Глубокое хранилище: первый взгляд на сохраняемость
- •Составные объекты и развернутые типы
- •Ссылок не достаточно
- •Развернутые типы
- •Роль развернутых типов
- •Агрегирование
- •Свойства развернутых типов
- •Недопустимость ссылок на подобъекты
- •Присоединение: две семантики - ссылок и значений
- •Присоединение
- •Присоединение: ссылочное и копии
- •Гибридное присоединение
- •Проверка эквивалентности
- •Работа со ссылками: преимущества и опасности
- •Динамические псевдонимы
- •Семантика использования псевдонимов
- •Выработка соглашений для динамических псевдонимов
- •Псевдонимы в ПО и за его пределами
- •Инкапсуляция действий со ссылками
- •Обсуждение
- •Графические соглашения
- •Ссылки и простые значения
- •Форма операций клонирования и эквивалентности
- •Статус универсальных операций
- •Ключевые концепции
- •Библиографические замечания
- •Упражнения
- •У8.1 Книги и авторы
- •У8.2 Личности
- •У8.3 Проектирование нотации
- •Лекция 9. Управление памятью
- •Что происходит с объектами
- •Создание объектов
- •Использование динамического режима
- •Повторное использование памяти в трех режимах
- •Отсоединение
- •Недостижимые объекты
- •Достижимые объекты в классическом подходе
- •Достижимые объекты в ОО-модели
- •Проблема управления памятью в ОО-модели
- •Три ответа
- •Несерьезный подход (тривиальный)
- •Может ли быть оправдан несерьезный подход?
- •Надо ли заботиться о памяти?
- •Байт здесь, байт там, и реальные покойники
- •Восстановление памяти: проблемы
- •Удаление объектов, управляемое программистом
- •Проблема надежности
- •Проблема простоты разработки
- •Подход на уровне компонентов
- •Управление памятью связного списка
- •Работа с утилизированными объектами
- •Дискуссия
- •Автоматическое управление памятью
- •Необходимость автоматических методов
- •Что в точности понимается под восстановлением?
- •Подсчет ссылок
- •Сборка мусора
- •Механизм сборки мусора
- •Основа сборки мусора
- •Сборка по принципу "все-или-ничего"
- •Продвинутый (Advanced) подход к сборке мусора
- •Алгоритмы параллельной сборки мусора
- •Практические проблемы сборки мусора
- •Класс MEMORY
- •Механизм освобождения
- •Сборка мусора и внешние вызовы
- •Среда с управлением памятью
- •Основы
- •Сложные проблемы
- •Перемещение объектов
- •Механизм сборки мусора
- •Повышенное чувство голода и потеря аппетита (Bulimia and anorexia)
- •Операции сборщика мусора
- •Ключевые концепции
- •Библиографические заметки
- •Упражнения
- •У9.1 Модели создания объектов
- •У9.2 Какой уровень утилизации?
- •У9.3 Совместное использование стека достижимых элементов
- •У9.4 Совместное использование
- •Лекция 10. Универсализация
- •Горизонтальное и вертикальное обобщение типа
- •Необходимость параметризованных классов
- •Родовые АТД
- •Проблема
- •Роль типизации
- •Родовые классы
- •Объявление родового класса
- •Использование родового класса
- •Терминология
- •Проверка типов
- •Правило типизации
- •Операции над сущностями родового типа
- •Типы и классы
- •Массивы
- •Массивы как объекты
- •Свойства массива
- •Размышления об эффективности
- •Синонимичная инфиксная операция
- •Стоимость универсализации
- •Обсуждение: что все-таки не сделано
- •Ключевые концепции
- •Библиографические замечания
- •Упражнения
- •У10.1 Ограниченная универсализация
- •У10.2 Двумерные массивы
- •У10.3 Использование своего формального родового параметра фактически как чужого
- •Лекция 11. Проектирование по контракту: построение надежного ПО
- •Базисные механизмы надежности
- •О корректности ПО
- •Выражение спецификаций
- •Формула корректности
- •Сильные и слабые условия
- •Введение утверждений в программные тексты
- •Предусловия и постусловия
- •Класс стек
- •Предусловия
- •Постусловия
- •Педагогическое замечание
- •Контракты и надежность ПО
- •Права и обязательства
- •Интуиция (Дзен) и искусство программной надежности: больше гарантий и меньше проверок
- •Утверждения не являются механизмом проверки вводимых данных
- •Утверждения это не управляющие структуры
- •Ошибки, дефекты и другие насекомые
- •Работа с утверждениями
- •Класс стек
- •Императив и аппликатив (применимость)
- •Замечание о пустоте структур
- •Проектирование предусловий: толерантное или требовательное?
- •Предусловия и статус экспорта
- •Толерантные модули
- •Инварианты класса
- •Определение и пример
- •Форма и свойства инвариантов класса
- •Инвариант в момент изменения
- •Кто должен обеспечить сохранность инвариантов
- •Роль инвариантов класса в программной инженерии
- •Инварианты и контракты
- •Когда класс корректен?
- •Корректность класса
- •Роль процедур создания
- •Ревизия массивов
- •Связывание с АТД
- •Не просто коллекция функций
- •Компоненты класса и АТД функции
- •Выражение аксиом
- •Функция абстракции
- •Инварианты реализации
- •Инструкция утверждения
- •Инварианты и варианты цикла
- •Трудности циклов
- •Сделаем циклы корректными
- •Ингредиенты доказательства корректности цикла
- •Синтаксис цикла
- •Использование утверждений
- •Утверждения как средство для написания корректного ПО
- •Использование утверждений для документирования: краткая форма класса
- •Мониторинг утверждений в период выполнения
- •Каков оптимальный уровень мониторинга?
- •Обсуждение
- •Нужен ли мониторинг в период выполнения?
- •Выразительная сила утверждений
- •Включение функций в утверждения
- •Инварианты класса и семантика ссылок
- •Что дальше
- •Ключевые концепции
- •Библиографические замечания
- •Упражнения
- •У11.1 Комплексные числа
- •У11.2 Класс и его АТД
- •У11.3 Полные утверждения для стеков
- •У11.4 Экспортирование размера
- •У11.5 Инвариант реализации
- •У11.6 Утверждения и экспорт
- •У11.7 Поиск жучков (bugs)
- •У11.8 Нарушение инварианта
- •У11.9 Генерация случайных чисел
- •У11.10 Модуль "очередь"
- •У11.11 Модуль "множество"
- •Постскриптум: Катастрофа Ариан 5
- •Лекция 12. Когда контракт нарушается: обработка исключений
- •Базисные концепции обработки исключений
- •Отказы
- •Исключения
- •Источники исключений
- •Ситуации отказа
- •Обработка исключений
- •Как не следует делать это - C-Unix пример
- •Как не следует делать это - Ada пример
- •Принципы обработки исключений
- •Цепочка вызовов
- •Механизм исключений
- •Спаси и Повтори (Rescue и Retry)
- •Как отказаться сразу
- •Таблица истории исключений
- •Примеры обработки исключений
- •Поломки при вводе
- •Восстановление при исключениях, сгенерированных операционной системой
- •Повторение программы, толерантной к неисправностям
- •N-версионное программирование
- •Задача предложения rescue
- •Корректность предложения rescue
- •Четкое разделение ролей
- •Когда нет предложения rescue
- •Продвинутая обработка исключений
- •Запросы при работе с классом EXCEPTIONS
- •Какой должна быть степень контроля?
- •Исключения разработчика
- •Обсуждение
- •Дисциплинированные исключения
- •Должны ли исключения быть объектами?
- •Методологическая перспектива
- •Ключевые концепции
- •Библиографические замечания
- •Упражнения
- •У12.1 Наибольшее целое
- •У12.2 Объект Exception
- •Лекция 13. Поддерживающие механизмы
- •Взаимодействие с не объектным ПО
- •Внешние программы
- •Улучшенные варианты
- •Использование внешних программ
- •ОО-изменение архитектуры (re-architecturing)
- •Вопрос совместимости: гибридный программный продукт или гибридные языки?
- •Передача аргументов
- •Инструкции
- •Вызов процедуры
- •Присваивание (Assignment)
- •Создание (Creation)
- •Условная Инструкция (Conditional)
- •Множественный выбор
- •Циклы
- •Проверка
- •Отладка
- •Повторение вычислений
- •Выражения
- •Манифестные константы
- •Вызовы функций
- •Текущий объект
- •Выражения с операторами
- •Нестрогие булевы операторы
- •Строки
- •Ввод и вывод
- •Лексические соглашения
- •Ключевые концепции
- •Упражнения
- •У13.1 Внешние классы
- •У13.2 Избегая нестрогих операторов
- •Лекция 14. Введение в наследование
- •Многоугольники и прямоугольники
- •Многоугольники
- •Прямоугольники
- •Основные соглашения и терминология
- •Наследование инварианта
- •Наследование и конструкторы
- •Пример иерархии
- •Полиморфизм
- •Полиморфное присоединение
- •Что на самом деле происходит при полиморфном присоединении?
- •Полиморфные структуры данных
- •Типизация при наследовании
- •Согласованность типов
- •Пределы полиморфизма
- •Экземпляры
- •Статический тип, динамический тип
- •Обоснованы ли ограничения?
- •Может ли быть польза от неведения?
- •Когда хочется задать тип принудительно
- •Полиморфное создание
- •Динамическое связывание
- •Использование правильного варианта
- •Переопределение и утверждения
- •О реализации динамического связывания
- •Отложенные компоненты и классы
- •Движения произвольных фигур
- •Отложенный компонент
- •Эффективизация компонента
- •Отложенные классы
- •Соглашения о графических обозначениях
- •Что делать с отложенными классами?
- •Задание семантики отложенных компонентов и классов
- •Способы изменения объявлений
- •Повторное объявление функции как атрибута
- •Обратного пути нет
- •Использование исходной версии при переопределении
- •Смысл наследования
- •Двойственная перспектива
- •Взгляд на класс как на модуль
- •Взгляд на класс как на тип
- •Наследование и децентрализация
- •Независимость от представления
- •Парадокс расширения-специализации
- •Роль отложенных классов
- •Назад к абстрактным типам данных
- •Отложенные классы как частичные интерпретации: классы поведения
- •Не вызывайте нас, мы вызовем вас
- •Программы с дырами
- •Роль отложенных классов при анализе и глобальном проектировании
- •Обсуждение
- •Явное переопределение
- •Доступ к предшественнику процедуры
- •Динамическое связывание и эффективность
- •Оценка накладных расходов
- •Статическое связывание как оптимизация
- •Кнопка под другим именем: когда статическое связывание ошибочно
- •Подход языка С++ к связыванию
- •Ключевые концепции
- •Библиографические замечания
- •Упражнения
- •У14.1 Многоугольники и прямоугольники
- •У14.2 Многоугольник с малым числом вершин
- •У14.3 Геометрические объекты с двумя координатами
- •У14.4 Наследование без классов
- •У14.5 Классы без объектов
- •У14.6 Отложенные классы и прототип
- •У14.7 Библиотека поиска в таблицах (семестровый проект)
- •У14.8 Виды отложенных компонентов
- •У14.9 Комплексные числа
- •Лекция 15. Множественное наследование
- •Примеры множественного наследования
- •Пример, неподходящий для введения
- •Может ли самолет быть имуществом?
- •Числовые и сравнимые значения
- •Окна - это деревья и прямоугольники
- •Деревья - это списки и их элементы
- •Составные фигуры
- •Брак по расчету
- •Структурное наследование
- •Наследование функциональных возможностей
- •Лунка и кнопка
- •Оценка
- •Переименование компонентов
- •Конфликт имен
- •Результат переименования
- •Смена имен и переопределение
- •Подбор локальных имен
- •Играем в имена
- •Использование родительской процедуры создания
- •Плоские структуры
- •Плоская форма класса
- •Применение плоской формы
- •Краткая плоская форма
- •Дублируемое наследование
- •Общие предки
- •По обе стороны океана
- •Совместное использование и репликация
- •Ненавязчивое дублирующее наследование
- •Правило переименования
- •Конфликт переопределений
- •Конфликт при совместном использовании: отмена определения и соединение компонентов
- •Конфликты при репликации: выделение
- •Выделение всех компонентов
- •Сохранение исходной версии при переопределении
- •Пример повышенной сложности
- •Дублируемое наследование и универсальность
- •Правила об именах
- •Обсуждение
- •Переименование
- •ОО-разработка и перегрузка
- •Ключевые концепции
- •Библиографические замечания
- •Упражнения
- •У15.1 Окна как деревья
- •У15.2 Является ли окно строкой?
- •У15.3 Завершение строительства
- •У15.4 Итераторы фигур
- •У15.5 Связанные стеки
- •У15.6 Кольцевые списки и цепи
- •У15.7 Деревья
- •У15.8 Каскадные или "шагающие" (walking) меню
- •У15.9 Плоский precursor (предшественник)
- •У15.10 Дублируемое наследование и репликация
- •Лекция 16. Техника наследования
- •Наследование и утверждения
- •Инварианты
- •Предусловия и постусловия при наличии динамического связывания
- •Как обмануть клиентов
- •Как быть честным
- •Пример
- •Устранение посредника
- •Субподряды
- •Абстрактные предусловия
- •Правило языка
- •Повторное объявление функции как атрибута
- •Замечание математического характера
- •Глобальная структура наследования
- •Универсальные классы
- •Нижняя часть иерархии
- •Универсальные компоненты
- •Замороженные компоненты
- •Запрет повторного объявления
- •Фиксированная семантика компонентов copy, clone и equality
- •Не злоупотребляйте замораживанием
- •Ограниченная универсальность
- •Вектора, допускающие сложение
- •Не ОО-подход
- •Ограничение родового параметра
- •Игра в рекурсию
- •Попытка присваивания
- •Когда правила типов становятся несносными
- •Проблема
- •Механизм решения
- •Правильное использование попытки присваивания
- •Типизация и повторное объявление
- •Устройства и принтеры
- •Одно- и двусвязные элементы
- •Правило повторного объявления типов
- •Закрепленные объявления
- •Несогласованность типов
- •Примеры из практики
- •Серьезное затруднение
- •Понятие опорного элемента
- •Опорный элемент Current
- •Еще раз о базовых классах
- •Правила о закрепленных типах
- •Когда не используются закрепленные объявления
- •Статический механизм
- •Наследование и скрытие информации
- •Кое-что о политике
- •Применение
- •Зачем нужна такая гибкость?
- •Интерфейс и повторное использование реализаций
- •Слово в защиту реализаций
- •Два стиля
- •Выборочный экспорт
- •Ключевые концепции
- •Библиографические замечания
- •Упражнения
- •У16.1 Наследование: простота и эффективность
- •У16.2 Векторы
- •У16.3 Экстракт?
- •Лекция 17. Типизация
- •Проблема типизации
- •Базисная конструкция
- •Статическая и динамическая типизация
- •Правила типизации
- •Реализм
- •Пессимизм
- •Статическая типизация: как и почему
- •Преимущества
- •Аргументы в пользу динамической типизации
- •Типизация: слагаемые успеха
- •"Типизирована ли кроха"?
- •Типизация и связывание
- •Ковариантность и скрытие потомком
- •Ковариантность
- •Параллельные иерархии
- •Своенравие полиморфизма
- •Скрытие потомком
- •Корректность систем и классов
- •Практический аспект
- •Корректность систем: первое приближение
- •Контравариантность и безвариантность
- •Использование родовых параметров
- •Типовые переменные
- •Полагаясь на закрепление типов
- •Глобальный анализ
- •Остерегайтесь полиморфных кэтколлов!
- •Назад, в Ялту
- •Одно правило и несколько определений
- •Оценка
- •Полное соответствие
- •Ключевые концепции
- •Библиографические замечания
- •Лекция 18. Глобальные объекты и константы
- •Константы базовых типов
- •Атрибуты-константы
- •Использование констант
- •Константы пользовательских классов
- •Константы с манифестом для этого непригодны
- •Однократные функции
- •Применение однократных подпрограмм
- •Разделяемые объекты
- •Однократные функции с результатами базовых типов
- •Однократные процедуры
- •Параметры
- •Однократные функции, закрепление и универсальность
- •Константы строковых типов
- •Unique-значения
- •Обсуждение
- •Инициализация: подходы языков программирования
- •Строковые константы
- •Unique-значения и перечислимые типы
- •Ключевые концепции
- •Библиографические замечания
- •Упражнения
- •У18.1 Эмуляция перечислимых типов однократными функциями
- •У18.2 Однократные функции для эмуляции unique-значений
- •У18.3 Однократные функции в родовых классах
- •У18.4 Однократные атрибуты?
- •Примечания
- •1.1. Хансена
- •1.2. Computer World
- •3.1. метода
- •3.2. именованные константы
- •3.3. Метод, требующий от каждого модуля, вводящего данные, проверку их достоверности, пригоден для реализации модульной защищенности
- •4.1. книга
- •4.3. таблице
- •10.1. Как рассматривать наследование и параметризацию, как соперников или как соратников, когда целью является построение более гибкого ПО
- •10.2. прозрачной
- •10.3. Рассмотрим первый набросок этого класса
- •10.4. инфиксную операцию
- •11.1. общий класс, описывающий стеки
- •11.2. Для инвариантов ответ такой же, как и для постусловий
- •12.1. учебника
- •13.1. процедура
- •13.2. Эта техника будет обсуждаться вместе с вопросом побочных эффектов в разделе принципов модульного проектирования
- •13.3. предыдущих лекциях
- •13.4. анализ символа, введенного пользователем
Пять принципов
Из предыдущих правил и, косвенным образом, из критериев следуют пять принципов конструирования ПО:
*Принцип Лингвистических Модульных Единиц (Linguistic Modular Units).
*Принцип Самодокументирования (Self-Documentation).
*Принцип Унифицированного Доступа (Uniform Access).
*Принцип Открыт-Закрыт (Open-Closed).
*Принцип Единственного выбора (Single Choice).
Лингвистические Модульные Единицы
Принцип Лингвистических Модульных Единиц утверждает, что формализм описания ПО на различных уровнях (спецификации, проектирования, реализации) должен поддерживать модульность:
Принцип Лингвистических Модульных Единиц Модули должны соответствовать синтаксическим единицам используемого языка.
Упомянутым выше языком может быть язык программирования, язык проектирования, язык оформления технических требований и т. д. В случае языка программирования модули должны независимо компилироваться.
Этот принцип на любом уровне (анализа, проектирования, реализации) не допускает объединения метода, исходящего из концепции модульности, и языка, не содержащего соответствующих модульных конструкций. В самом деле, нередко встречаются фирмы, которые на этапе проектирования применяют некие методологические подходы, например используя модули языка Ada, но затем реализуют свои замыслы в таком языке программирования, как Pascal или C, не поддерживающим эти подходы. Такой подход нарушает некоторые из критериев модульности:
*Непрерывность: если границы модуля в окончательном тексте программы не соответствуют логической декомпозиции спецификации или проекта, то при сопровождении системы и ее эволюции будет затруднительно или даже невозможно поддерживать совместимость различных уровней. Изменение спецификации можно считать небольшим, если оно затрагивает спецификацию лишь небольшого числа модулей. Для обеспечения "непрерывности" должно иметь место прямое соответствие между спецификацией, проектом и модулями реализации.
*Прямое отображение: необходимо поддерживать явное соответствие между структурой модели и структурой решения. Для этого необходимо иметь явную синтаксическую идентификацию концептуальных единиц модели и решения, отражающее разбиение, предусмотренное методом разработки.
*Декомпозиция: для разбиения системы на отдельные задачи необходимо быть уверенным, что результатом решения каждой из задач явится четко ограниченная синтаксическая единица; на этапе реализации эти программные компоненты должны быть раздельно компилируемыми.
*Композиция: что же, кроме модулей с однозначно определенными синтаксическими границами, можно объединять между собой?
*Защищенность: лишь в случае, если модули синтаксически разграничены, можно надеяться на возможность контроля области действия ошибок.
Самодокументирование
Подобно правилу Скрытия Информации, принцип Самодокументирования определяет, как следует документировать модули:
Принцип Самодокументирования Разработчик модуля должен стремиться к тому, чтобы вся информация о модуле
содержалась в самом модуле.
Обычно реализации этого принципа мешает общепринятое положение, согласно которому информацию о модуле помещают в отдельные проектные документы.
|Документация, рассматриваемая здесь, является внутренней документацией о компонентах ПО. Пользовательская документация о выпущенном программном продукте может быть отдельным документом, реализованном в виде печатного текста, либо размещенном на CD-ROM или страницах в Интернете. Как отмечалось при обсуждении вопроса о качестве программного обеспечения, следствием общего принципа самодокументирования является наблюдаемая сейчас тенденция к большему использованию средств диалоговой оперативной подсказки. (См."О документировании" лекция 1) |
Наиболее очевидным обоснованием необходимости принципа Самодокументирования является критерий модульной понятности. По-видимому, однако, более важным является то, что этот принцип помогает реализации критерия непрерывности. Если программное обеспечение и документацию к нему рассматривать как отдельные объекты, то трудно гарантировать, что они будут оставаться совместимыми - будут синхронно изменяться при всех изменениях системы. Однако если хранить все в одном месте, то это, хотя и не дает полную гарантию, но все же поможет поддерживать совместимость.
Этот принцип, безобидный на первый взгляд, противоречит многому из того, что обычно рекомендуется к практическому применению в литературе по разработке ПО. Преобладает мнение, что разработчик ПО - инженер-программист - должен делать то, чем, по-видимому, обязаны заниматься остальные инженеры: производить килограмм бумаги на каждый грамм фактически создаваемой продукции. Предложение вести запись процесса разработки ПО является неплохим советом, но из этого вовсе не следует, что программа и документация к ней являются разными продуктами.
Такой подход игнорирует характерное свойство ПО, которое здесь неоднократно обсуждается: возможность его изменения. Если рассматривать программу и документацию к ней как два разных продукта, то вскоре можно оказаться в ситуации, когда в документации утверждается одно, а программа делает нечто иное. А ведь наличие неправильной документации намного хуже, чем ее отсутствие.
|Главным достижением последних нескольких лет явилось появление стандартов качества ПО. Разработаны сертификаты ISO, стандарт "2167" и его преемники, Модель Полноты Потенциала (Capability Maturity Model), предложенная Институтом программной инженерии (Software Engineering Institute). Но поскольку они брали начало из моделей, используемых в других отраслях знания, они наделены обширным "хвостом" бумажной документации. Некоторые из этих стандартов могли бы оказать значительно большее влияние на качество ПО, (помимо того, что они дают администраторам программного продукта средство для оправданий в случае последующих эксплуатационных неполадок) если бы они включали принцип
Самодокументирования. |
В этом курсе следствием принципа Самодокументирования является метод документирования классов - модулей при ОО-конструировании ПО, предусматривающий включение документации в сам модуль. Это вовсе не означает, что сам модуль является своей документацией: текст программы обычно содержит слишком много подробностей (это и явилось доводом в пользу скрытия информации). Просто модуль должен содержать свою документацию. (См. "Использование утверждений класса (assertions) для документирования" в лекции 11. См. также лекция 5 курса "Основы объектно-ориентированного проектирования" и последние два упражнения в ней.)
При таком подходе ПО превращается в единственный программный продукт, обеспечивающий его различные представления или облики (views). Один облик, пригодный для компиляции и выполнения, - полный исходный текст модуля. Другой - документация, задающая абстрактный интерфейс модуля, позволяющий разработчикам программного обеспечения создавать модули-клиенты, не знакомясь с содержанием исходного модуля, что соответствует правилу Скрытия Информации. Возможны и другие представления.
Унифицированный Доступ
Хотя вначале может показаться, что принцип Унифицированного Доступа направлен лишь на решение проблем, связанных с принятой нотацией, в действительности он задает правило проектирования, влияющее на многие аспекты ОО-разработки ПО. Принцип следует из критерия Непрерывности; его можно рассматривать и как частный случай правила Скрытия
Информации3.6)
Пусть x - имя, используемое для доступа к некоторому элементу данных, который в последующем будем называть объектом. Пусть f - имя компонента (feature), применимого к x. Под компонентом понимается некоторая операция; далее этот термин будет определен подробнее. Например, x может быть переменной, представляющей счет в банке, а f - компонент, задающий текущий баланс этого счета (account's current balance). Унифицированный Доступ направлен на решение вопроса о том, какой должна быть нотация, задающая применение f к x, не содержащая каких-либо преждевременных обязательств по способу реализации f.
Во многих языках проектирования и программирования выражение, описывающее применение f к x, зависит от реализации f, выбранной разработчиком. Это может быть свойство, хранимое вместе с x, или метод, вызываемый всякий раз, когда это требуется. В примере с банковскими счетами и остатками на счетах возможно использование обоих подходов:
*A1 Можно представить баланс банковского счета в виде одного из полей записи, описывающей каждый счет. При использовании такого подхода каждая банковская операция, изменяющая баланс, должна предусматривать корректировку соответствующего поля.
*A2 Можно определить функцию, вычисляющую баланс на основании других полей этой записи, например полей, представляющих списки денежных сумм, снятых со счета и внесенных на счет. При использовании такого подхода значение баланса не сохраняется, а вычисляется по запросу.
В общепринятой нотации таких языков, как Pascal, Ada, C, C++ и Java используется
обозначение x.f для случая A1 и f(x) для случая A2.
Рис. 3.11. Два представления банковского счета
Выбор между представлениями A1 и A2 это компромисс между "памятью и временем": первое экономит на вычислениях, а второе - на памяти. Решение о выборе одного из вариантов является типичным примером решения, изменяемого разработчиком, по крайней мере один раз за время существования проекта. Поэтому с целью поддержания непрерывности желательно иметь нотацию для доступа к компоненту, не зависящую от выбора одного из двух представлений. Если способ реализации x 'ов на некотором этапе разработки проекта будет изменен, то это не потребует изменений в модулях, использующих вызов f.
Мы рассмотрели пример принципа Унифицированного Доступа. В общем виде принцип можно сформулировать так:
Принцип Унифицированного Доступа Все службы, предоставляемые модулем, должны быть доступны в унифицированной
нотации, которая не подведет вне зависимости от реализации, использующей память или вычисления.
Этому принципу удовлетворяют немногие языки. Старейшим из них был Algol W, в котором как вызов функции, так и доступ к полю записывались в виде a(x). Первым из ОО-языков, удовлетворяющих Принципу Унифицированного Доступа, был язык Simula 67, использовавший обозначение x.f в обоих случаях. Нотация, предлагаемая в лекциях 7-18 этого курса, будет поддерживать такое соглашение.
Открыт-Закрыт
Любой метод модульной декомпозиции должен удовлетворять принципу семафора: Открыт-Закрыт:
Принцип Открыт-Закрыт Модули должны иметь возможность быть как открытыми, так и закрытыми.
Противоречие является лишь кажущимся, поскольку термины соответствуют разным целевым установкам:
*Модуль называют открытым, если он еще доступен для расширения. Например, имеется возможность расширить множество операций в нем или добавить поля к его структурам данных.
*Модуль называют закрытым, если он доступен для использования другими модулями. Это
означает, что модуль (его интерфейс - с точки зрения скрытия информации) уже имеет строго определенное окончательное описание. На уровне реализации закрытое состояние модуля означает, что модуль можно компилировать, сохранять в библиотеке и делать его доступным для использования другими модулями (его клиентами ). На этапе проектирования или спецификации закрытие модуля означает, что он одобрен руководством, внесен в официальный репозиторий утвержденных программных элементов проекта - базу проекта (project baseline ), и его интерфейс опубликован в интересах авторов других модулей.
Необходимость закрывать модули и необходимость оставлять их открытыми вызываются разными причинами. Для разработчиков ПО естественным состоянием модуля является его открытость, поскольку почти невозможно заранее предусмотреть все элементы - данные, операции - которые могут потребоваться в процессе создания модуля. Поэтому разработчики стараются сохранять гибкость ПО, допускающую последующие изменения и дополнения. Но необходимо, особенно с точки зрения руководителя проекта, закрывать модули. В системе, состоящей из многих модулей, большинство модулей зависимы. Например, модуль интерфейса пользователя может зависеть от модуля синтаксического разбора (parsing module) - синтаксического анализатора и от модуля графики. Синтаксический анализатор может зависеть от модуля лексического анализа, и так далее. Если не закрывать модуль до тех пор, пока не будет уверенности, что он уже содержит все необходимые компоненты, то невозможно будет завершить разработку многомодульной программы: каждый из разработчиков будет вынужден ожидать, когда же завершат свою работу все остальные.
При использовании традиционной методики, две рассмотренные целевые установки оказываются несовместимыми. Либо модуль остается открытым, что не позволяет пользоваться им всем остальным, либо он закрывается, и тогда любое изменение или дополнение может дать начало неприятной цепной реакции трудоемких изменений во многих других модулях, непосредственно или косвенно зависящих от этого исходного модуля.
Два рисунка, приведенные ниже, иллюстрируют ситуацию, в которой трудно согласовать потребности в открытых и закрытых состояниях модуля. На первом рисунке модуль A используется модулями-клиентами B, С, D, которые сами могут иметь своих клиентов - E, F и так далее.
Рис. 3.12. Модуль А и его клиенты
В процессе течения времени ситуация изменяется и появляются новые клиенты - F и другие, которым требуется расширенная или приспособленная к новым условиям версия модуля A, которую можно назвать A':
Рис. 3.13. Старые и новые клиенты При использовании не ОО-методов, возможны лишь два решения этой проблемы, в равной
степени неудовлетворительные:
*N1 Можно переделать модуль A так, чтобы он обеспечивал расширенную или видоизмененную функциональность, требуемую новым клиентам.
*N2 Можно сохранить A в прежнем виде, сделать его копию, изменить имя копии модуля
на A', и выполнить все необходимые переделки в новом модуле. При таком подходе новый модуль A' никак не будет связан со старым модулем A.
Возможные катастрофические последствия решения N1 очевидны. Модуль A мог использоваться длительное время и иметь многих клиентов, таких как B, С и D. Переделки, необходимые для удовлетворения потребностей новых клиентов, могут нарушить предположения, на основе которых старые клиенты использовали модуль A ; в этом случае изменения в A могут "запустить" катастрофическую цепочку изменений у клиентов, у клиентов этих клиентов, и так далее. Для руководителя проекта это будет настоящим кошмаром: внезапно целые части ПО, считавшегося давным-давно завершенным и сданным в эксплуатацию, окажутся заново открытыми, что "запустит" новый цикл разработки, тестирования, отладки и документирования. Многие ли из руководителей проектов ПО захотят видеть себя в роли Сизифа - быть приговоренными вечно катить камень на вершину горы лишь для того, чтобы видеть, как он всякий раз вновь скатывается вниз - и все из-за проблем, вызванных необходимостью заново открывать ранее закрытые модули.
На первый взгляд решение N2 кажется лучшим: оно позволяет избежать синдрома Сизифа, поскольку не требует модификации уже существующих программных средств (показанных в верхней части последнего рисунка). Но в действительности, это решение может иметь еще худшие последствия, поскольку оно лишь отодвигает час расплаты. Экстраполируем воздействие этого решения на множество модулей, - потребуется множество модификаций, занимающих длительное время. В конечном счете, последствия оказываются ужасными: бурный рост числа вариантов исходных модулей, многие из которых очень похожи, хотя и не вполне идентичны.
Для многих организаций по разработке ПО такое изобилие модулей, не согласующееся с количеством выполняемых функций (многие из вариантов, кажущихся различными, оказываются, по существу, клонами), создает серьезную проблему управления конфигурацией ПО. И эту проблему обычно пытаются преодолеть путем использования сложных инструментальных средств. Полезные сами по себе, эти инструментальные средства пытаются "лечить" программу в ситуациях, когда предпочтительней было бы первое из рассмотренных
решений. Ведь лучше избежать избыточности, чем создавать ее.
|Несомненно, управление конфигурацией окажется полезным, но лишь в случае, если удастся найти модули, нуждающиеся в повторном открытии после возникших изменений, и в то же время избежать повторной компиляции модулей, не нуждающихся в этом. (В упражнении У3.6 предлагается выяснить, какова будет необходимость управления конфигурацией в объектно-ориентированной среде программирования.) |
Но как можно получить модули, которые были бы одновременно и открытыми и закрытыми? Можно ли сохранить неизмененным модуль A и всех его клиентов в верхней части рисунка, и в то же время предоставить модуль A' клиентам в нижней части, избегая дублирования программных средств? Благодаря механизму наследования (inheritance), ООподход обеспечивает особенно изящный вклад в решение этой проблемы.
Механизм наследования подробно рассматривается в последующих лекциях, а здесь дается лишь общее представление об этом. Для разрешения дилеммы, - изменять или повторно выполнять - наследование позволяет определить новый модуль A' на основе существующего модуля A, констатируя лишь различия между ними. Опишем A' как
class A' inherit
A
redefine f, g, ... end
feature
f is ...
g is ...
...
u is ...
...
end
где предложение feature содержит как определение новых компонент, характерных для A', например u, так и переопределение тех компонент (таких как f, g,:), представление которых в A' отличается от того, которое они имели в A.
Для графической иллюстрации наследования используется стрелка от "наследника" (heir) (нового класса A' ) к "родителю" (parent) (классу A ):