среда, 11 февраля 2009 г.

Code Less?

Ходят слухи (на RSDN, например) об исследованиях, по результатам которых получается, что плотность ошибок в программах практически не зависит от языка программирования. Грубо говоря, на 1000 строк C++ кода будет приходиться такое же количество ошибок на 1000 строк Java кода. Поэтому тот язык лучше, который позволяет писать меньше кода для решения задачи. Крайним проявлением этого можно считать современное внимание к функциональным языкам программирования, в которых сложные операции могут записываться в одну строчку. Например:


[ arr!name | (_,name,_) <- foos ]

(это получения списка элементов из ассоциативного контейнера, ключом поиска для которых является имя элемента из списка foos).

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

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

std::string message_id = parsed.message_id();
if( message_id.empty() )
  message_id = make_fake_message_id();

Я переписал его так:

std::string original_message_id;
std::string actual_message_id;
take_message_id_from_submit_sm_resp(
parsed,
info->m_msisdn,
original_message_id,
actual_message_id );

Да еще добавил новый метод:

void
a_channel_t::take_message_id_from_submit_sm_resp(
const smpp_pdu_1::submit_sm_resp_t & pdu,
const std::string & msisdn,
std::string & original_message_id,
std::string & actual_message_id )
{
     original_message_id = pdu.message_id();
     if( original_message_id.empty() )
       actual_message_id = make_fake_message_id();
     else
       actual_message_id = m_message_id_formatter.transform(
           original_message_id,
           msisdn );
   }

Хотя можно было бы просто вставить в код on_submit_sm_resp:

std::string original_message_id(parsed.message_id());
std::string actual_message_id;
if( original_message_id.empty() )
   actual_message_id = make_fake_message_id();
else
   actual_message_id = m_message_id_formatter.transform(
     original_message_id,
     info->m_msisdn );

Что, конечно бы привело к меньшему количеству кода. Но был бы этот короткий код лучше?

Я считаю, что нет.

Во-первых, такая вставка увеличивает размер метода on_submit_sm_resp.

Во-вторых, такая вставка добавляет в метод on_submit_sm_resp технические подробности, которые являются слишком низкоуровневыми для метода on_submit_sm_resp: для него важно наличие original_message_id и actual_message_id, а детали их получения лежат на более низком уровне абстракции.

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

В-четвертых, задокументировать алгоритм вычисления actual_message_id и причины, по которым это вообще делается гораздо проще в Doxygen-комментариях к take_message_id_from_submit_sm_resp, чем к on_submit_sm_resp.

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

1 комментарий:

Анонимный комментирует...

Женя, включи, плиз, полные посты в RSS ленте. Неудобно кастрированные читать.