суббота, 5 ноября 2011 г.

Что такое "идеальный код"?


Из обсуждения на форуме RSDN.RU (привожу свои доводы):

Судя по приведенному примеру, Вы предполагаете, что программный код – это набор символов. Но это неправильно! Это все равно, что предполагать, будто дом – набор кирпичей или строительных блоков.

Так же, как письменная речь не сводится к набору букв, так и программный код – нечто большее, чем набор символов. Код имеет иерархическую структуру, и на каждом уровне этой иерархии в качестве элементов выступают разные абстракции:

0. Символы.
1. Слова (операторы, переменные, константы, типы данных).
2. Функции и функциональные блоки.
3. Типы данных, определяемые пользователем.
4. Иерархии, группы связанных классов.
5. Библиотеки.
6. И т.д. – наверняка что-то пропустил.

Соответственно, когда я пишу, что хороший код – это компактный код, я отнюдь не имею в виду минимальное количество символов. Ибо хорошо понимаю, что код – это нечто большее, чем просто набор символов, и вижу различные системные уровни.


Как я уже писал, для задания критерия качества кода мне привычнее пользоваться понятием идеальность. Это «тризовский» термин, и суть его заключается в следующем:

Идеальная система – эта система, которой нет, а функция ее выполняется.

Применительно к программному коду, это правило будет звучать так:

Идеальный код – это код, которого нет, а функция его выполняется.

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

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

Например, для хранения координат и выполнения операций над ними можно использовать встроенные типы данных:

int iOldX;
int iOldY;
int cx;
int cy;
//...
int iNexX = iOldX + cx;
int iNewY = iOldY + cy;

А можно создать свои типы данных и выразить те же самые действия иначе:

CPoint pt;
CSize sz;
//...
CPoint pt1 = pt + sz;

Мы видим, что вторая запись компактнее, а, следовательно, идеальнее. Ее компактность обеспечивается не небольшим количеством символов (системный уровень 0), а небольшим количеством слов – названий переменных и операторов (системный уровень 1).

В данном случае, повышение идеальности на уровне 1 привело к небольшому снижению идеальности на уровнях 2 и 3 – нам пришлось добавить классы CPoint и CSize и определить оператор ‘+’ для этих типов данных. Соответственно, мы можем повысить идеальность на этих уровнях, если будем использовать уже готовую библиотеку, где классы CPoint и CSize определены (например, MFC). В этом случае, правда, ухудшится идеальность на уровне 5. Но если библиотека уже была нами использована для каких-то иных целей, то снижение идеальности не произойдет.

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

О чем это говорит? О том, что любое правило нельзя применять бездумно. Нужно четко видеть различные системные уровни и изменения, которые происходят на них в процессе развития или под воздействием правила. Как было написано в статье, отсутствие системного мышления – типовая ошибка разработчиков и причина «глючного» кода. Подозреваю, что по этой причине просьба Заказчика о внесении изменений в Ваш компонент оказалась неожиданной. Потому что Вы оперируете одной системой, одним компонентом, а не классами подобных систем и не классами подобных компонентов.

...

1. Умение использовать для постановки и решения задач различные модели, разумеется, благо. И паттерны – хорошая модель, потому что она проста. Эта модель доступна даже студенту. Или, скажем так, она гораздо доступнее, чем системное мышление или идеальность. Это – как приемы в ТРИЗ, которые всем доступны и всем понятны. Поэтому большинство людей и пользуются ими.

2. Казалось бы, раз приемы (паттерны) доступнее и понятнее, то нужно выявить как можно больше приемов, и, благодаря этому, получить возможность решать все новые и новые классы задач. Однако практика показалась тупиковость такого подхода. Скажем так, существует ограничения на качественный уровень задач, которые можно решить при помощи приемов. Поэтому в ТРИЗ и появились другие инструменты: стандарты, физические противоречия, физэффекты и т.п. Соответственно, и в нашем случае основная цель – это не переоткрыть паттерны, а создать инструментарий более высокого класса, который бы позволял решать более сложные и менее очевидные задачи.

3. Разумеется, более сложный инструментарий предъявляет и более высокие требования к квалификации того, кто им будет пользоваться. Конечно, проблему с датчиками можно устранить паттерном Visitor или двойной диспетчеризацией. Но лучше вообще так не проектировать. В ряде случаев паттерны выступают в роли «заплатки», которая закрывает архитектурные «глюки».

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

Качественная архитектура должна быть расширяемой. Это означает, что разработчик должен спрогнозировать возможные направления развития системы. Для этого он должен не смотреть на систему в одиночестве (саму по себе), а желательно посмотреть на систему в контексте. Т.е. должен видеть и систему, и надсистему, и отдельные подсистемы. Желательно, еще и в развитии. Здесь мы вплотную подступаем к системному мышлению. А это – не такая уж простая штука, и простым набором паттернов здесь не обойдешься.

4. Все выше изложенное свидетельствует о том, что дискуссия вызвана не только и не столько разницей инструментария (паттерны, ТРИЗ)... Можно, например, до посинения спорить, что трубка курительная и «трубка» телефонная суть разные вещи, и не понимать, что данное различие обусловлено избранным критерием – функцией системы или технологическими «наворотами». Те же самые предметы, но в иной системе отсчета, могут быть однородными, т.к. функциональные или технологические различия в ней не важны. В качестве примера такой системы я уже приводил опись имущества, принадлежащего конкретному человеку.

...

Вы опять читаете невнимательно, из сказанного берете только половину, а половину – упускаете. Над взятой половиной надстраиваете уже свои интерпретации и с ними спорите.

«Общепринятый смысл термина "идеальность", это, всё-таки, приближение к некоей идее, в нашем случае — идее решения некоторой задачи».

Процитирую еще раз определение идеальности, выделив пропущенные Вами места:

Идеальная система – это система, которой нет, а функция ее выполняется.

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

«Вы противоречите сами себе. Если уж система развивается, а "идеальность" тем выше, чем система меньше, то становится ясно, что система развивается всегда только в одном направлении — к не-идеальности».

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

Но схоластика потерпела фиаско на рубеже 16 – 17 веков. С наиболее аргументированной критикой такого подхода выступил Фрэнсис Бэкон в своей работе «Новый Органон».

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

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

Еще удобнее рассматривать системы в развитии. Например, современный автомобиль гораздо идеальнее своего первого прототипа, т.к. ездит быстрее, а топливо "ест" меньше. Т.е. выполняет функцию лучше, а ресурсов тратит меньше.