Чтение онлайн

ЖАНРЫ

Дефрагментация мозга. Софтостроение изнутри
Шрифт:

Журнал хозяйственных операций

Отвлечёмся ненадолго от общих концепций и рассмотрим более конкретные проблемы разработки учётных приложений. Вопросы, подобные «почему нельзя хранить остатки в форме текущих величин», «зачем нужна история операций», «зачем там нужна транзакция в режиме «сериализация» (что такое режим serialized можно прочитать в статье [20]), не раз всплывали в дискуссиях, поэтому я кратко расскажу об этом в рамках отдельной главы, чтобы в следующий раз просто ссылаться.

Довольно сжатое изложение статьи «Как проектировать бухгалтерию»[19] в терминах абстракций может быть не очень понятным начинающим. Напротив, статья «Введение в складской учёт»[18] рискует показаться излишне упрощённой и «заточенной» на складскую бухгалтерию с отраслевой спецификой. И в обоих случаях не хватает конкретики, разъясняющей вышеназванные вопросы.

Начнём с первого антишаблона «Таблица остатков», на которые я вдоволь насмотрелся во времена

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

Представьте, что в документ недельной давности вкралась ошибка. Её исправили, причём пересчитанные текущие остатки по-прежнему неотрицательны. Значит ли, что они неотрицательны и на каждый день прошедшей недели? Разумеется, нет. Приходит клиент и говорит: «Мне выписали 10 штук, а на складе только 8, я на вас, жуликов, в суд подам». Парадокс? Никакого парадокса. За товаром он пришёл сегодня, но продали ему товар вчера. А на состояние «вчера» после корректировки остаток был бы отрицательным. Вот ему и не хватило.

Если не верите, что такое возможно, вот схема движения.

Теперь откорректируем приход 2012-04-01 с 12 утюгов на 10. Получаем, что на сегодня их 0. Вроде бы все в порядке. Всё, да не совсем: сегодняшняя закупка ещё не поступила на реализацию. Поэтому вчерашний «минус 2» пока действителен.

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

Но считать по журналу:

• может быть долго;

• необходимо защитить считанные значения, чтобы при последующей записи не возникло «минусов».

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

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

Если же ваши операции расчёта, проверки и расхода работают в одной транзакции на уровне изоляции «сериализация», то такая ситуация исключена. «Продавец 2» из примера будет ждать, пока «Продавец 1» закончит операцию и в свою очередь убедится, что утюгов уже не 10, как было на экране в момент заказа, а только 5.

Почему нельзя просто блокировать всю таблицу-журнал, а надо использовать какие-то хитроумные транзакции с непонятным уровнем изоляции?

Объяснить это тоже просто. Представьте, что один продаёт утюги, а второй – гладильные доски. Если заблокировать таблицу, то второй продавец всё равно будет ждать первого. Всегда. И вообще, все и всегда будут ждать одного, подобно очереди в общественный туалет с одной кабинкой. Кстати, именно эта метафора наиболее употребительна при объяснении работы механизма бинарного семафора – мьютекса [111] .

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

Рассуждая, мы плавно подошли к вопросу «Зачем хранить историю остатков?». Действительно, ведь есть журнал, всё можно посчитать по состоянию на любой период.

Вернёмся к примеру корректировки документа недельной давности. Таблицы остатков у нас уже нет, а мы должны рассчитать и проверить на «минус» все дни за последнюю неделю. Допустим, ваша программа правильная, использует соответствующие транзакции и считает остаток от операций одного дня примерно за 500 миллисекунд. Умножим на 7, получим, что с большой вероятностью примерно 3,5 секунды пользователи учётной системы будут ожидать окончания вашей операции.

Это, мягко говоря, нехорошо. К тому же, если ваш расчёт не написан на языке СУБД – SQL, то цифра в 500 миллисекунд явно завышена в разы.

Придётся нам возвращаться к «таблице» остатков. Но совсем не к такой таблице, что была вначале, а к новой, называемой «сальдо» или «итогами». Мы добавим туда ещё один важный разрез – период. И будем поддерживать остаток на заданный период в актуальном состоянии. Тогда ваша программа просто должна попытаться отменить операцию и посмотреть, нет ли в сальдо «минусов», начиная с даты аннулированного документа. Здесь тоже возможна блокировка, но, во-первых, менее вероятная, а во-вторых, более быстрая, так как мы просто

производим вычитание количества от уже рассчитанных остатков, начиная с даты вместо полного пересчёта по журналу операций.

Как поддержать актуальность остатков? Скорее всего, триггером (кто сказал «материализованные виды»?). Это будет весьма критичный код в вашей системе, поэтому придётся, не побоюсь этого слова, «вылизывать» его так, чтобы он выстреливал за единицы миллисекунд. Дам совет начинающим: учите сиквел, транзакции, уровни изоляции, и будет ваша система быстрой и надёжной. И не только в примере с учётной системой, но и вообще по жизни. Опытным же разработчикам есть поле для оптимизации и дальнейшего совершенствования решений, включая альтернативные подходы.

UML и птолемеевские системы

Для того чтобы было легче подступиться к теме унифицированного языка моделирования UML, уже упоминавшегося в предыдущих главах, необходимо сделать небольшое историческое отступление [21].

...

Что же заставило учёных отказаться от системы Птолемея? Ответ с позиций современной теории познания мы видим в следующем. Система Птолемея описывала движение планет, видимое с Земли, то есть описывала явление. Если же мы оказались, например, на Меркурии или Марсе, то земную птолемеевскую систему нам пришлось бы упразднить и заменить новой.

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

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

В UML должно настораживать уже самое первое слово – «унифицированный». Не универсальный, то есть пригодный в большинстве случаев, а именно унифицированный, объединённый. Силами небольшого количества экспертов, если точнее, сначала двух, позже к ним присоединился третий, был создан сборник, содержащий наиболее удачные нотации из их собственной практики. Сборник стали продвигать в массы, а за неимением лучшего, и в стандарты.

Стандарт получился несколько странным. В его названии присутствует слово «моделирование». Модель от картинки в графическом редакторе отличается наличием базового формализма, позволяющего проверить созданную конструкцию на непротиворечивость, а если повезёт, то и на полноту. Очень простой, но верный признак проблемы – отсутствие в инструменте моделирования команды «Проверить». И если условной кнопки «Check model» в панели не предусмотрено, то ваш CASE [112] является просто продвинутым редактором специализированной векторной графики с шаблонами генерации кода.

Для сравнения, в IDEF0 [113] проверка целостности имеется, что не даст аналитику нарисовать в своей модели возникающие из пустоты и пропадающие в никуда объекты. Концептуальная модель данных в любой нотации, ER [114] или IDEF1х, также обратит внимание проектировщика на «висячие» сущности, циклические связи и прочие моменты, скорее всего, являющиеся ошибкой. На уровне логической модели данных (реляционная) к вышеназванным проверкам присоединятся новые, например, связанные с ограничениями типов (доменов), наличия ключей и другие. UML работает на логическом уровне.

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

Наконец, главное слово, «язык». Графические образы – это, конечно, аналог слов, но не всякий набор слов является языком. Поэтому UML до языка ещё далеко. Язык, даже естественный, можно автоматически проверить на формальную правильность. Входящий в UML OCL хоть и является языком, но выполняет лишь малую часть работы по проверке, и не собственно модели, а её элементов. А о способностях проверить модель написано выше.

Всё сказанное не должно вас смущать и поражать новизной. Ведь и XML тоже не язык, несмотря на соответствующую букву в аббревиатуре, а технология создания языков пользователя на базе разметки и формальных правил её проверки посредством DTD или схем.

Итак, UML:

• не универсальный, а унифицированный, объединяющий отдельные практики;

• не язык, а набор нотаций (графических);

• не моделирования, а в основном рисования иллюстраций, поясняющих текст многочисленных комментариев.

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

Цель – использовать «как есть». Не заниматься вопросами целостности, ограничившись рисованием частей системы в разных ракурсах. Если повезёт, то часть кода можно будет генерировать из схем.

Цель – использовать для моделирования и генерации кода. Придётся создать свои формализмы, соответствующие моделируемой области. В самом минимальном варианте – использовать в принудительном порядке стереотипы и написанные руками скрипты и ограничения для проверки непротиворечивости такого использования. Чтобы, например, стереотип «Водитель» в рамках ассоциаций «Управление машинами» был связан только с классом, реализующим интерфейс «Транспортное средство».

Поделиться с друзьями: