Программный продукт обладает свойством
модифицируемости, если он имеет структуру, позволяющую легко вносить требуемые изменения.
Для начала стоит отметить традиционно разные подходы в программировании. В Windows изначально развивался подход комбайнов, вся многочисленная функциональность в одном приложении. В Linux использовались маленькие утилиты имеющие строго ограниченную функциональность для конкретных целей. В современном мире кроссплатформенных приложений такое деление уже не играет особой роли.
Не смотря на это у программ есть назначение, ведь они создаются для решения каких-то конкретных задач. Любое изменение в функционале, такое как наращивание или отсечение возможностей, а так же усовершенствование существующих алгоритмов связано со свойством модифицируемости. Наплевательский подход состоит в том, чтобы держать одних и тех же программистов, которые будут худо ли бедно хранить маркеры изменений у себя в голове.
Чисто умозрительно представим весь код программы в виде таблицы в базе данных, тогда для его модификации нам бы потребовался
язык манипулирования данными (Data Manipulation Language). Функции языков DML определяются первым словом в предложении (часто называемом запросом), которое почти всегда является глаголом. В случае с SQL эти глаголы —
выбрать (select),
вставить (insert),
обновить (update), и
удалить (delete).
Для примера простой код на C++:
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char* argv[])
{
printf("Hello World!\n");
return EXIT_SUCCESS;
}
В сложные детали самодокументированного кода или иной технологии вдаваться не будем. Везде будут использованы комментарии, которые как правило есть практически в любом языке программирования.
Деление по структуре кода могло бы иметь следующий вид:
// begin: заголовочные файлы
#include <stdlib.h>
#include <stdio.h>
// end: заголовочные файлы
// begin: точка входа в приложение
int main(int argc, char* argv[])
{
printf("Hello World!\n");
return EXIT_SUCCESS;
}
// end: точка входа в приложение
Ключевой особенностью группировки являются синтаксические конструкции. В классе это могут быть закрытые поля, искусственное выделение свойств и методов. Главное, что для конкретной программы подобное разделение не уникально.
А вот функционал одновременно является и уникальным и не уникальным. Его уникальность в том, что он часто содержится в программе в единственном числе, нет смысла плодить китайский код. Не уникальность же позволяет переносить его между различными программами, в том числе добавлять чужие решения в свой код.
Предположим есть некий функционал:
1. ...
2. …
3. поздороваться с миром
n. …
Где 1…n уникальные идентификаторы функционала. Буквы обозначают b — begin (начало секции), e — end (конец секции), c — continue (секция до конца строки).
Вставили новый функционал (insert):
#include <stdlib.h>
#include <stdio.h> // 3.c
int main(int argc, char* argv[])
{
printf("Hello World!\n"); // 3.c
return EXIT_SUCCESS;
}
Обновили текущий функционал (update):
#include <stdlib.h>
#include <stdio.h> // 3.c
// 3.b
inline void hello_world()
{
printf("Hello World!\n");
}
// 3.e
int main(int argc, char* argv[])
{
hello_world(); // 3.c
return EXIT_SUCCESS;
}
Удалили текущий функционал (delete):
#include <stdlib.h>
int main(int argc, char* argv[])
{
return EXIT_SUCCESS;
}
Выделение нового функционала (select) могло бы осуществляться простым поиском по файлам проекта "// 3.".
Конечно, всё это условность и можно использовать более совершенные методы отвечающие за возможность модифицируемости. В статье же хотелось показать сам принцип. Произведя операцию удаления функционала под номером 3 после вставки или после обновления получаем абсолютно одинаковые результаты. Та же директива включения stdlib.h нужна была лишь для использования EXIT_SUCCESS.
Текущий список функционала программы:
1. запустить приложение
2. выйти из приложения
3. поздороваться с миром
Код с маркерами изменения функционала:
#include <stdlib.h> // 2.c
#include <stdio.h> // 3.c
// 3.b
inline void hello_world()
{
printf("Hello World!\n");
}
// 3.e
// 1.b
int main(int argc, char* argv[])
{
hello_world(); // 3.c
return EXIT_SUCCESS; // 2.c
}
// 1.e
Или вариант не меняющий количество строк кода. Так же не мешает однострочным комментариям и самодокументируемому коду. Применительно к C++ не прерывает многострочные комментарии (/*..*/):
#include <stdlib.h> // 2.c
#include <stdio.h> // 3.c
inline void hello_world() // 3.b
{
printf("Hello World!\n");
} // 3.e
int main(int argc, char* argv[]) // 1.b
{
hello_world(); // 3.c
return EXIT_SUCCESS; // 2.c
} // 1.e