суббота, 6 ноября 2010 г.

Проектирование: построение функциональной модели

Прежде чем проектировать структуры данных, разработчик должен построить функциональную модель проектируемой системы.

В умных книжках по OOA&D рекомендуют поступать наоборот: сначала выявлять классы и объекты и лишь затем – находить операции для этих классов и объектов. Автор данного блога рекомендует не верить умным книжкам и начинать проектирование с выявления функций.

У автора есть веские основания для такой рекомендации: любая техническая система создаётся для выполнения каких-то полезных функций. Если она их не выполняет, то такая система не нужна. Согласитесь, мало кому в быту будет полезен стул со сломанными ножками. Просто потому что на нём невозможно сидеть. В программной индустрии точно также. Если программа не делает нечто полезное, её можно удалить с компьютера, чтобы она не занимала место.

Направление проектирования должно быть задано однозначно: от функций к структурам данных, а не наоборот.


Функциональная модель проектируемой системы напрямую вытекает из функциональных требований, которые, в свою очередь, могут быть описаны в виде прецедентов. Существует несколько разных шаблонов оформления прецедентов (например, здесь и здесь). Но, на мой взгляд, главное – не оформление, а – суть. Прецедент должен описывать технологию создания чего-либо полезного при помощи проектируемой системы.

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

А) Первым шагом документируем функциональные требования к модулю редактирования. Для этого возьмём пример создания пригласительной открытки в программе CorelDraw и опишем его в виде набора прецедентов.

Ниже я намеренно привожу описания всех прецедентов, чтобы заинтересованный читатель мог при желании разобраться в примере.

Описание начинается с прецедента высокого уровня, который обращается к прецедентам более низкого уровня, а те, в свою очередь, обращаются к прецедентам ещё более низкого уровня и т.д. Каждый абстрактный прецедент детализируется конкретными прецедентами. Это позволяет сократить размер прецедента и сделать его более понятным для восприятия.

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

Прецедент 1. Создать приглашение

  1. Создать дома.
  2. Создать ночной фон.
  3. Создать облака и месяц.
  4. Создать надписи.
  5. Создать отрывной корешок.

Прецедент 2. Создать дома

  1. Создать дом.
  2. Создать копию дома.
  3. Переместить копию дома на расстояние.
  4. Создать зеркальное отражение копии дома.
  5. Уменьшить размеры второго дома.
  6. Изогнуть первый дом.
  7. Изогнуть второй дом.
  8. Переместить второй дом в сторону первого дома (чтобы частично перекрывал).
  9. Переместить второй дом по Z за первый дом.

Прецедент 3. Создать дом

  1. Создать контур здания.
  2. Создать контуры окон.
  3. Залить контур здания красным цветом.
  4. Залить контуры окон жёлтым цветом.
  5. Создать антенну.
  6. Выделить все детали дома.
  7. Объединить выделенное в группу.
  8. Уменьшить размеры дома.

Прецедент 4. Создать контур здания

  1. Создать прямоугольник.
  2. ???????????????????????

Прецедент 5. Создать контуры окон

  1. Создать ряд окон.
  2. Создать дубликат ряда окон.
  3. Переместить дубликат на заданное расстояние.
  4. Создать дубликат ряда окон.
  5. Переместить дубликат на заданное расстояние.
  6. Выделить все ряды окон.
  7. Объединить ряды окон в группу.

Прецедент 6. Создать ряд окон

  1. Создать окно.
  2. Создать дубликат окна.
  3. Переместить дубликат на заданное расстояние.
  4. Создать дубликат окна.
  5. Переместить дубликат на заданное расстояние.
  6. Выделить три окна.
  7. Объединить окна в группу.

Прецедент 7. Создать окно

  1. Создать маленький прямоугольник.
  2. Создать линию, отделяющую одну треть окна от остальной части.
  3. Выделить прямоугольник и линию.
  4. Объединить выделенное в группу.

Прецедент 8. Создать антенну

  1. Создать первую (вертикальную!) линию.
  2. Создать вторую линию.
  3. Создать третью линию.
  4. Создать четвёртую линию.
  5. Создать пятую линию.
  6. Выделить все 5 линий.
  7. Объединить выделенное в группу.

Прецедент 9. Создать ночной фон

  1. Создать большой прямоугольник для фона.
  2. Заполнить прямоугольник чёрным цветом.
  3. Выделить прямоугольник фона.
  4. Поменять местоположение прямоугольника по Z (отправить на задний план).

Прецедент 10. Создать облака и месяц

  1. Создать эллипс.
  2. Разместить эллипс над домом.
  3. Залить эллипс бордовым цветом.
  4. Создать второй эллипс.
  5. Разместить второй эллипс над домом, слегка закрыв первый эллипс.
  6. Залить второй эллипс коричневым цветом.
  7. Создать круг.
  8. Расположить его над домом рядом с первыми двумя эллипсами.
  9. Залить круг светлым цветом.
  10. Преоборазовать эллипс в сектор эллипса.
  11. Изменить угол дуги сектора эллипса до полумесяца.

Прецедент 11. Создать надписи

  1. Создать надпись "NIGHT".
  2. Изменить шрифт надписи.
  3. Окрасить надпись в жёлтый цвет.
  4. Повернуть надпись на 90 градусов.
  5. Переместить надпись на чёрный фон слева.
  6. Создать надпись "PARTY".
  7. Изменить шрифт надписи.
  8. Изменить размер надписи.
  9. Окрасить надпись в белый цвет.
  10. Переместить надпись на чёрный фон снизу.
  11. Создать надпись "WELCOME".
  12. Изменить шрифт надписи.
  13. Изменить размер надписи.
  14. Изменить цвет надписи.
  15. Изменить местоположение надписи.
  16. Создать надпись "23:00".
  17. Изменить шрифт надписи.
  18. Изменить размер надписи.
  19. Изменить цвет надписи.
  20. Изменить местоположение надписи.

Прецедент 12. Создать отрывной корешок

  1. Создать прямоугольник.
  2. Поместить прямоугольник правее остального изображения.
  3. Окрасить прямоугольник фрактальной заливкой.

Б) Вторым шагом – выпишим неделимые функции (которые не представлены в виде других прецедентов), сгруппируем их по темам и представим в виде таблицы.

Получится функциональная модель модуля редактирования:

Группа
Функция
1. Создание
F1.1. Создать прямоугольник.
F1.2. Создать эллипс.
F1.3. Создать окружность.
F1.4. Создать прямую.
F1.5. Создать текст.
2. Копирование
F2.1. Создать копию фигуры.
3. Группирование
F3.1. Объединить фигуры в группу.
F3.2. Разделить группу на отдельные фигуры.
4. Размещение
F4.1. Переместить фигуру в заданную точку.
F4.2. Переместить фигуру на заданное расстояние.
5. Масштабирование
F5.1. Изменить размеры фигуры по вертикали.
F5.2. Изменить размеры фигуры по горизонтали.
F5.3. Изменить размеры фигуры пропорционально.
6. Размещение по Z
F6.1. Поместить фигуру на задний план.
F6.2. Поместить фигуру на передний план.
7. Форма
F7.1. Создать зеркальное отражение фигуры относительно вертикальной оси.
F7.2. Создать зеркальное отражение фигуры относительно горизонтальной оси.
F7.3. Изогнуть фигуру.
F7.4. Преобразовать эллипс/окружность в сектор.
F7.5. Изменить угол сектора.
8. Форматирование
F8.1. Изменить шрифт текста.
F8.2. Изменить размер шрифта.
F8.3. Изменить цвет текста.
9. Заливка
F9.1. Залить фигуру заданным цветом.
F9.2. Залить фигуру фрактальным рисунком.
10. Вращение
F10.1. Повернуть фигуру на 90 градусов.
F10.2. Повернуть фигуру на 180 градусов.
F10.3. Повернуть фигуру на произвольный угол.

Данная функциональная модель не является полной, т.к. основана только на одном примере. Её можно и нужно дополнять, описывая и анализируя другие примеры создания поздравительных и пригласительных открыток.

11 комментариев:

  1. "В умных книжках по OOA&D рекомендуют поступать наоборот: сначала выявлять классы и объекты и лишь затем – находить операции для этих классов и объектов. Автор данного блога рекомендует не верить умным книжкам и начинать проектирование с выявления функций."

    Для примитивов подходит, примерно как описано у Вас, для открытки.
    А вы попробуйте сначала сформулировать набор функций для более сложных систем. Для примера предложу подумать над системой прогнозирования тех процессов. Прикиньте, какие функции вы в данном случае можете предложить, не имея базу классов и данных на которых вы собираетесь проводить операции.

    ОтветитьУдалить
  2. Каждый разработчик почему-то считает свою систему уникальнее и сложнее, чем у других. :) Не думаю, что графический редактор уровня CorelDraw проще той системы, что упомянули Вы.

    Конечно, для большой системы бывает сложно составить сходу функциональную модель. Поэтому предварительно полезно выполнить декомпозицию большой системы на функциональные модули (например, как описано здесь http://askofen.blogspot.com/2010/10/blog-post_31.html).

    ОтветитьУдалить
  3. Мне кажется, вы зря противопоставляете выявление объектов и функций при анализе. Они единое целое. На самом деле всеишда выявляются и объекты, и функции, в которые эти объекты вовлечены. Сами посмотрите на ваши презеденты - "создать дома" (т.е. объекты графических примитивов типа Дом), "создать ночной фон" (т.е. объект фоновая заливка типа Ночь) и т.д. В комменте к вашему предыдущему посту я упомянул, что начинаю с Actors. В прецедентах они бы тоже не помешали. Их может быть лишь два - Система и Пользователь. Зато сразу видно, автоматическое это действие или управляется оператором.

    Подчеркну, что мои комментарии не следует воспринимать как критику. Я говорю то же самое, что и вы. Просто акценты чуть по-другому расставляю.

    А тему вы подняли очень хорошую...

    ОтветитьУдалить
  4. Oops! Прошу простить за описки - "всегда" и "прецеденты"

    ОтветитьУдалить
  5. Каждая из xDrivenDevelopment не представляет из себя серебрянной пули.

    Но! изучив их, получив навыки работы с этими методологиями, программист лучше осознает уровни композиции.

    А это, пожалуй, необходимый навык для работы над сложными системами. Архитектор (или ведущий разработчик), по-моему, просто обязан хорошо разбираться в огромном множестве разных методологий разработки.

    И вот такой, функциональный подход - еще одна ступень к улучшению навыков проектирования.

    ОтветитьУдалить
  6. Объекты объектам рознь... )

    В машиностроении обычно разделяют инструмент и изделие. Изделие - это объект, который обрабатывается или изготавливается. В нашем случае, это открытка.

    Примитивы - прямоугольники, эллипсы, прямые и текст можно считать заготовками (тоже изделия, но ещё не обработанные).

    Инструмент - это то, с помощью чего создаётся изделие (== обрабатывается заготовка). В нашем случае, это проектируемая система.

    Нет ничего плохого в анализе изделий и заготовок, т.к. всякий инструмент создаётся для работы с конкретными изделиями. Плохо, когда начинают проектировать конструкцию инструмента в отрыве от его функций. В частном случае, когда разделение инструмента на подсистемы основывается на объектах предметной области, когда эти объекты как бы переносятся внутрь инструмента в виде отдельных его подсистем.

    Спасибо,

    ОтветитьУдалить
  7. Мой комментарий был первым, извиняюсь что анонимно отправился.

    Я недаром указал на промышленные тех процессы.
    Дело в том, что функциональность и набор данных бывают разорванными и существующими отдельно сами по себе.

    Приведу пример на основе газотранспортной системы:
    Тех процесс - транспорт газа по некоторой территории включает в себя трубопроводы, замерные узлы, заводы по поддержанию потока. На замерных узлах имеется своя "сантехника", разные датчики, манометры, вычислители. В свою очередь эти самые вычислители генерируют надцать потоков данных, на основании которых вы и платите за газ, но в свою очередь, эти данные отражают и физическую суть процесса позволяющие проводить анализ и иные операции над этими данными.

    Для приведенной системы, составить набор необходимого функционала для ПО, структур данных и т.д. не составит проблемы. А вот составление функционала для аналитики, прогнозирования, анализа и т.д. опирается на имеющиеся данные. Причем данные которые можно получить на промышленном потоке, а не в лабораторных условиях, а это в свою очередь означает что сформировать возможный функционал Вы можете только после построение модели данных.
    Следующей стороной являются математические и научные разработки, опираясь на которые, Вы можете расширять функционал систем, но опять таки опираясь на имеющиеся модели данных. Тут не имеется ввиду масштабируемость системы, имеется ввиду что функционал порождают сами данные и мат аппарат физ процесса.

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

    Также хочу обратить внимание что таких систем множество, а первичное выделение функционала допустимо для одноуровневых систем к которым относиться и Открытка и КорольДров, как и множество иных достаточно сложных, но все же включающих в себя один уровень программ.

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

    Спасибо за внимание.

    ОтветитьУдалить
  8. Здравствуйте, Александр!

    Вы пишите: "А вот составление функционала для аналитики, прогнозирования, анализа и т.д. опирается на имеющиеся данные. Причем данные которые можно получить на промышленном потоке, а не в лабораторных условиях, а это в свою очередь означает что сформировать возможный функционал Вы можете только после построение модели данных".

    На самом деле в этом нет никакого противоречия по сравнению с тем, что пишу я. Выше я уже упоминал, что существуют изделия (пригласительная открытка, данные потока и т.п.) и инструменты (программа для рисования, программа для анализа данных и т.д.). Ключевое между ними отличие - инструмент создаёт и/или обрабатывает изделие.

    В этом блоге я пишу о проектировании программ (т.е. инструментов). И всё, что я пытался сказать фразой "Направление проектирования должно быть задано однозначно: от функций к структурам данных, а не наоборот" означает, что неправильно строить объектную модель программы раньше функциональной модели.

    К анализу изделий (в Вашем случае - промышленного потока) и процессов их обработки это никак не относится.

    Спасибо,

    ОтветитьУдалить
  9. Ок) Я просто воспринял ваш текст более обобщенно)

    ОтветитьУдалить
  10. можете порекомендовать книжки на русском по OOA&D?

    ОтветитьУдалить
  11. Иван, посмотрите здесь: http://askofen.blogspot.com/2010/08/blog-post.html

    ОтветитьУдалить