Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

Как стать программистом и избежать детских ошибок: Как я пишу очередную фичу


Сегодня о проектировании программ: от первого лица и на пальцах.

Термин «фича» я буду использовать, чтобы не путать её с подпрограммами, которые я продолжу называть функциями (что естественно для PHP).

Но вообще это сленговое слово.

ТЗ

Положим, следующим пунктом по плану идёт добавление на одну из страничек (форм) трёх последних сообщений с Твиттера. Принципиально без дат и прочей метаинформации: только сообщения в чистом виде. Выделять в текстах ссылки для простоты задачи тут тоже не будем. Язык — PHP.

Как будем делать?

Шаг первый — мечтаем

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

Вот так, например:

// Извлекаем сообщения вместе с метаданными
// Тройку выносим в настройки программы:
// нам не нужны в коде «волшебные числа»,
// которые бог знает чего обозначают
$msgs Twitter::getRecent(
    OUR_TWITTER_USERNAMEOUR_TWITTER_MSG_COUNT);

// Извлекаем из сообщений тексты, отбрасывая остальное
// Для этого используем другую «вымечтанную» функцию
$msgs array_pluck('text'$msgs);

Красота. Массив $msgs уже можно скормить шаблонизатору штатным образом, с учётом экранирования.

Функция array_pluck() на самом деле не просто вымечтанная, а подсмотренная в ExtJS.

Причём всё это мы не пишем, а пока только представляем.

Шаг третий — пишем код фичи

А вторым шагом мы что сделали? Правильно. Поискали готовые средства:

  • в PHP,
  • в используемом фреймворке,
  • в подключенных библиотеках
  • и (не всегда) просто на просторах Интернета.

Пишем мы сейчас тот самый код, который вообразили себе на первом шаге. Только с поправкой для найденного на втором шаге, если таковое случилось.

Шаг четвёртый — реализуем недостающие функции

Предполагаем, что классы Core и Cache — часть фреймворка.
Всё, написанное заглавными — считаем константами из конфигурации программы.

class Twitter {

    function getRecent($username$count 1) {
        
        // Проверяем кэш
        if ($result = 
            Cache::read('Twitter\\'.$username.'\\'.$count))
            return $result;

        // Получаем JSON
        $result file_get_contents(
            'http://twitter.com/status/user_timeline/'.
            $username.'.json?count='.$count)
        if (!$result// Пустая строка для нас тоже не ответ
            Core::error('Cannot open Twitter');
                                            
        // Извлекаем JSON
        $result json_decode($resulttrue);
        if (!is_array($result))
            Core::error('Twitter data corrupted');
            
        // Сюда можно вписать специфичную для Twitter
        // проверку корректности данных
        // Я с этим для подготовки статьи не разбирался
            
        // Сохряняем в кэш, чтобы не исчерпать лимит запросов
        Cache::write(
            'Twitter\\'.$username.'\\'.$count,
            $result,
            TWITTER_CACHE_TTL);
            
        return $result;
    }
    
}
function array_pluck($arr$prop) {
    $result = array();
    foreach ($arr as $key => $value)
        $result[$key] = $value[$prop];
    return $result;
}

Здесь использованы разные способы именования функций. Те, кто знаком с PHP, обратят внимание на то, что функция array_pluck маскируется под системную для естественности. А доступ к Твиттеру оформляется типичным для компонента образом.

Зачем

Теперь смотрим на код глазами другого разработчика. Даже уберём комментарии для острастки.

$msgs Twitter::getRecent(
    OUR_TWITTER_USERNAMEOUR_TWITTER_MSG_COUNT);
$msgs array_pluck('text'$msgs);

Первый оператор очевиден. Все три названия: Twitter, getRecent, USERNAME и MSG_COUNT однозначно показывают, его назначение.

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

  • будет использоваться по всей программе,
  • имеет стандартный аналог в широко известной ExtJS,
  • оформлена по мотивам встроенной PHP-функции array_map.

Для ознакомления с кодом этого достаточно. Вот и главное осязаемое преимущество.

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

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

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

Учитесь мечтать!


Этот выпуск Вы можете прокомментировать в Живом Журнале.

Задать вопрос

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


В избранное