Дефрагментация мозга. Софтостроение изнутри
Шрифт:
Кстати, ошибка в данном примере означает, что книгу, между нами говоря, вы попросту спёрли, а интерфейс библиотеки позволяет это легко сделать.
2. Обратиться в справочную службу библиотеки и попросить их прислать копию нужной страницы.
//Правильно:
читатель. Реферат. Цитаты. Добавить(библиотека. КопироватьСтраницу(код,
номер_страницы));
//Ошибка:
читатель. Реферат. Цитаты. Добавить(библиотека. Книги(код). Страницы(номер_
страницы));3. В библиотеке книги не оказалось, купить книгу в магазине.
//Правильно:
покупатель. Книги. Добавить(магазин. Продать(код, покупатель));
//Ошибка:
покупатель. Книги. Добавить(магазин. Книги(код));Ошибка в данном примере снова означает, что книгу вы украли.
Два совпадения. Случайные ли?
В случае явного управления памятью возникнет системная ошибка: библиотека или магазин полезут без спросу в ваше отсутствие в квартиру за объектом и заберут его назад. Придя домой, вы не обнаружите на полке нужной книги.
В
Журнал хозяйственных операций
Отвлечёмся ненадолго от общих концепций и рассмотрим более конкретные проблемы разработки учётных приложений. Вопросы, подобные «почему нельзя хранить остатки в форме текущих величин», «зачем нужна история операций», «зачем там нужна транзакция в режиме «сериализация» (что такое режим serialized можно прочитать в статье [20]), не раз всплывали в дискуссиях, поэтому я кратко расскажу об этом в рамках отдельной главы, чтобы в следующий раз просто ссылаться.
Довольно сжатое изложение статьи «Как проектировать бухгалтерию»[19] в терминах абстракций может быть не очень понятным начинающим. Напротив, статья «Введение в складской учёт»[18] рискует показаться излишне упрощённой и «заточенной» на складскую бухгалтерию с отраслевой спецификой. И в обоих случаях не хватает конкретики, разъясняющей вышеназванные вопросы.
Начнём с первого антишаблона «Таблица остатков», на которые я вдоволь насмотрелся во времена буйного расцвета торгово-складского софтостроения в 1990-х годах. Начинающий проектировщик рассуждает так: мне нужно текущее количество товара в наличии, а все движения, в результате которых эта цифра и появилась, можно оставить в стороне, а то и прямо в первичных документах.
Представьте, что в документ недельной давности вкралась ошибка. Её исправили, причём пересчитанные текущие остатки по-прежнему неотрицательны. Значит ли, что они неотрицательны и на каждый день прошедшей недели? Разумеется, нет. Приходит клиент и говорит: «Мне выписали 10 штук, а на складе только 8, я на вас, жуликов, в суд подам». Парадокс? Никакого парадокса. За товаром он пришёл сегодня, но продали ему товар вчера. А на состояние «вчера» после корректировки остаток был бы отрицательным. Вот ему и не хватило.
Если не верите, что такое возможно, вот схема движения.
Теперь откорректируем приход 2012-04-01 с 12 утюгов на 10. Получаем, что на сегодня их 0. Вроде бы все в порядке. Всё, да не совсем: сегодняшняя закупка ещё не поступила на реализацию. Поэтому вчерашний «минус 2» пока действителен.
Почесав свой мыслительный орган, проектировщик приходит к неутешительному выводу: даже для фактических операций нужно считать остаток по истории (журналу). Не говоря уже о резервировании товара, где ситуация меняется гораздо быстрее: то тут отменили, то там подтвердили.
Но считать по журналу:
• может быть долго;
• необходимо защитить считанные значения, чтобы при последующей записи не возникло «минусов».
Последний пункт требует пояснений. По сути, это и есть та самая сериализованная транзакция. Она гарантирует, что считанные значения не будут изменены другой транзакцией. То есть продажа не будет давать отрицательный остаток, если между операцией расчёта остатка и расхода вклинится другой. Это просто, смотрите.
В итоге молодцы-продавцы, нечаянно воспользовавшись ошибкой программиста, сплавили клиентам 12 утюгов, хотя в наличии было 10.
Если же ваши операции расчёта, проверки и расхода работают в одной транзакции на уровне изоляции «сериализация», то такая ситуация исключена. «Продавец 2» из примера будет ждать, пока «Продавец 1» закончит операцию и в свою очередь убедится, что утюгов уже не 10, как было на экране в момент заказа, а только 5.
Почему нельзя просто блокировать всю таблицу-журнал, а надо использовать какие-то хитроумные транзакции с непонятным уровнем изоляции?
Объяснить это тоже просто. Представьте, что один продаёт утюги, а второй – гладильные доски. Если заблокировать
Здесь я должен отметить, что использование транзакции в режиме serialized вполне может привести на физическом уровне к блокировке всей таблицы. Но это уже проблема следующего порядка, которую вы будете решать самостоятельно или в содружестве со специалистом по СУБД.
Рассуждая, мы плавно подошли к вопросу «Зачем хранить историю остатков?». Действительно, ведь есть журнал, всё можно посчитать по состоянию на любой период.
Вернёмся к примеру корректировки документа недельной давности. Таблицы остатков у нас уже нет, а мы должны рассчитать и проверить на «минус» все дни за последнюю неделю. Допустим, ваша программа правильная, использует соответствующие транзакции и считает остаток от операций одного дня примерно за 500 миллисекунд. Умножим на 7, получим, что с большой вероятностью примерно 3,5 секунды пользователи учётной системы будут ожидать окончания вашей операции.
Это, мягко говоря, нехорошо. К тому же, если ваш расчёт не написан на языке СУБД – SQL, то цифра в 500 миллисекунд явно завышена в разы.
Придётся нам возвращаться к «таблице» остатков. Но совсем не к такой таблице, что была вначале, а к новой, называемой «сальдо» или «итогами». Мы добавим туда ещё один важный разрез – период. И будем поддерживать остаток на заданный период в актуальном состоянии. Тогда ваша программа просто должна попытаться отменить операцию и посмотреть, нет ли в сальдо «минусов», начиная с даты аннулированного документа. Здесь тоже возможна блокировка, но, во-первых, менее вероятная, а во-вторых, более быстрая, так как мы просто производим вычитание количества от уже рассчитанных остатков, начиная с даты вместо полного пересчёта по журналу операций.
Как поддержать актуальность остатков? Скорее всего, триггером (кто сказал «материализованные виды»?). Это будет весьма критичный код в вашей системе, поэтому придётся, не побоюсь этого слова, «вылизывать» его так, чтобы он выстреливал за единицы миллисекунд. Дам совет начинающим: учите сиквел, транзакции, уровни изоляции, и будет ваша система быстрой и надёжной. И не только в примере с учётной системой, но и вообще по жизни. Опытным же разработчикам есть поле для оптимизации и дальнейшего совершенствования решений, включая альтернативные подходы.UML и птолемеевские системы
Для того чтобы было легче подступиться к теме унифицированного языка моделирования UML, уже упоминавшегося в предыдущих главах, необходимо сделать небольшое историческое отступление [21].
Что же заставило учёных отказаться от системы Птолемея? Ответ с позиций современной теории познания мы видим в следующем. Система Птолемея описывала движение планет, видимое с Земли, то есть описывала явление. Если же мы оказались, например, на Меркурии или Марсе, то земную птолемеевскую систему нам пришлось бы упразднить и заменить новой.
Система Коперника сумела схватить сущность взаимного движения планет Солнечной системы. Такое описание, говоря современным языком, уже не зависело от того, какую планету в качестве системы отсчёта захочет выбрать себе наблюдатель.
С точки зрения теории познания объективной истины теологи совершали грубейшую ошибку: они сущность подменяли явлением. Наблюдаемое с Земли движение планет по небосводу они считали их действительным движением относительно Земли. .
В UML должно настораживать уже самое первое слово – «унифицированный». Не универсальный, то есть пригодный в большинстве случаев, а именно унифицированный, объединённый. Силами небольшого количества экспертов, если точнее, сначала двух, позже к ним присоединился третий, был создан сборник, содержащий наиболее удачные нотации из их собственной практики. Сборник стали продвигать в массы, а за неимением лучшего, и в стандарты.
Стандарт получился несколько странным. В его названии присутствует слово «моделирование». Модель от картинки в графическом редакторе отличается наличием базового формализма, позволяющего проверить созданную конструкцию на непротиворечивость, а если повезёт, то и на полноту. Очень простой, но верный признак проблемы – отсутствие в инструменте моделирования команды «Проверить». И если условной кнопки «Check model» в панели не предусмотрено, то ваш CASE [112] является просто продвинутым редактором специализированной векторной графики с шаблонами генерации кода.
Для сравнения, в IDEF0 [113] проверка целостности имеется, что не даст аналитику нарисовать в своей модели возникающие из пустоты и пропадающие в никуда объекты. Концептуальная модель данных в любой нотации, ER [114] или IDEF1х, также обратит внимание проектировщика на «висячие» сущности, циклические связи и прочие моменты, скорее всего, являющиеся ошибкой. На уровне логической модели данных (реляционная) к вышеназванным проверкам присоединятся новые, например, связанные с ограничениями типов (доменов), наличия ключей и другие. UML работает на логическом уровне.