Ненормальное программирование
Программирование
Проектирование и рефакторинг
26 ноября 2013

Ущербно-ориентированное программирование

Автор оригинала: Typical Programmer
Перевод
Ущербно-ориентированное программирование — это набор подходов, поощряющий повторное использование кода и гарантирующий долгосрочное использование производимого программистами кода в боевых системах. Количество строк кода является повсеместно применяемым показателем значимости приложения, а количество строк, которые программист пишет за рабочий день — полезная метрика, применяемая при планировании проектов и распределении ресурсов. Ущербно-ориентированное программирование — это один из наиболее эффективных способов получить наиболее объемный исходник в кратчайшие сроки.

Ущербный — имеющий изъян, неполноценный. Вредный, недостаточный.

Наследование


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

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

Пример наследования в виде псевдокода:

function getCustName(custID)
{
    custRec = readFromDB("customer", custID);
    fullname = custRec[1] + ' ' + custRec[2];
    return fullname;
}

function getCustEmail(custID)
{
    custRec = readFromDB("customer", custID);
    fullname = custRec[1] + ' ' + custRec[2];
    /***************
    * 4/15/96 git : адрес email хранится
    * во втором поле для факса
    ***************/
    return custRec[17];
}

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

Полиморфизм типов — один из типов наследования. В нем при наследовании кода заменяются типы исходных переменных.

Модульность


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

  • Копирайт
  • Отказ от ответственности
  • От трех до пяти строк со звездочками
  • История изменений
  • Описание того, что модуль изначально должен был делать
  • Еще три — пяти строк со звездочками
  • Большой блок с пробелами и пустыми строками, окруженный звездочками или другими символами, в котором перечислены названия всех функций, имя или инициалы автора, а также дата написания
  • Код

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

Компоненты и библиотеки


В ущербно-ориентированном программировании повсеместно используются плагины и компоненты — куски кода, найденные в интернете или книгах. Гугление позволяет смекалистому программисту сэкономить уйму времени благодаря тому, что уже есть написанные заранее компоненты практически для чего угодно. Самые лучшие среди них — это черные ящики: программисту не нужно знать или думать о том, как компонент работает. Множество крупных приложений написаны с применением наследования из других приложений и компонентов, найденных в сети.

Инкапсуляция


Суть инкапсуляции заключается в отделении данных от кода. Иногда этот подход также называется сокрытием данных, но данные на самом деле не скрываются — они размещаются в дополнительном слое кода. Например, когда в коде повсюду запросы к базе данных — это плохая практика. Ущербно-ориентированный подход предполагает написать обертки для этих функций, тем самым инкапсулировав базу данных. В методе getCustName, представленном выше, обращение к базе данных идет не напрямую, а через такую специальную обертку, считывающую одну запись. Оба этих метода (а также еще множество аналогичных им) «знают», где в записи о клиенте есть необходимые им данные. Способ считывания записи из базы инкапсулирован в каком-нибудь другом модуле.

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

Полиморфизм


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

Например, функции выше можно переписать в одну полиморфную функцию, используя наследование и полиморфизм:

function getCustData(custId, what)
{
    if (what == 'name') {
        custRec = readFromDB("customer", custId);
        fullname = custRec[1] + ' ' + custRec[2];
        return fullname;
    } else if (what == 'email') {
        custRec = readFromDB("customer", custId);
        fullname = custRec[1] + ' ' + custRec[2];
    /***************
    * 4/15/96 git : адрес email хранится
    * во втором поле для факса
    ***************/
        return custRec[17];
    }

    /* ... и так далее. */
}

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

"Является" против "содержит"


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

Виртуальные классы и функции


Виртуальный класс или функция — это код, который понадобится в программе в будущем, однако пока еще не написан. Как правило, это реализуется посредством базового класса, на котором будет основан финальный код.

function calcSalesTax(price, isTaxable, state)
{
    /****************************************
    *
    * TO DO:
    *
    * получать тариф для пользователя
    * из какой-нибудь таблицы, когда ее сделают
    *
    *
    ****************************************/

    /** 02/07/99 git -- пока возьмем такой тариф **/
    return price * (7.25 / 100.0);
}

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

Перегрузка


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

Документация


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

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

Забивание кода комментариями, объявляющими механизмы его работы, отвлекает внимание и замедляет компиляцию. Поэтому программисты, которые следуют «лучшим практикам» ущербно-ориентированного программирования хранят документацию в системе учета документооборота — чтобы программисты не могли ее случайно затереть.

Управление версиями


Это, конечно, не то чтобы практика программирования, но последователи ущербно-ориентированного подхода ее применяют. Хранение предыдущих версий и истории изменений под рукой важно, даже если над проектом работает один человек. Опытные ущербно-ориентированные программисты придерживаются следующих правил:

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

Заключение


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

От переводчика: хоть статья и 2007 года, но актуальности не потеряла. Спасибо vovochkin за ссылку в статье "Как разрабатывать неподдерживаемое ПО".
+145
82,1k 289
Комментарии 115