Archive for the 'Дизайн ПО' Category

Программерское: Мой топ признаков плохого кода

Собрал тут свой собственный список признаков факапнутости кода.

На первом месте – классы со словом “Manager” в названии. Безоговорочный лидер в номинации “превратим любой код в страшные спагетти”. Самое ужасное в таких классах это то, что делаются они из самых лучших побуждений. Впрочем, так всегда – самые кошмарные деяния совершаются под прикрытием лозунга “мы же хотим тебе лучшего”. Я специально не пишу о внутренностях этих классов, само название “Manager” для меня уже достаточное основание к тому, чтобы код полетел в помойку.

Второе место достается небезопасным преобразованиям типов. Ну, это когда в коде появляется преобразование от класса-предка к классу-потомку. Вроде этого

class A;
class B: public A;

void DoSomething(A* ptr)
{
(B*)ptr->MethodOfB();
}

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

Третье место – классы “универсальный всемогутор”.  Близкий родственник класса “Manager”, протащеный оным на ключевую позицию в проекте, способен отравлять жизнь двум-трем поколениям программистов, которым выпадет этот код поддерживать. Обычно характеризуются тучей публичных методов, никак не связанных по смыслу ни друг с другом, ни с названием и предназначением класса и не изменяющих и не использующих ни одного из свойств класса, но при этом зачастую имеющих тучу неявных связей друг с другом.

Четвертое место – преднамеренное нарушение энкапсуляции. Это когда в класс “бутылка пива” пытаются заодно вкрячить нечто, что вроде как должно научить экземпляры его автоматически упорядочиваться внутри класса “ящик пива”. Когда через неделю закономерно выяснится, что бутылки нужно упорядочивать в сикспаки, начинается любимый в народе аттракцион “аврал”. Есть вообще только один случай, когда объектам допустимо иметь какие-то знания о контейнере, в которые их будут складывать – это преднамеренно делается интрузивный контейнер. Во всех остальных случаях такой код – индикатор того, что code review можно прекращать прямо сейчас, иначе проблемы этот код начнет доставлять уже на следующей неделе. В общем случае – запихивание в классы внешних нерелевантных зависимостей есть большое зло.

Пятый признак – преднамеренное игнорирование или извращенное употребление паттернов программирования. Когда из-за страха перед великим и ужасным шаблоном “фабрика” образюутся иерархии, где один-единственный параметр конфигурации спускается каждым промежуточным классом вниз на десять уровней, чтобы там ему где-то делали if или switch..case, когда при программировании на C++ боятся использовать RAII или бьют по рукам за bind; или же, наоборот, пихают повсюду smart pointers (доморощенные, конечно же, бо boost – это слишком сложно), жди беды. Очень быстро кто-то посередине той иерархии забудет, зачем нужен этот мистический параметр, нигде в обозримых окрестностях не применяющийсяи “пофиксит” код, обеспечив остальных неделями отладки (ведь юнит-тесты для такого кода писать не принято – он же очень сложный!); неиспользование RAII рождает трудноуловимые дедлоки, а умные поинтеры дают вкусить циклических зависимостей по полной.

Шестой признак – использование наследования там, где нужна агрегация. Например, есть интерфейс классов, есть базовый класс, которые реализует 85% всей требуемой функциональности. Позднее выясняется, что функциональность реализована вообще на 100%, а единственное требуемое различие – это разное поведение в случае создания класса с определенными внешними параметрами. Например, в реальной жизни есть у нас автомобиль. Точнее, его кузов. Во всем мире все производители просто ставят разные двигатели на один и тот же кузов и обзывют это обидным словом “комплектация”. В мире программирования часто происходит закат разума за ум – вместо того, чтобы создать класс “автомобиль” со свойством “двигатель”, начинают городить иерархию “автомобиль А с двигателем Б”, автомобиль Б с двигателем А” и так далее, что вообще противоречит здравому смыслу. Еще бы, класс автомобиль оказывается наследником класса “двигатель”. Проблема тут в том, что разобраться во всем этом хитросплетении и потом поддерживать ее становится решительно невозможно. В то же время правильный вариант – создать нужный объект тпиа “двигатель” и передать его новому объекту типа “автомобиль” при инициализации- не только сэкономил бы пару тысяч строк кода, но и позволил бы унифицировать и протестировать все имеющиеся автомобили и двигатели отдельно друг от друга и даже попробовать впоследствии легко примерить движок от Мерседеса на Запорожец.

Пятисекундка ненависти

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

Юнит-тестирование. Всем чтить!

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

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

ППКС.

Код: до и после н.э. Зачем на самом деле нужны юнит-тесты.

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

БОльшая половина кода нашего проекта на первый взгляд выглядит очень хардкорно. Если опустить пару моментов, где я вдоволь оттянулся с белой и черной шаблонной магией, то бросится в глаза, что остальная часть этой половины идет вразрез с заветами секты “ООП”  и ее течения “многоэтажные иерархии классов” (да, я редко сажаю леса вроде приведенного на картинке ниже). В глаза бросится, да не долетит, а упадет и стечет (может быть даже прямо в ботинки).

Многоэтажная иерархия
При ближайшем рассмотрении

Deadlock for free или почему нельзя делать suspend потокам

Сегодня коллега напоролся на дамп программы, которая зависла. Такое бывает, но когда затык происходит на int* pInt = new int, Штирлицу положено насторожиться.

Налицо deadlock, но прикол в том, что в коде никакой синхронизации в принципе нет вообще. Зато была приаттачена куча всяких bounds-checker’ов вкупе с отладчиками, что заставило меня вспомнить, что я когда-то где-то читал о том, что  неосторожное обращение с менеджером памяти может привести к дедлоку.

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

Так вот, интернет уже неоднократно объяснил, что делать suspend потоку извне – очень, очень плохая идея. Есть миллиард объяснений, почему, но все они сводятся к общему знаменателю – поток сам распоряжается своими ресурсами и поэтому должен сам контроллировать свое поведение. Даже если на первый взгляд поток не использует никаких разделяемых ресурсов, при более пристальном рассмотрении это может оказаться совсем не так.

Любой поток делит как минимум один ресурс с остальными потоками. Оперативную память. Распределение памяти контроллирует менеджер памяти, который, как правило, общий для всех потоков, в определенных рамках, конечно. Соответственно, есть очень неплохие шансы, что при процедура выделения памяти синхронизирована внутри менеджера памяти, грубо говоря, менеджер имеет mutex внутри. А дальше все просто – Вася запустил поток, поток радостно поскакал по инструкциями и весело побежал за оперативкой. Менеджер памяти залочил было мютекс, а тут Вася бац! Suspend потоку! И тут же в другом потоке начинает ломиться к тому же самому менеджеру памяти, которому пофиг, ибо мютекс зажат другим потоком. И, заметьте, совершенно бесплатно приходит им всем, включая Васю, первый, второй поток и менеджер памяти злой дед лок.

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

Полезная шаблонная магия. Еще не singleton, но уже не global static.

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

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

Тогда просуммируем все наши хотелки примерно таким псевдонаучным выражением “необходим механизм, производящий автоматическую инициализацию единственного объекта определенного типа до первого обращения к нему и обеспечивающий доступ к данному конкретному экземпляру из любого места программы. Выявить ограничения механизма.”.
Читать дальше

Что не так в TFS.

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

Сразу отмечу, что я в основном использовал в своей работе CVS и Subversion, а также немного потыкал палочкой VSS, чтобы убедиться, что он и правда мертв, посему некоторые приколы, которые я упоминаю, могут быть и не проблемами вовсе. Так, дело привычки.

Ну, обо всем по порядку.

Закон Мерфи

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

Не зря я тестирую даже “хелловорлды”, не зря.

В код мне баги! (С)

Готовимся к релизу продукта.

Так получилось, что я присоединился к команде где-то на середине процесса разработки и сразу огнем и мечом пинками ввел практику юнит-тестирования кода.

Сейчас функциональный код завершен полностью, настало время тестерам оторваться. Что получается? Правильно, 99% найденных багов – в коде, который был написан до моего прихода, то есть не был покрыт юнит-тестами.

(Оставшийся процент багов относится к различиям в формулировке ТЗ и тем, что на самом деле имелось в виду).

…тот в цирке не смеется.

Читаю спецификацию к одному очень промышленному формату. Спецификация солидная, 114 страниц, на двух языках. На 15 странице натыкаюсь на буквально следующее:

binary data
data organized in the form of bytes
NOTE The 8 bits in a byte can be organized in 256 different combinations. They can be used, therefore, to
represent the numbers from 0 to 255. If larger numbers are needed, several bytes can be used to represent a single
number, e.g., 2 bytes (16 bits) can represent the numbers from 0 to 65535. When the bytes are interpreted in this
fashion, they are known as binary data. Several different formats are in common use for storage of numeric data in
binary form.

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

Кстати, если вымарать весь подобный идиотизм оттуда, то описание формата влезло бы на половинку листа А4. Но за такой несолидный манускрипт денег брать, наверное, не получится :)

Там еще два абзаца о битах есть.




7 visitors online now
7 guests, 0 members
Max visitors today: 8 at 01:20 pm MST
This month: 13 at 03-09-2010 02:06 am MST
This year: 41 at 01-23-2010 03:43 am MST
All time: 41 at 01-23-2010 03:43 am MST