Конспект: МиСПРИСиТ


Общее

Литература


Некоторое ПО

Свободное/бесплатное & не IDE.


1 — 15.09

Введение

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

CAD == computer-aided design == проектирование с помощью компьютера.

CAM == computer-aided modelling == моделирование с помощью компьютера.

CASE == computer-aided software engineering == разработка ПО с помощью компьютера.

Средства CASE:

О популярности визуальных средств моделирования (UML и т.п.): “bubbles don’t crash” (B. Meyer).

Уровни архитектуры информационной системы (последовательность “снизу-вверх”):

  1. Аппаратное обеспечение (hardware).
  2. Программное обеспечение (software).
  3. Архитектура данных (БД).
  4. Архитектура доступа к данным.
  5. Бизнес-процессы.

Их можно разделить на платформу (то, что используется, нижние уровни) и приложение (то, что разрабатывается).

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

Объектная декомпозиция

Дополнено.

“Масштаб” программ традиционно оценивается в количестве строк (LOC — lines of code, kLOC — тысяча строк). Данный показатель имеет практический смысл при фиксированном языке, наборе библиотек и стиле. Сверхмалые программы: до 500 строк.

Объектно-ориентированное программирование возникло как средство описания задач имитационного моделирования (в частности, средство имитационного моделирования — интерпретируемый язык Симула — стало основой ОО-черт ряда языков программирования, в первую очередь, C++). Затем стало популярно как “парадигма программирования” широкого спектра применения. Однако, не всегда уместно обращаться к ООП, особенно в случае небольших программ. Данный факт затрудняет изучение ООП на разумных примерах, т.к. примеры должны быть достаточно велики.

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


Основа ООП — понятие объекта. Объект — единица объектной декомпозиции.

Декомпозиция — разбиение задачи или программы на части.

Декомпозиция может быть “пространственной” и “временной”.

“Временная” декомпозиция предполагает разбиение процесса решения задачи на подзадачи и процесса работы программы на последовательность действий (решений подзадач). Каждой подзадаче/действию в программе отвечает “процедура”, поэтому такую декомпозицию называют процедурной.

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

“Пространственная декомпозиция” предполагает разбиение состояния программы (данных) на отдельные элементы.

Как правило, временная и пространственная декомпозиции сосуществуют в разных пропорциях и оформлении. Например, процедурная декомпозиция предполагает разбиение данных на переменные, включая локальные переменные процедур. Дальнейшее её развитие — модульная декомпозиция, выделение “сильно-связных” компонент (групп переменных, процедур и, возможно, пользовательских типов данных) в виде модулей, скрывающих часть состояния от других модулей (инкапсуляция состояния).

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

Объектная декомпозиция предполагает разделение состояния программы (данных), точнее “ответственности за состояние/данные”, между “объектами”. С технической точки зрения объекты очень похожи на модули — у них есть внешний интерфейс и внутренняя реализация, включающая данные, принадлежащие объекту, которые могут быть недоступны извне объекта (инкапсуляция). Отличие заключается в двух моментах: 1) объекты в программе часто являются отражением объектов в модели (и в реальном мире); 2) может быть множество объектов одной структуры (однотипных), может иметь смысл создание и уничтожение объектов во время работы программы, соответственно динамике данных и моделируемым процессам.

Объект “сам” управляет своим состоянием — данными программы (решаемой задачи), находящимися в его зоне ответственности.


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


Родо-видовые отношения

Набор однотипных объектов выделяется в вид объектов, называемый в программировании классом. С точки зрения синтаксиса класс — тип данных (где данные — объекты класса), или множество всех возможных объектов одного (==этого) вида. С прагматической точки зрения класс — “чертёж” объекта. Главное, что определяет класс — как создаются и уничтожаются объекты (элементы жизненного цикла объектов), какие данные они содержат (атрибуты == “свойства”), какие действия с ними можно выполнять (методы).

Дальнейшее абстрагирование приводит нас к понятию рода объектов. Род задаёт некоторые свойства, которым должны удовлетворять объекты (или виды объектов). Отличие рода от вида легко пояснить на примере из реального мира: если конкретный стол — “объект”, “стол” — “вид”/“класс”, то “предмет мебели” — “род”. Не может существовать просто “предмет мебели”, он должен быть какого-либо вида.

В C++ понятие рода нашло отражение в виде концепции абстрактного класса, т.е. класса, определяющего не все заявленные действия, объекты которого нельзя создать. Однако, можно сказать, что некий другой класс, являются подтипом данного абстрактного класса (видом данного рода), реализует недостающие части и уже может служить рабочим “чертежом” для создания объектов.

Более “новые” языки программирования, такие как Java и C#, основывающиеся на объектной парадигме, проводят более чёткую черту, позволяющую отделить “виды” от “родов”, выделяя понятие интерфейса — описания набора действий, которые должен реализовывать (посредством соответствующего описания класса) любой объект, удовлетворяющий данному интерфейсу.

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


2 — 22.09

Домен (domain) — предметная область, моделируемая часть реального (или воображаемого) мира.

Доменный объект (domain object, business object) — объект в программе, моделирующий объект в домене.

DTO (data transfer object) — объект, предназначенный для хранения или передачи данных. Если DTO имеет методы, то их роль сводится к обеспечению доступа к хранимым данным.

Объект-значение (value object) — значение алгебраического типа данных, который реализован средствами ОО-языка (операции через методы и т.п.). В ОО-языках объекты-значения могут выделяться с помощью специальных средств (например, struct в C#) и обладать особой семантикой (в C# размещение на стеке и передача по значению вместо размещения в управляемой куче и передачи по ссылке).

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

Исторически выделялось четыре “принципа” ООП: единственность, инкапсуляция, наследование и полиморфизм. Инкапсуляция была рассмотрена выше.

Идентичность, уникальность или единственность (identity) — свойство (доменных) объектов. Каждый объект “уникален” и, строго говоря, “равен” только самому себе. Технически это выражается в том факте, что каждый объект в памяти имеет уникальный адрес.

Дополнено.

Значения (в противопоставлении объектам) в абстрактном смысле существуют независимо от своих представлений, например, число 3 не имеет место во времени и пространстве само по себе, однако может быть представлено состояниями физических объектов. Одно значение может иметь множество представлений в памяти компьютера, которые равны друг другу. Поэтому для “значений” (“объектов значений”) имеет смысл операция сравнения на равенство. Обычно равными считаются взаимозаменяемые значения. Операция копирование создаёт из одного представления новое представление, равное старому, т.е. представляющее то же самое значение. Обращение данного утверждения неверно: равные представления могут не быть копиями друг друга. Пример: рациональные числа, представленные парами целых: 1/2 == 2/4, но 2/4 и 1/2 не являются копиями друг друга.

Доменный объект является отражением некоего объекта в домене и, таким образом, является уникальным представлением самого себя.


SOLID — набор принципов разработки ОО-программ, предложенный Р.Мартином. Название является аббревиатурой первых слов названий включенных в этот набор принципов:

to be continued


Проблема “эллипс-круг”.

схема с множественным наследованием (“копир”), замена наследования на композицию повторное использование vs наследование реализация интерфейса vs наследование

SIP (separated interface principle) в C++ Std.Iostreams.

UML: диаграмма классов, отношения между классами (наследование, композиция, агрегация, использование, ассоциация)

Задача: составление расписания занятий, “ядро” –API– интерфейс

Расписание
Расписание


3 — 29.09

KISS (keep it simple and(,) stupid)

компоненты-чёрные ящики, развитие модульной декомпозиции в выделение отдельно собираемых библиотек (ABI, DLL), надстройка над другими программами или сервисами (web-API, REST, RPC)

to schedule

“декомпозиция” коллектива: введение иерархии уменьшает затраты на коммуникацию

отделение интерпретатора от “конфигурации”, скрипты, DSL

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

присваивание и прочие побочные эффекты –> транзакции, STM/HTM

процесс “сверху вниз” (анализ) и “снизу вверх” (синтез)

уровни: матричные операции, геометрические операции, множества достижимости, игровые задачи

Software

Целеполагание

— нужно ли адаптироваться к изменению/уточнению требований по ходу разработки (основной смысл методологии — если нужно?).


4 — 06.10

Проблемы процесса разработки

  1. Низкая культура разработки — следствие недостаточного опыта/навыков или давления жёстких сроков. Приводит к накоплению технического долга (technical debt) — неудачных решений, которые либо будут вызывать потери рабочего времени в будущем (в случае доработки/поддержки продукта), либо приведут к необходимости радикальной переработки с выбрасыванием большей части сделанного. Пример: “write-only code” — код, который невозможно читать (порой, даже автор не может спустя некоторое время понять, что он написал), такой код обычно дешевле и проще переписать с нуля, чем разобраться в нём.
  2. Гниение кода (code rotting) — постепенное рассогласование кода, который используется (и обновляется), и кода, который не используется (и обычно не обновляется). В некоторый момент этот неиспользуемый код становится проще удалить, чем заставить его работать правильно, если появилась такая необходимость. Какое-то количество неиспользуемого кода почти неизбежно, однако желательно не писать код наперёд, предвосхищая какие-то возможные потребности, так как с большой вероятностью, силы будут потрачены зря: код не будет использоваться и “сгниёт”.
  3. Недостаточный учёт нюансов, важных деталей, которые “не видны” в начале разработки, но в процессе могут привести к необходимости полной переделки проекта. (“Было гладко на бумаге, да забыли про овраги”.)
  4. Подтекающие абстракции (leaking abstractions) — обычно не существует единственной идеальной модели домена. Вариант п.3: выбранные абстракции постепенно всё хуже соответствуют реальности проекта на данный момент. Существенно сказывается на иерархиях классов (неединственность возможных иерархий).
  5. Изменяющиеся требования — процесс разработки длителен, а процесс коммуникации с заказчиками непредсказуем. Однажды может оказаться, что решалась совсем не та задача, которую нужно было решать. Средство борьбы: составление как можно более точно и полно сформулированного технического задания — спецификации продукта. Впрочем, этот процесс (составление) нецелесообразно или даже невозможно довести до логического конца, так как с увеличением точности и полноты мы по сути начинаем заниматься разработкой (идеальная модель — это сам моделируемый объект).

Некоторые простые приёмы борьбы с проблемами разработки

  1. Протоколирование работы. Составление “журналов” (в духе бортжурнала):
  2. Использование системы управления версиями. Позволяет спокойно удалять код, а также протоколировать изменения в коде.
  3. Если формально задаётся “стиль кода”, то его описание должно быть максимально коротким (не более 1 страницы).
  4. Глоссарий: составление словаря терминов.
  5. Анализ с помощью предпрототипа: воображаемого решения. Выявление прецедентов (характерных ситуаций использования разрабатываемого продукта).
  6. Создание прототипа — максимально простой (демонстрационной) версии продукта, которая, однако, содержит все основные его части (хотя бы в виде “заглушек”). Первый прототип часто выбрасывается (“напиши программу, а потом перепиши её с нуля”). После чего делается второй прототип.
  7. Постепенное развитие: эволюция второго протипа до готового продукта.


5 — 13.10

Анализ: отделение констант от переменных.

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

Методы обнаружения и локализации ошибок

Верификация — формальное доказательство корректности программы. Обычно применяется к абстрагированным алгоритмам или протоколам.

Пример пред и постусловий (используется стандартный макрос assert).

// C++
// Вычисление квадратного корня
double sqrt(double x)
{
    // Предусловие: x >= 0.
    assert(x >= 0.);
    // Код, вычисляющий квадратный корень (result).
    // ...
    // Постусловие: result >= 0.
    assert(result >= 0.);
    // Так как точное выполнение равенства result * result == x
    // не всегда возможно, используем неравенство с заданной относительной погрешностью.
    // Постусловие: abs(result * result - x) / x <= DBL_EPSILON
    assert(std::abs(result * result - x) <= DBL_EPSILON * x);
    return result;
}

Некоторые языки программирования (Eiffel, D) поддерживают инварианты классов, пред и постусловия явно, что облегчает документирование кода. (Инварианты и условия должны быть отражены в документации.)


Тестирование — исполнение сценариев работы с заранее известным желаемым поведением программы (или заранее известными результатами). Тест — сценарий тестирования.

Тесты можно считать частью документации — они предоставляют примеры рабочего кода и сценариев работы.

TDD — разработка через тестирование (test driven development). Подход к разработке, подразумевающий написание тестов до написания кода, который эти тесты тестируют.


Шаблоны проектирования

Шаблоны создания

Одиночка (синглтон)

// C++
class Application
{
private:
    // Конструктор по умолчанию (должен быть определён).
    Application();
    // Запрещаем копирование объектов.
    Application(Application&) = delete;
    
    // ...
    
public:
    // Получить ссылку на объект Application.
    static Application& get_object()
    {
        // Объект создаётся при первом вызове функции get_object.
        static Application app;
        return app;
    }
    
    // ...
};

Прототип

// C++
// Интерфейс прототипа.
class Cloneable
{
public:
    virtual ~Cloneable() = default;
    virtual Cloneable* clone() const = 0;
};

// Класс, реализующий интерфейс прототипа.
class Object: public Cloneable
{
public:
    // Конструктор копирования (должен быть определён).
    Object(const Object&);
    
    // Операция "клонирования": "виртуальный конструктор копирования".
    // Ковариантный возвращаемый тип.
    Object* clone() const override
    {
        // Возвращает копию себя.
        return new Object(*this);
    }
    
    // ...
};

Строитель

Абстрактная фабрика

(ковариантность/контравариантность)

Шаблоны структуры

Адаптер

Прокси

Фасад

Декоратор



6 — 20.10

Шаблоны проектирования — продолжение

Шаблоны структуры

Композит

Шаблоны поведения

Интерпретатор

Итератор

Посетитель


7 — 27.10

Проблема мультиметода, реализация мультиметода с помощью шаблона Посетитель.

Наблюдатель

Очередь событий/сообщений/задач

Команда


8 — 03.11

MVP, naked objects

UX/usability, анализ задач, “«интуитивная понятность» для разработчиков не является таковой для конечных пользователей” фокус-группы, асинхронность, минимизация задержек, визуализация “деятельности” (заставки, бегущие полоски и т.п.)

Шаблоны параллелизма


9 — 10.11

Процессы разработки (обзорно): Agile, MSF, RUP/OpenUP, Cleanroom.

Пример: автоматизации работы диспетчера аэропорта.



HTML-файл сгенерирован с помощью системы Pandoc.

Дата последнего обновления: 2016.11.13 (revision 10).

Кувшинов Д.Р. 2016