Показаны сообщения с ярлыком Архитектура. Показать все сообщения
Показаны сообщения с ярлыком Архитектура. Показать все сообщения

среда, 27 декабря 2023 г.

Философское. Про XML и боксёров

Вспоминается ВМК в далекие 2000-2001 года... В частности курс про распределенку вперемешку с этими нашими интернетами...

Как же тогда восторженно дрочили на XML! Ах, еще один markup language, ах как клёва, ах как круто. И все я никак не мог вникнуть, с чего такие оргазмы. Ну еще один, ну еще один markup language, ну и чего? Мало ли что ли их? Есть SGML, SHTML, есть в конце концов сам HTML - и все они *ML - markup language. Чего восторгаться-то!?!

Пока кое-кто из толковых разработчиков не ткнул меня носом и показал на деле, на что нужно возбуждаться. А именно на eXtensible markup language. А вовсе не на markup language.

Вот тут-то я всё и прочувствовал! А некоторые из наших преподов как-то не на то упор делали: всё markup да language, language да markup.... 

Вот так-то и вычисляют боксёров-теоретигоф. Гы! Фишка XML`я именно в eXtensible, причем стандартизованном.


пятница, 7 октября 2016 г.

Хозяйке на заметку : многопоточность.

Пейте, курите, занимайтесь любовью… Но ни в коем случае никакой многопоточности!

Note Bene: особливо на этапе проектирования!
Ярлыки для себя на будущее: Aml Pages, асинхронный импорт рисунков веб-страниц, многопоточный причем, мудренная синхронизация… Ни-и-и-ичче просто не понадобилось.

суббота, 25 апреля 2015 г.

Костыль памяти. Раз, два, три, много

Есть такое мифическое племя — мумбу-юмбу. Так вот у них очень интересная система счисления. Она не похожа ни на распространенную арабскую, ни на римскую, ни на не в коем разе какую-нибудь шестнадцитиричную. Она проще — и называется Раз, Два, Три, Много. Вот вам и все порядки, и арифметические операции.

Дык вот в разработке софта владение этой замечательной системой экономит ну просто уйму усилий, времени, и денег. Просто нужно никогда не забывать про Раз, Два, Три, Много. Если есть раз, то может быть и два, а если два, то может быть и три. И уж тогда совершенно точно будет и много.

Этот пост для меня самого. Ну случай очередной амнезии. Костыль памяти.

пятница, 3 февраля 2012 г.

Про сериализацию в MFC

Сегодня про сериализацию, в частности в библиотеке MFC. Описанные ниже приемы — мой личный, опробованный на собственной шкуре опыт. О таком в MSDN, да в книгах не пишут. А зря, мль, зря… Почти все приемы выстраданы в конкретном проекте — в частности в Aml Pages. И большая их часть в ней и реализована. А некоторые нет, и я искренне весьма дюже сожалею о тяом, что допер до них слишком поздно.

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

Итак, MFC поддерживает класс CArchive для сериализации, который позволяет писать-читать из файла (хотя и не только), практически любые типы данных. Все стандартные классы MFC легко читаются\пишутся конструкциями вида CArchive<< и CArchive>>. Грабли начинаются уже потом, при развитии проекта. Именно когда объекты абстрактной софтины получают новые сериализуемые поля, или строковые данные могут быть в разных кодировках, или еще как.

1. Используйте номер версии архива

Используйте версионность CArchive. Смотреть на макрос IMLEMENT_SERIAL+VERSIONABLE_SHEMA. Суть проста: записывайте в CArchive сначала номер формата архива, а только потом сами данные. В дальнейшем это позволит сделать чтение архивов совместимым снизу вверх. Если в первой версии архива (файла), у нас записывались int+CString, а во второй int+CString+еще_int, то сериализация будет выглядеть так:

… Serialize(CArchive& ar)
{
if (CArchive::IsStoring()) {//запись архива
ar<<2;//записали номер формата — в данном случае вторая версия архива
ar<<My_int;//понеслась запись данных
ar<<myString1;
ar<<My_Int2;
}
else {//чтение
int nArchiveFormat;
ar>>nArchiveFormat;//читаем номер архива
ar>>My_int;//первый int
ar>>myString1;//первый CString
if (nArchiveFormat >=2)//если мы читаем архив второй версии, то считываем второй инт. Если архив второй или старше  версии, то читаем второй int — иначе пропускаем.
ar>>My_Int2;
}
}

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

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

2. Сериализуйте BOOL-переменные как битовую маску

Если в сериализуемом объекте у Вас есть набор булевых (BOOL)переменных, некоторых флагов, описывающих какое-то состояние, то не стоит их писать в архив последовательно. Не стоит писать нечто вроде CArchive<<boolFlag1<<boolFlag2<<boolFlag3; И дело тут вовсе не в экономии байтов.

Представьте себе, что в следующих версиях какие-то булевые переменные исчезнут из объекта, а какие-то добавятся. Соответственно, должен и измениться формат архива.  Вот блин, досада. И только из-за этой мелочи, как советовал выше, придется городить новый формат архива? Который не прочтут старые версии? Ан фиг! Эту проблему можно обойти. Решение простое: записывайте все флаги единой битовой маской. Нечто вроде такого:

DWORD dwBitMask=0;
enum {FLAG1=1, FLAG2=2… и.т.д.};
if (boolFlag1)
dwBitMask|=FLAG1;
if (boolFlag2)
  dwBitMask|=FLAG2;

и уже именно битовую маску dwBitMask записываем в архив. Какие плюсы? Обратите внимание, что во первых мы обошли проблему версий архивов. Появление или добавление нового флага BOOL не влияет на версию архива — все равно у нас пишется и читается DWORD.

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

Согласитесь, движение в сторону совместимости сверху вниз это уже неплохо. У этого приема есть пара деталей, но это уже подробности и если нужно опишу их в другом посте. Как использовать битовую маску для хранения в объекте в runtime писать не буду — не об том пост.

3. Не сериализуйте строки как есть

Не реализуйте сериализацию строк, как предлагает в примерах по MFC. Не используйте в лоб запись и чтение вида CArchive<<myString и CArchive>>myString. Поясню почему. CString в MFC инкапусулирует или Unicode-строку, или ANSI-строку — в зависимости от настроек компиляции (MBCS|Unicode). Поэтому стандартная сериализация полностью полагается на внутренности CString. А оно нам надо? Всё, робяты! 90-ые давно прошли — на дворе 21-век. Нет просто строк, есть последовательности с завершающим нулем. Но вот в какой кодировке могут быть конкретные строки — выбирай на вкус. Хотите ANSI, хотите UTF8, хотите Unicode.

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

Ну вот, собственно, некоторые несложные приемы сериализации. Есть конечно и еще детали, и еще подробности. А деталей хватает. Начиная от корректной реакции софта на попытку прочтения новой версии архива, и заканчивая, ситуцией, когда MFC-программа, скомпилированная как MBCS читает архив записанной UNICODE версией, или наоборот. Есть там нюансы, и именно в них весь дьявол. Но о них как-нибудь в другом посте.

понедельник, 13 декабря 2010 г.

Мысли про деревья и не-деревья

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

Вот чем больше смотрю на LeaderTask, тем больше задумываюсь, ну на хрена там деревья проектов!?! А ведь не только смотрю, я ведь еще им и пользуюсь. Причем давно, и вообще говоря с удовольствием. В принципе среди перепробованных органайзеров, LeaderTask, пожалуй, лучший. И чего в LT только нет: и категории, и сроки задач, и напоминания, и контакты. Буду краток (©). LT – это именно органайзер, в смысле слова “организатор” – то бишь софтина для планирования дел и контроля их выполнения.

В LT есть возможность создавать дерево проектов, и подпроектов. К примеру, “софтина такая-то”, “домашние дела” ну и.т.д. Но в подавляющем большинстве случаев, мне, как пользователю, достаточно затруднительно привязать задачу к тому или иному проекту. Скажем, надо мне нечто анонсировать в RSS ленте про “софтину такую-то”. Ну и куда мне сие отнести? К проекту “софтина такая-то” или к проекту “веб сайт”? ОК, конечно, можно привязать задачу к обоим проектам сразу, но тогда становится затруднителен анализ запланированных дел. Копаемся в проекте “софтина такая-то”, а находим записи про RSS для проекта “веб сайт”. А они там явно не ко времени, т.к. вроде как анализировали мы совершенно иное, а вовсе не задачи по работе с веб сайтом... По крайней мере, такой разнобой должен все-таки отвлекать (анализируем же? планируем? а не бардак перебираем?)

К слову, деревья это явно наше родное, программерское изобретение. Чего уж тут лукавить. Ну любит наш брат разбить всё и вся на деревья, на общее и частное, на абстракции и конкретику. Чувствуется что-то до боли знакомое. Это наше всё. Но тут-то всё как раз ясно. На самом деле иерархии помогают нам управляться со сложностью, бороть и побеждать (“лажать, но не сдаваться” если в несколько в другой интерпретации :).

Но вот обычному пользователю частенько все эти шибко ветвистые, вложенные деревья не так уж надобны. Ну не представляю я себе пользователя, у которого в голове выстраивается что-то вроде такого дерева: “шкаф”, “полка вторая”, “папка слева”, “запись об RSS”. Как водится всё несколько зауряднее: папка с делами, и папка с порнухой фотками. И всё! И нет никаких вам “деревьев”. И никакой гипер-пупер-супер вложенности тоже нет. Для анализа, поиска содержимого подобных папок нормальному, широко распространенному пользователю вложенность вовсе не нужна. Ну максимум один-два уровня. А это уже не дерево, максимум “кустарник”.

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

Это вовсе не критика LT как такового. Фиг ли критиковать? Есть старинная поговорка: “не нравится – сделай лучше”. Мне вот лично LT очень сильно помогает. Но проблема деревьев коснулась и его. Повод задуматься.

В чужой софтине, мне, как обычному пользователю, далеко не очевидны все приемы работы. Поэтому и “сбокуприлепленность” деревьев выглядит для меня более “выпукло”. Проблема-то явно остается значительно более общей. Вот в своей собственной Aml Pages я точно также снова задумываюсь: “ну на хрена мне эти деревья”. Но в своей собственной софтине у меня есть и еще вагон и маленькая тележка вариантов использования, поэтому, там я всегда найду способ без деревьев и обойтись. Но ведь должно же существовать и какое-то иное решение подобных проблем? Есть же способ работы с карточками – записали мысль, отложили, и забыли. Задача быстро записать, и с глаз долой, из сердца вон (угу, тот самый мозговой штурм). Уже потом берем стопку карточек и просматриваем, выбирая себе задачки, группируем, оструктуриваем их всеми мыслимыми и немыслимыми способами.

Но ведь должно же быть, какое-то толковое и более менее общее решение!?! Вот его и хочется отыскать! Если с LeaderTask, как с чужой программой, меня в первую очередь волнует решение для себя любимого. То с Aml Pages,приходится задумываться об общих, куда как более абстрактных решениях. Что делать с LT, вроде как понятно: есть API плагинов – время найдется, напишу себе именно то, что именно мою проблему решит. А вот с Aml Pages все не так-то просто. И главное, подобная дилемма занимает голову далеко не первый месяц. И забил бы давно, дык ведь любопытно же решить…

PS: а еще бредилось про графы… Диплом все ж про графы был. А когда-то был умысел написать с-о-о-о-всем иной, то бишь напрочь отличный от всех остальных менеджер контактов (угу, блин, я в курсе, что все девелоперы на определенной стадии писали записные книжки :). Дык в тех “контактах” тоже все на графы было завязано… Уж больно енти “графья” завсегда по жизни рядом “вращаются”. Ну да это уже совсем отдельная песня.

Technorati Теги: ,,

среда, 2 декабря 2009 г.

Про баги: падать или не падать

Здравствуй, дорогой и горячо любимый Access Violation Елена Сагалаева начала серию статей “Искусство отладки” и даже уже опубликован первый пост “Как предупредить появление багов”. Тема конечно же глубинная. Ре-хех, чуется да будет драка… Да чего уж там, судя по комментариям “гнать из команды”, “не пишем код и не будет багов” – драка уже началась! Ну, как говорится: “amen. понеслася!”. Но посты прелюбопытнейшие, даже порой заглядываю перечитать и поразмышлять. Чего уж тут!?! Достали меня уже в ноль анализы засранных бизнес-требований (сами не знают, чего хотят), бесконечные флеймы в форуме технической поддержки, и все прочие маркетинги-и-иже-с-ними. Давайте-ка лучше маленько подебажим :)

Хех, баги есть, были и всегда будут. А кончатся? Не пропадем – съездим к трем вокзалам и возьмем наплодим новых! Когда баги вползли по невнимательности, ну или по банальной опечатке (такое бывает: начинаю уже тихо материться на автозавершение кода (autocompletion), о  сколько багов оно наплодило в моем коде) - с этим более менее понятно как бороться: code review, нормальные комментарии, assertions где только возможно. В общем, все понятно: баги подобного рода нужно максимально переловить на стадии альфа (бета) релиза.

Но ведь бывает и иначе: “Ты суслика видишь? Нет… А он есть!”. То бишь, ну неоткуда багу взяться, хоть тресни неоткуда и всё тут! Но он есть. И хорошо, если четко диагностируемый и постоянный, а если мигающий да зависящий от конфигурации!?! Imho, в альфа-версиях лучше уж пусть софтина рухнет, грохнется с самым поганым Access Violation, погребя под собою весь процесс вместе с пользовательскими данными заодно. Ну и ладно, зато проблема станет явной.

Буквально последние дни доделываю новую фичу в Aml Pages. Фича весьма неслабая. Мало того, что вовсю используются виртуальные списки (SysListView), когда данные в одном месте, а визуализация их происходит по мере прокручивания списка пользователем. Дык там еще и сами данные выдергиваются многопоточно (а юзер – нет, ну вот зараза какая – старается еще и изменить данные уже в третьем треде). Лично по мне, пусть вся Aml Pages рухнет с треском в релизной сборке, если где-то вдруг у нас порою закрался баг. Всё лучше, чем кряхтя и поднатужившись продолжит выполнение, непонятно в какой винегрет превращая пользовательские данные.

Нет, нет! Никто не собирается вывалить все эти баги на пользователя. Ни в коем случае. В коде уже очень многое предусмотрено: и контроль индексов, и корректная обработка висячих указателей, и проверка на deadlock при многопоточной обработке. Но все это будет задействовано только в первом публичном бета-релизе. В альфа-версии фиг – все максимально сбое-вероятно, пусть уж лучше мой код покажет свое истинное нутро.

Всё просто: проблема не в баге  - проблема в том, откуда он вообще взялся. Если написано, что индекс должен быть от массива “А”, то именно для него он и должен быть, и причем валидным, а не для соседнего. А коль индекс не валидный, то вынуть по нему данные все равно не получится. Выкрутиться можно, но вот вынуть данные корректно, это фиг. В публичном релизе будет и такая страховка. Но проблема в данном случае, не в том что пытаемся вытащить данные по кривому индексу, а в том, откуда такой кривой индекс вообще взялся.

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

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

  1. Банальное переименование некоторых функций. Причина проста: семантически параметры некоторых функций изменились, но не изменились синтаксически. То бишь надо все эти, а желательно еще и соседние, моменты проверить. Но компилятор сам такие ляпы не поймает, т.к. ну нет никакого ляпа с его точки зрения. И что делать? Шерстить глазами? Дык не годится – человеку, и его глазам в частности, свойственно ошибаться. А коли свойственно, то ошибка допущена будет.
    Делаем просто: переименуем старую функцию, ну скажем, GetDataByIndex в нечто вроде GetDataByIndex2 и перекомпилируемся. Компилятор, ясное дело ругается на все вызовы функции по старому имени. Аккуратно просматриваем всю “ругань”, не спеша и тщательно проверяя семантику параметров. Приводим код в полную работоспособность. Ну и под конец, не грех переименовать GetDataByIndex2 во что-нибудь более говорящее, вроде GetDataByArrayIndex чтобы в будущем не путать, что за индекс.
  2. Везде где можно применяются assertions: т.е. проверяется истинность почти всего подряд: висячесть указателей, выход индексов за пределы массива, pre- и post-условия выполнения. В отладочном коде при неверности условия, будет отрабатывать отладочный код, а хоть бы и жутко “матерный” MessageBox. В релизной сборке таких проверок нет, и тем более нет этого “матерного MessageBox`а”. Но даже если что-то было и упущено, то в релизной сборке программа просто рухнет, и проблема станет явной. Чего и добивались – чтобы найти решение проблемы, о ней хотя бы нужно знать. 

Собственно всё! Уже только этих два приема сходу выловили с пяток косяков точно. Остальные ждём-с в релизной сборке – нехай падает.
Имхо, в тестовых версиях баг это благо – он повод задуматься “а с какого-такого перепуга”. Качество и надежность кода – это задача финальных версий. Но без вопроса “с какого перепуга” эта самая пресловутая надежность кода никогда не будет достигнута. Нет вопроса – не будет и ответа.

PS: в комментариях к первому посту Елены упоминал книгу Роббинса по отладке – собственно вот она на Books.ru. К сожалению, в свое время, так и не удалось найти самую первую “отладку” Роббинса. Но, у него есть еще одна любопытная книга: “Отладка Windows приложений” – тоже вещь впечатляющая, а главное и актуальная по сей день. Имхо, must have обе. Но… блин, первую отладку похоже не скоро переиздадут, а жаль – солидное чтиво – мозги на нужный лад настраивает.

PPS: стоп! А как же самый главный пост-скриптум!?! Елене отдельное СПАСИБО за интереснейший цикл статей. Так держать!

вторник, 17 ноября 2009 г.

Все сразу получить невозможно

AVL начал переводы “97 Things Every Software Architect Should Know”. Это подборка статей об архитектуре программного обеспечения. Статьи небольшие: одна статья – один аспект, поэтому читается легко, здесь вам не академические мануалы в стиле Фаулера.

Как и обещал AVL, все сразу получить невозможно. Но… Можно даже сказать но! AVL слово держит и новые переводы выдает более чем регулярно: буквально каждые 2-3 дня появляется новый перевод, причем 2-3 дня перерыва это максимум, частенько и по одному переводу за день!

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

PS: давно собирался написать про инициативу AVL`а. Но как-то руки все не доходили! А тут аккурат последний перевод “Все сразу сделать невозможно” привел в чуйство от излишнего запала в разработке некоторых новых фич в Aml Pages, ибо нехер нефиг (а то чую, определенный нездоровый троллизм начал меня доводить до ползучего фичеризма).

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