Приложения ТРИЗ к программированию
Приложение ТРИЗ к программированию. Пример 1. Что увидишь, то и неси
Для решения некоторых задач программирования в качестве "инструмента", облегчающего разработчику абстрагирование от конкретики, предлагается использовать "абсолютно тупого героя", которому поручается некая деятельность. Поскольку этот герой непроходимо туп (мы даже пишем его с маленькой буквы, чтобы и тени величия в нем не наблюдалось), то задача поручить ему деятельность, которая, тем не менее, должна быть выполнена — нетривиальна.
Тем не менее, надо описать решение так, чтобы тупой (несколько тупых) смогли качественно и незатратно порученную задачу выполнить.
ПРИМЕР 1. "ЧТО УВИДИШЬ, ТО И НЕСИ..."
С.В. Сычев, идея, текст про тупого
К.А. Лебедев, задача
Шаг 0. Что мы имеем? (описание языком программиста)
Шаг 1. Абстрагируемся (с помощью тупого героя)
Шаг 2. Пишем абстрактное решение (понятное любому)
Шаг 3. Переводим "абстрактное решение" на привычный для программиста язык
ШАГ 0
ЧТО МЫ ИМЕЕМ?
Дан фрагмент некоторой функции:
// Указатель на буфер с данными
char * pszBuff;
// Размер буфера
DWORD dwSize;
// Вспомогательные указатели
char * pszTemp = pszBuff;
char * p0;
char * pEnd;
// Ищем строку
p0 = FindStr(pszTemp, dwSize, "/FullName", strlen("/FullName"));
// Ищем открывающий символ
p0 = FindSymbol(p0, ‘(‘);
p0++;
// Ищем закрывающий символ
pEnd = FindSymbol(p0, ‘)’);
// Сохраняем значение
OutputValue(p0, pEnd – p0);
// Вычитаем из общего размера разницу
dwSize -= (pEnd - pszTemp);
// Запоминаем указатель
pszTemp = pEnd;
// Ищем строку
p0 = FindStr(pszTemp, dwSize, "/FamilyName", strlen("/FamilyName"));
// Ищем открывающий символ
p0 = FindSymbol(p0, ‘(‘);
p0++;
// Ищем закрывающий символ
pEnd = FindSymbol(p0, ‘)’);
// Сохраняем значение
OutputValue(p0, pEnd – p0);
// Вычитаем из общего размера разницу
dwSize -= (pEnd - pszTemp);
// Запоминаем указатель
pszTemp = pEnd;
// Ищем строку
p0 = FindStr(pszTemp, dwSize, "/FontName", strlen("/FontName"));
// Ищем открывающий символ
p0 = FindSymbol(p0, ‘/‘);
p0++;
// Ищем закрывающий символ
pEnd = FindSymbol(p0, ‘ ’);
// Сохраняем значение
OutputValue(p0, pEnd – p0);
// Вычитаем из общего размера разницу
dwSize -= (pEnd - pszTemp);
// Запоминаем указатель
pszTemp = pEnd;
Мы видим, что в данном фрагменте кода трижды повторяется одинаковая последовательность действий. Вот эта последовательность:
// Ищем строкуТеоретически, ее можно поместить в тело цикла и, тем самым, существенно сократить код функции. Но вот беда: каждый раз последовательность применяется к разным данным. Как быть?
FindStr(...);
// Ищем открывающий символ
FindSymbol(...);
// Ищем закрывающий символ
FindSymbol(...);
// Сохраняем значение
OutputValue(...);
ШАГ 1
АБСТРАГИРУЕМСЯ
Надо постоянно посылать тупого за чем-нибудь:
- сбегай за сигаретами,
- сбегай за избирателями,
- сбегай за правдой,
- сбегай за девками...
Если бы нужны были только девки (пусть и часто), то сказали бы: «Неси!» и все.
Если бы девки нужны были по графику, то сказали бы тупому: "Неси, как написано!".
А так всякий раз тупому приходится объяснять: куда бежать, когда бежать, зачем бежать, кто такие избиратели, являются ли девки избирателями тоже, кто такие девки в целом и т.д.
ШАГ 2
ОПИШЕМ АБСТРАКТНОЕ РЕШЕНИЕ
Чтобы много не говорить, разложим "сигареты", "избирателей", "правду" и "девок" аккуратно вдоль одной большой дороги пачками, не смешивая. И скажем тупому: "Что увидишь, то и неси, потом опять беги, но подряд не повторяйся". (И зациклим).
А еще тупому можно говорить: "Принеси номер такой-то".
После упорядочения "объекты" получают следующие номера:
- Сигареты.
- Избиратели.
- Правда.
- Девки.
И тогда тупому нужно просто назвать номер того, что надо принести: "Принеси номер 1" или "Принеси номер 4".
ШАГ 3
ПЕРЕВЕДЕМ "АБСТРАКТНОЕ РЕШЕНИЕ" НА ПРИВЫЧНЫЙ ЯЗЫК
Все данные "упаковываем" в массив. На каждой итерации цикла последовательность действий обращается к "своим" данным по значению индексной переменной.
// Указатель на буфер с данными
char * pszBuff;
// Размер буфера
DWORD dwSize;
// Вспомогательные указатели
char * pszTemp = pszBuff;
char * p0;
// Массив искомых строк
const char * pszFind[3] =
{
"/FullName",
"/FamilyName",
"/FontName"
};
// Массивы открывающих и закрывающих символов
const char * szIn = "((/";
const char * szOut = ")) ";
for (int i = 0; i {
// Ищем строку
p0 = FindStr(pszTemp, dwSize, pszFind[i], strlen(pszFind[i]));
// Ищем открывающий символ
p0 = FindSymbol(p0, szIn[i]);
p0++;
// Ищем закрывающий символ
char * pEnd = FindSymbol(p0, szOut[i]);
// Сохраняем значение
OutputValue(p0, pEnd – p0);
// Вычитаем из общего размера разницу
dwSize -= (pEnd - pszTemp);
// Запоминаем указатель
pszTemp = pEnd;
}
Примечание.
Разбор этой же задачи иным способом - см. в статье: С.В. Сычев, К.А. Лебедев "Как вспомнить "и так известное".
Другие примеры раздела TStupid - см. здесь и здесь.
Материал опубликован на сайте "Открытые методики рекламы и PR "Рекламное Измерение" 31 мая 2005 г.