воскресенье, 31 октября 2010 г.

Проектирование: как выполнять декомпозицию задачи?

Нередко формулировка задачи, полученная от заказчика, вызывает у проектировщика шок:

  • "Спроектируйте графический редактор".
  • "Спроектируйте игру для девочек 4 – 8 лет".
  • "Спроектируйте GPS-навигационную систему для мобильного телефона".

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

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

  1. выполнить декомпозицию задачи на подзадачи;
  2. оценить объём полученных подзадач;
  3. составить план работы, упорядочив подзадачи во времени и распределив их между участниками команды.

Рассмотрим эту процедуру на примере проектирования графического редактора.


Шаг 1. Определение назначения системы
Укажите назначение проектируемой системы.

Примечание: Необходимо избегать размытых формулировок вида: "Система для хранения данных".

ПРИМЕР. Назначение графического редактора – создание/редактирование поздравительных открыток.

Шаг 2. Описание модели функционирования системы
Опишите то, как будет функционировать система. Для этого необходимо указать основные действия системы и упорядочить их во времени.

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

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

ПРИМЕР. Построим модель функционирования графического редактора.

На первой итерации она будет выглядеть так:

А) в виде списка:

  1. Загрузка документа (из файла).
  2. Редактирование документа.
  3. Сохранение документа (в файле).

Б) в виде рисунка:



На второй итерации детализируем функции "загрузка" и "сохранение", т.к. это сделать проще всего.

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

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




На третьей итерации попробуем детализировать функцию "редактирование". Для этого рассмотрим, как пользователь редактирует документ.



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

Далее, заметим, что устройство ввода тоже влияет на документ не напрямую, а через различные GUI-элементы, такие как: меню, тулбары, selection'ы, различные модификаторы (например, для масштабирования фигуры используется рамка с точками, за которые можно потянуть мышью).

Эти GUI-элементы вызывают команды редактирования, которые уже непосредственно изменяют документ.

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

Наконец, пользователь не "видит" документ напрямую. Для него документ визуализируется с помощью устройства вывода. Чтобы устройство вывода могло визуализировать документ, документ следует сначала растеризовать.

Шаг 3. Составление списка задач
Составьте список задач. Для его составления следует использовать модель функционирование системы.

При этом нужно руководствоваться такими правилами:

  1. Если действие сформулировано чётко и понятно, то оно переносится в список задач без изменений.
  2. Если действие трудно назвать или его название выглядит слишком общо, то следует проверить, не является ли это действие преобразованием из одного в другое (например, преобразование команд от устройства ввода в команды от элементов GUI).

ПРИМЕР. Для графического редактора получаем:

  1. Редактирование документа (== составить список операций над документом и его элементами).
  2. Растеризация документа.
  3. Загрузка документа из файла.
  4. Сохранение документа в файле.
  5. Преобразование документа из неродного формата в родной.
  6. Преобразование документа в неродной формат из родного.
  7. Преобразование действий пользователя в команды от устройства ввода.
  8. Преобразование команд от устройств ввода в команды от элементов GUI.
  9. Преобразование команд от элементов GUI в команды редактирования.

Шаг 4. Объединение задач в группы
Объедините задачи в группы, если:
  • задачи похожи по смыслу;
  • задачи противоположны друг другу.
Например, задача "загрузка" противоположна по смыслу задаче "сохранение". Эти задачи можно объединить в группу "загрузка/сохранение".

ПРИМЕР. После группировки получаем такой список задач:

  1. Редактирование.
  2. Растеризация.
  3. Загрузка/сохранение.
  4. Преобразование из формата в формат.
  5. Преобразование действий пользователя в команды от устройства ввода.
  6. Преобразование команд от устройств ввода в команды от элементов GUI.
  7. Преобразование команд от элементов GUI в команды редактирования.

Шаг 5.  Упорядочение задач во времени и пространстве
Расположите задачи в порядке, необходимом для получения решения. Если задача позволяет решить другую задачу, то она должна быть расположена раньше неё. Если задача не может быть решена без решения другой задачи, то она должна быть расположена позже неё.

ПРИМЕР. Самой главной задачей в нашем примере является задача "редактирование". Чтобы выполнить остальные задачи, нам нужно знать либо перечень операций над документом, либо структуры данных, которые будут использованы для представления документа. Поэтому задачу "редактирование"  расположим в самом начале.

Следующей по важности задачей является задача "преобразование действий пользователя в команды от устройств ввода". Эту задачу мы ставим раньше задачи 6 ("преобразование команд от устройств ввода в команды от элементов GUI ") и задачи 7 ("преобразование команд от элементов GUI в команды редактирования "), т.к. вид и функции GUI-элементов нередко определяются возможностями устройств ввода.

На пятое место ставим сразу три задачи:

  1. загрузка и сохранение;
  2. преобразование из/в;
  3. растеризация.

Эти задачи могут выполняться параллельно.

В виде графической схемы план разработки графического редактора выглядит так:



Задачи, которые могут выполняться параллельно, можно делегировать сразу нескольким разработчикам. Более сложные задачи (например, "редактирование") следует отдавать более опытным разработчикам.

Примечание: После составления схемы плана разработки можно ещё раз взглянуть на задачи и, если получится, объединить некоторые из них в группы. В нашем случае задачи "преобразование действий пользователя в команды от устройств ввода" и "преобразование команд от устройств ввода в команды GUI" можно объединить в задачу "проектирование ввода". А задачу "преобразование команд от GUI в команды редактирования" можно назвать по-другому – "проектирование GUI".

С учётом изменений получится:

Резюме
В данном посте предложен алгоритм, который позволяет:

  1. преобразовать изначально слишком общую и потому неприступную задачу к серии более конкретных и чуть лучше измеряемых (в плане необходимых ресурсов) задач;
  2. упорядочить задачи во времени и пространстве (между разработчиками) и составить, пусть и приблизительный, но работоспособный план работ.

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