IT Notes

Пять типичных ошибок проектирования и программирования

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

Ошибка № 1. Слишком узкое имя класса или интерфейса

Не всегда получается предусмотреть направление развития программы и/или библиотеки. Это может привести к тому, что имя класса или интерфейса оказывается слишком узким для его фактической области применения.

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

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

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

См: Пример полиморфизма в C++ на основе ООП

Ошибка № 2. Неконтролируемое разрастание иерархий наследования

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

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

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

См: Профессия программиста: Абстрактное мышление

Ошибка № 3. Вызов функций не по смыслу, а из "удобства"

Иногда, когда поджимают сроки сдачи проекта, возникает соблазн что-то где-то срезать и упростить. Почти всегда это приводит к проблемам в будущем.

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

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

Ошибка № 4. Осознанное дублирование фрагментов кода

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

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

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

См: Принцип DRY в действии

Ошибка № 5. Неподходящие имена функций и переменных

Этот тип ошибок во многом пересекается с ошибками № 1 и 3, но все же я решил выделить его отдельно, поскольку между ними имеются концептуальные отличия.

Часто из-за спешки мы даем функциям и переменным не самые подходящие имена. Иногда это связано с неполным пониманием предметной области или с ошибками проектирования. Но это не оправдание.

Конечно, придерживаться 100%-ой чистоты тоже нет необходимости. В простом for-цикле вполне можно использовать переменную i, но чем больше область видимости, тем осмысленнее должно быть имя.

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

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

См: Принцип единой ответственности

Похожие публикации

Комментарии

Здравствуйте, не могли бы вы помочь мне разобраться с устройством работы программы которая будет удалять файлы из реестра.

Anonymous:

Здравствуйте, не могли бы вы помочь мне разобраться с устройством работы программы которая будет удалять файлы из реестра.

Здравствуйте. У Вас уже имеются наработки, в которых требуется разобраться? Или Вас интересует то, как создать некий упрощенный аналог regedit?

Наработки отсутствуют, по скольку я буквально вчера начал разбираться с єтим вопросом. По сути я хочу создать программу которая будет входить в регистр, находить запрашиваемый ключ и удалять его.

А просто удалить из командной строки нельзя? Без создания программ.

http://www.windowsfaq.ru/content/view/301/60/

Anonymous:

А просто удалить из командной строки нельзя? Без создания программ.

http://www.windowsfaq.ru/content/view/301/60/

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

Если вопрос еще актуален, то могу подготовить пример программы к следующей статье.

Да актуален будет интересная реализация =)

Anonymous:

Да актуален будет интересная реализация =)

Ок. Займусь в ближайшее время =)

Не подскажите ли, когда выйдет следующая статья?

Anonymous:

Не подскажите ли, когда выйдет следующая статья?

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