Комментарии 5
Производительность. Оптимизация. Рефакторинг. Как много в этом слове…
Интересно! Но, если бы статья сопровождалась бы еще демо-примером, то цены бы ей не было.
Большинству из нас рано или поздно приходится сталкиваться с задачами оптимизации ПО.
Приходится! Мне, вот, пришлось задуматься об оптимизации и рефакторинге собственной обучающей (иностранным языкам) программе «L'école».
Дело в том, что, я себе чуть мозги не сломал, пока не довел её до, более-менее, рабочего состояния. Но, только теперь, через полтора года публикации ее первой версии, я, наконец-то, дозрел до размышлений об ее оптимизации, рефакторинге и расширению возможностей.
Основные шаги проведения оптимизации производительности
Определить цель / параметры оптимизации
Получить / подготовить эталонные тестовые случаи
Провести тесты производительности и зафиксировать результаты
Определить проблемные места с точки зрения производительности
Определить изменения, их стоимость и выигрыш от их применения
Выбрать и реализовать изменения
Проанализировать результат. В случае необходимости, повторить .. Сontinue everything / непрерывное совершенствование и вот это все
В целом звучит красиво, но, малополезно, в моем случае. Производительность меня устраивает и в первой версии, а во второй я хотел бы получить:
Расширение функционала и
Понимание, как «правильно» делать проекты подобного рода, чтобы не забыть их структуру и программную логику, несколько месяцев спустя.
Вот последний момент мне и кажется наиболее важным, в данном случае.
После некоторых размышлений, пришел к выводу, что прототип программы должен разрабатывать архитектор программы. Это должен быть работающий минимальный код, в котором весь потенциальный функционал реализован в виде формальных классов – модулей. В данном случае я имею в виду оконные приложения на С++.
Все формальные классы представлены своими публичными функциями, которые ничего не возвращают (обмен данными происходит через их параметры), за исключением обработчиков событий. Те возвращают стандартные значения LRESULT (TRUE либо FALSE), но, как и все остальные публичные функции, имеют пустые тела.
Приватные части автономных классов (на уровне файлов это *.h и *.cpp модули) находятся в компетенции членов команды.
Далее, архитектор программы фиксирует вызовы формальных публичных функций в «модуле управления» (для оконных интерфейсов это, как правило, обработчики класса типа CMainFrame либо CMainWindow). Но, поскольку, эти функции – пустые, то их использование никак не проявляется.
На примере моей обучающей программы, основными, независимыми друг от друга модулями, являются: класс для работы с чтением / записью текущего состояния приложения, класс логирования, классы для работы с графикой, текстом (отображение и редактирование), звуком и данными, классы вспомогательных окон («О программе», «Помощь», «Настройка» и прочее) и т.д. и т.п.
Для всех этих классов архитектор дает их формальную реализацию. Затем раздает копии своего проекта всем членам команды и говорит, кому, какой модуль реализовывать. Такой подход позволяет достаточно просто организовать коллективную работу без использования внешних систем контроля версий, вроде, гита.
Члены команды доводят до ума свои модули и передают их архитектору. Он замещает формальные модули – их полными версиями и тестирует общую реализацию. При необходимости вносит коррективы в работу команды.
При таком подходе члены команды могут работать удаленно и иметь минимальный конфликт интересов.
Я даже не знаю, нужен ли, в таком случае, тимлид либо техлид. Наверное, да, для больших проектов. Но, для группы из нескольких программистов, думаю, без последних можно обойтись. В таком случае, архитектор «пишет» код прототипа либо ваяет его по принципу конструктора модулей.
Таким образом, используя модульное программирование либо итерационно-модульное (где предыдущие итерации, собственные для «своего» модуля каждого программиста, фактически являются просто бэкапами), можно, как мне кажется, упростить работу команды. Зато, основная нагрузка ложится на архитектора. А члены команды могут даже работать независимо друг от друга.
Однако, возможность «легко подключить модуль к проекту» (путем простой замены формальной реализации – полной) и «легко отключить модуль от проекта» (соответственно, наоборот, заменяя полную реализацию – формальной), позволяет, со временем, накаливать потенциальные модули для будущих проектов. Что дает возможность быстро создавать новые прототипы для новых проектов, аналогичного типа.
По сути, в идеале, мы получаем этакий конструктор для будущих программ.
Например, у меня есть неопубликованная программа «МедиаТекст».

http://scholium.webservis.ru/Pics/MediaText.png
Вместе, эти две программы, явно, могут иметь общие модули. При этом, в последней используются потоки и опенсорсный видеоплейер, для которых можно создать свою формальную реализацию.
Вот, приведение уже только этих двух программ к общему модульному типу (к ним можно добавить и мою третью программу – загрузчик «MiniDL» для любимых видосиков, которая опубликована здесь, на «Хабре»), позволит создать хорошую базу прототипов для будущих проектов данного типа задач. После чего можно будет уже спокойно предлагать себя в роли архитектора, на похожую работу :) .
Но, даже если программировать только для себя на уровне пет-проектов, то данный подход кажется перспективным, по крайней мере, я перестану забывать программную логику своих давно написанных программ и смогу спокойно модернизировать их до новых версий. Чем и собираюсь заняться, в ближайшее время.
Как-то так.
Есть неплохая книга "Фундаментальный подход к программной архитектуре" , Марк Ричардс, Нил Форд.
Могу порекомендовать.
К.м.к. на часть ваших вопросов она ответит
Есть неплохая книга "Фундаментальный подход к программной архитектуре" , Марк Ричардс, Нил Форд.
на часть ваших вопросов она ответит
Скачал книгу и посмотрел. Сразу наткнулся на фразу:
Девяносто пять процентов слов [об архитектуре программного обеспечения] посвящено преимуществам «модульности», но при этом почти ничего говорится о том, как ее можно достичь.
Вот, об этом то и речь. Я же даю и определение и способ ее достижения (применительно к оконным приложениям на С++ / WTL, под «Форточки»).
Далее пошло теоретизирование и общие слова о модульности «вообще», что, конечно, малоинтересно.
На рис. 9.1,и 23.1 там показана «визуализация архитектуры в стиле Большой ком грязи на примере реальной кодовой базы». Это типичная связь: «многие ко многим». Как минимум, её нужно преобразовать к связи: «многие к одному» и «один ко многим». У меня самого, первая версия программы организована также, что напрягло мои мозги до максимального предела. Именно в этом ключе я и работаю, с целью упрощения ее архитектурной логики.
Теоретически, меня должна была бы заинтересовать глава 14: «Архитектура, управляемая событиями», но, там она представлена на «нижнем» уровне, тогда как уже есть уровень WTL, который скрывает всю «скучную» рутину и дает механизм управления событиями на достаточно высоком уровне, из-за чего я просто обожаю эту библиотеку кода (занимаемую, в исходниках, менее полутора мегабайт!), А подход в книге предлагает мне всю эту нижнееуровневую программную логику реализовывать самому. А оно мне надо? При этом, все «конкретные» примеры оттуда, на самом деле – слишком абстрактные.
Короче говоря, книга интересная, но только для общего развития. Для конкретных задач, вроде моей: «Организовать переход от первой версии программы «L'école», ко второй, путем оптимизации сильных связей: «многие ко многим», между модулями основных классов, к более слабым связям: «многие к одному» и «один ко многим» плюс добавить новую функциональность к проекту».
Так что, за ссылку спасибо, но ускорить изобретение собственного «велосипеда», она, к сожалению., не поможет.
С учетом того что при оптимизации сложных систем зачастую (~ в 90% случаев) все указанные автором параметры "оптимизируемой" системы взаимосвязаны, "легких" случаев (типа "вот тут поправить параметры/код и получить +20%") практически не бывает, хотя и есть исключения. :-)


Производительность. Оптимизация. Рефакторинг. Как много в этом слове…